Kaarsemaker.net


suid_script_wrapper.c
5.21 KB

173 lines

Download

resolve.py
wag.c

Preview

 1 /* suid_script_wrapper.c
 2  *
 3  * Wrapper around scripts for chmod +s purposes. Scripts can only be setuid if
 4  * their interpreter is, which most interpreters are not. You can work around
 5  * this limitation with this program. You must hardcode the full path to both
 6  * interpreter and script to prevent security breaches.
 7  *
 8  * An example script is included below.
 9 
10 % cat /tmp/suidtest.py
11 import os, sys
12 print "UID: %d GID: %d" % (os.getuid(), os.getgid())
13 print "EUID: %d EGID: %d" % (os.geteuid(), os.getegid())
14 print "ARGV: %s" % str(sys.argv)
15 print "ENVP: %s" % str(os.environ)
  1 /* suid_script_wrapper.c
  2  *
  3  * Wrapper around scripts for chmod +s purposes. Scripts can only be setuid if
  4  * their interpreter is, which most interpreters are not. You can work around
  5  * this limitation with this program. You must hardcode the full path to both
  6  * interpreter and script to prevent security breaches.
  7  *
  8  * An example script is included below.
  9 
 10 % cat /tmp/suidtest.py
 11 import os, sys
 12 print "UID: %d GID: %d" % (os.getuid(), os.getgid())
 13 print "EUID: %d EGID: %d" % (os.geteuid(), os.getegid())
 14 print "ARGV: %s" % str(sys.argv)
 15 print "ENVP: %s" % str(os.environ)
 16 
 17  * (c)2008 Dennis Kaarsemaker - Dedicated to the public domain
 18  */
 19 
 20 /* Full path to interpreter */
 21 #define INTERPRETER_PATH "/usr/bin/python"
 22 /* Full path to script */
 23 #define SCRIPT_PATH      "/tmp/suidtest.py"
 24 /* Pass argv on or not */
 25 #define TAKE_ARGV        1
 26 /* Paranoid mode: Every involved file should be owned by root */
 27 #define PARANOID         1
 28 
 29 #define _GNU_SOURCE
 30 
 31 #include <stdio.h>
 32 #include <stdlib.h>
 33 #include <string.h>
 34 #include <sys/stat.h>
 35 #include <sys/types.h>
 36 #include <unistd.h>
 37 
 38 extern char **environ;
 39 
 40 /* Clear the environment. Only preserve USER/USERNAME/LOGNAME/HOME/LANG/LC_* */
 41 void clear_environ() {
 42     char **env = environ;
 43     size_t len;
 44     char *key;
 45     while(*env) env++;
 46     do {
 47         env--;
 48         len = strchr(*env, '=') - *env;
 49         key = strndup(*env, len);
 50         /* You can extend this list if you want, but be careful */
 51         if(strcmp(key, "USER") &&
 52            strcmp(key, "USERNAME") &&
 53            strcmp(key, "LOGNAME") &&
 54            strcmp(key, "HOME") &&
 55            strcmp(key, "LANG") &&
 56            strncmp(key, "LC_", 3)
 57           )
 58             unsetenv(key);
 59         free(key);
 60     } while (env != environ);
 61     /* Inject a few safe things */
 62     setenv("PATH","/bin:/sbin:/usr/bin:/usr/sbin:/usr/games:/usr/X11/bin", 1);
 63     setenv("IFS"," \t\n", 1);
 64 }
 65 
 66 int main(int argc, char **argv) {
 67     int ret = 0;
 68     struct stat buf1, buf2;
 69     char *interpreter_path = INTERPRETER_PATH;
 70     char *script_path = SCRIPT_PATH;
 71 
 72     clear_environ();
 73 
 74     /* Config errors */
 75     if(strchr(interpreter_path, '/') != interpreter_path) {
 76         fprintf(stderr, "Path to interpreter not absolute\n");
 77         ret |= 1;
 78     }
 79     if(strchr(script_path, '/') != script_path) {
 80         fprintf(stderr, "Path to script not absolute\n");
 81         ret |= 1;
 82     }
 83 
 84     /* Access errors */
 85     if(stat(argv[0], &buf1) == -1) {
 86         perror("stat on argv[0] failed");
 87         ret |= 2;
 88     }
 89     if(!(buf1.st_mode & (S_ISGID | S_ISUID))) {
 90         fprintf(stderr, "Wrapper isn't suid/sgid\n");
 91         ret |= 2;
 92     }
 93     if(access(interpreter_path, X_OK)) {
 94         perror("Cannot execute interpreter " INTERPRETER_PATH);
 95         ret |= 2;
 96     }
 97     if(access(script_path, R_OK)) {
 98         perror("Cannot read script " SCRIPT_PATH);
 99         ret |= 2;
100     }
101 
102     /* Permission/owner errors */
103     if(!stat(argv[0], &buf1) && !stat(script_path, &buf2)) {
104 #if PARANOID
105         if((buf1.st_uid != 0) || (buf1.st_gid != 0)) {
106             fprintf(stderr, "%s is not owned by root\n", argv[0]);
107             ret |= 4;
108         }
109         if((buf2.st_uid != 0) || (buf2.st_gid != 0)) {
110             fprintf(stderr, "%s is not owned by root\n", script_path);
111             ret |= 4;
112         }
113 #else
114         if((buf1.st_mode & S_ISGID) && (buf1.st_gid != buf2.st_gid)) {
115             fprintf(stderr, "Mismatch betweeen the gid of %s and %s\n", argv[0], script_path);
116             ret |= 4;
117         }
118         if((buf1.st_mode & S_ISUID) && (buf1.st_uid != buf2.st_uid)) {
119             fprintf(stderr, "Mismatch betweeen the uid of %s and %s\n", argv[0], script_path);
120             ret |= 4;
121         }
122 #endif
123         if((buf1.st_mode & S_ISGID) && (buf1.st_mode & S_IWGRP)) {
124             fprintf(stderr, "%s is world writable\n", argv[0]);
125             ret |= 4;
126         }
127         if((buf1.st_mode & S_ISGID) && (buf2.st_mode & S_IWGRP)) {
128             fprintf(stderr, "%s is world writable\n", script_path);
129             ret |= 4;
130         }
131         if((buf1.st_mode & S_ISGID) && (buf1.st_mode & (S_IWGRP | S_IWOTH))) {
132             fprintf(stderr, "%s is world/group writable\n", argv[0]);
133             ret |= 4;
134         }
135         if((buf1.st_mode & S_ISGID) && (buf2.st_mode & (S_IWGRP | S_IWOTH))) {
136             fprintf(stderr, "%s is world/group writable\n", script_path);
137             ret |= 4;
138         }
139     }
140 
141     /* Interpreter check */
142     if(!stat(interpreter_path, &buf1)) {
143         if(buf1.st_uid != 0) {
144             fprintf(stderr, "Invalid interpreter, not owned by root\n");
145             ret |= 8;
146         }
147         if(buf1.st_mode & (S_IWGRP | S_IWOTH)) {
148             fprintf(stderr, "%s is world/group writable\n", interpreter_path);
149             ret |= 8;
150         }
151     }
152 
153     if(ret)
154         return ret | 128;
155 
156     #if TAKE_ARGV
157     char **new_argv = (char**)malloc(argc+2 * sizeof(char*));
158     int i = 1;
159     new_argv[0] = interpreter_path;
160     new_argv[1] = script_path;
161     while(i < argc) {
162         new_argv[i+1] = argv[i];
163         i++;
164     }
165     new_argv[i+1] = NULL;
166     #else
167     char* new_argv[3] = {interpreter_path, script_path, NULL};
168     #endif
169 
170     execve(interpreter_path, new_argv, environ);
171     perror("Could not execute wrapped script " INTERPRETER_PATH " " SCRIPT_PATH);
172     return 16 | 128;
173 }

Show all