Imported Upstream version 1.6.8p5
[debian/sudo] / testsudoers.c
1 /*
2  * Copyright (c) 1996, 1998-2004 Todd C. Miller <Todd.Miller@courtesan.com>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
16  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17  *
18  * Sponsored in part by the Defense Advanced Research Projects
19  * Agency (DARPA) and Air Force Research Laboratory, Air Force
20  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21  */
22
23 #define _SUDO_MAIN
24
25 #include "config.h"
26
27 #include <sys/param.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/socket.h>
31 #include <stdio.h>
32 #ifdef STDC_HEADERS
33 # include <stdlib.h>
34 # include <stddef.h>
35 #else
36 # ifdef HAVE_STDLIB_H
37 #  include <stdlib.h>
38 # endif
39 #endif /* STDC_HEADERS */
40 #ifdef HAVE_STRING_H
41 # include <string.h>
42 #else
43 # ifdef HAVE_STRINGS_H
44 #  include <strings.h>
45 # endif
46 #endif /* HAVE_STRING_H */
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif /* HAVE_UNISTD_H */
50 #ifdef HAVE_FNMATCH
51 # include <fnmatch.h>
52 #endif /* HAVE_FNMATCH */
53 #ifdef HAVE_NETGROUP_H
54 # include <netgroup.h>
55 #endif /* HAVE_NETGROUP_H */
56 #ifdef HAVE_ERR_H
57 # include <err.h>
58 #else
59 # include "emul/err.h"
60 #endif /* HAVE_ERR_H */
61 #include <ctype.h>
62 #include <pwd.h>
63 #include <grp.h>
64 #include <netinet/in.h>
65 #include <arpa/inet.h>
66 #include <netdb.h>
67 #include <dirent.h>
68
69 #include "sudo.h"
70 #include "parse.h"
71 #include "interfaces.h"
72
73 #ifndef HAVE_FNMATCH
74 # include "emul/fnmatch.h"
75 #endif /* HAVE_FNMATCH */
76
77 #ifndef lint
78 static const char rcsid[] = "$Sudo: testsudoers.c,v 1.88 2004/08/02 18:44:58 millert Exp $";
79 #endif /* lint */
80
81
82 /*
83  * Prototypes
84  */
85 void init_parser        __P((void));
86 void dumpaliases        __P((void));
87 void set_perms_dummy    __P((int));
88
89 /*
90  * Globals
91  */
92 int  Argc, NewArgc;
93 char **Argv, **NewArgv;
94 int parse_error = FALSE;
95 int num_interfaces;
96 struct interface *interfaces;
97 struct sudo_user sudo_user;
98 extern int clearaliases;
99 extern int pedantic;
100 void (*set_perms) __P((int)) = set_perms_dummy;
101
102 /*
103  * Returns TRUE if "s" has shell meta characters in it,
104  * else returns FALSE.
105  */
106 int
107 has_meta(s)
108     char *s;
109 {
110     char *t;
111
112     for (t = s; *t; t++) {
113         if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']')
114             return(TRUE);
115     }
116     return(FALSE);
117 }
118
119 /*
120  * Returns TRUE if user_cmnd matches, in the sudo sense,
121  * the pathname in path; otherwise, return FALSE
122  */
123 int
124 command_matches(path, sudoers_args)
125     char *path;
126     char *sudoers_args;
127 {
128     int clen, plen;
129     char *args;
130
131     if (user_cmnd == NULL)
132         return(FALSE);
133
134     if ((args = strchr(path, ' ')))
135         *args++ = '\0';
136
137     if (has_meta(path)) {
138         if (fnmatch(path, user_cmnd, FNM_PATHNAME))
139             return(FALSE);
140         if (!sudoers_args)
141             return(TRUE);
142         else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args))
143             return(TRUE);
144         else if (sudoers_args)
145             return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
146         else
147             return(FALSE);
148     } else {
149         plen = strlen(path);
150         if (path[plen - 1] != '/') {
151             if (strcmp(user_cmnd, path))
152                 return(FALSE);
153             if (!sudoers_args)
154                 return(TRUE);
155             else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args))
156                 return(TRUE);
157             else if (sudoers_args)
158                 return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
159             else
160                 return(FALSE);
161         }
162
163         clen = strlen(user_cmnd);
164         if (clen < plen + 1)
165             /* path cannot be the parent dir of user_cmnd */
166             return(FALSE);
167
168         if (strchr(user_cmnd + plen + 1, '/') != NULL)
169             /* path could only be an anscestor of user_cmnd -- */
170             /* ignoring, of course, things like // & /./  */
171             return(FALSE);
172
173         /* see whether path is the prefix of user_cmnd */
174         return((strncmp(user_cmnd, path, plen) == 0));
175     }
176 }
177
178 int
179 addr_matches(n)
180     char *n;
181 {
182     int i;
183     char *m;
184     struct in_addr addr, mask;
185
186     /* If there's an explicit netmask, use it. */
187     if ((m = strchr(n, '/'))) {
188         *m++ = '\0';
189         addr.s_addr = inet_addr(n);
190         if (strchr(m, '.'))
191             mask.s_addr = inet_addr(m);
192         else {
193             i = 32 - atoi(m);
194             mask.s_addr = 0xffffffff;
195             mask.s_addr >>= i;
196             mask.s_addr <<= i;
197             mask.s_addr = htonl(mask.s_addr);
198         }
199         *(m - 1) = '/';
200
201         for (i = 0; i < num_interfaces; i++)
202             if ((interfaces[i].addr.s_addr & mask.s_addr) == addr.s_addr)
203                 return(TRUE);
204     } else {
205         addr.s_addr = inet_addr(n);
206
207         for (i = 0; i < num_interfaces; i++)
208             if (interfaces[i].addr.s_addr == addr.s_addr ||
209                 (interfaces[i].addr.s_addr & interfaces[i].netmask.s_addr)
210                 == addr.s_addr)
211                 return(TRUE);
212     }
213
214     return(FALSE);
215 }
216
217 int
218 hostname_matches(shost, lhost, pattern)
219     char *shost;
220     char *lhost;
221     char *pattern;
222 {
223     if (has_meta(pattern)) {
224         if (strchr(pattern, '.'))
225             return(fnmatch(pattern, lhost, FNM_CASEFOLD));
226         else
227             return(fnmatch(pattern, shost, FNM_CASEFOLD));
228     } else {
229         if (strchr(pattern, '.'))
230             return(strcasecmp(lhost, pattern));
231         else
232             return(strcasecmp(shost, pattern));
233     }
234 }
235
236 int
237 userpw_matches(sudoers_user, user, pw)
238     char *sudoers_user;
239     char *user;
240     struct passwd *pw;
241 {
242     if (pw != NULL && *sudoers_user == '#') {
243         uid_t uid = atoi(sudoers_user + 1);
244         if (uid == pw->pw_uid)
245             return(1);
246     }
247     return(strcmp(sudoers_user, user) == 0);
248 }
249
250 int
251 usergr_matches(group, user, pw)
252     char *group;
253     char *user;
254     struct passwd *pw;
255 {
256     struct group *grp;
257     char **cur;
258
259     /* Make sure we have a valid usergroup, sudo style. */
260     if (*group++ != '%')
261         return(FALSE);
262
263     if ((grp = getgrnam(group)) == NULL)
264         return(FALSE);
265
266     /*
267      * Check against user's real gid as well as group's user list
268      */
269     if (getgid() == grp->gr_gid)
270         return(TRUE);
271
272     for (cur=grp->gr_mem; *cur; cur++) {
273         if (strcmp(*cur, user) == 0)
274             return(TRUE);
275     }
276
277     return(FALSE);
278 }
279
280 int
281 netgr_matches(netgr, host, shost, user)
282     char *netgr;
283     char *host;
284     char *shost;
285     char *user;
286 {
287 #ifdef HAVE_GETDOMAINNAME
288     static char *domain = (char *) -1;
289 #else
290     static char *domain = NULL;
291 #endif /* HAVE_GETDOMAINNAME */
292
293     /* Make sure we have a valid netgroup, sudo style. */
294     if (*netgr++ != '+')
295         return(FALSE);
296
297 #ifdef HAVE_GETDOMAINNAME
298     /* Get the domain name (if any). */
299     if (domain == (char *) -1) {
300         domain = (char *) emalloc(MAXHOSTNAMELEN);
301
302         if (getdomainname(domain, MAXHOSTNAMELEN) != 0 || *domain == '\0') {
303             free(domain);
304             domain = NULL;
305         }
306     }
307 #endif /* HAVE_GETDOMAINNAME */
308
309 #ifdef HAVE_INNETGR
310     if (innetgr(netgr, host, user, domain))
311         return(TRUE);
312     else if (host != shost && innetgr(netgr, shost, user, domain))
313         return(TRUE);
314 #endif /* HAVE_INNETGR */
315
316     return(FALSE);
317 }
318
319 void
320 set_perms_dummy(i)
321     int i;
322 {
323     return;
324 }
325
326 void
327 set_fqdn()
328 {
329     return;
330 }
331
332 int
333 set_runaspw(user)
334     char *user;
335 {
336     return(TRUE);
337 }
338
339 void
340 init_envtables()
341 {
342     return;
343 }
344
345 int
346 main(argc, argv)
347     int argc;
348     char **argv;
349 {
350     struct passwd pw;
351     char *p;
352 #ifdef  YYDEBUG
353     extern int yydebug;
354     yydebug = 1;
355 #endif
356
357     Argv = argv;
358     Argc = argc;
359
360     if (Argc >= 6 && strcmp(Argv[1], "-u") == 0) {
361         user_runas = &Argv[2];
362         pw.pw_name = Argv[3];
363         user_host = Argv[4];
364         user_cmnd = Argv[5];
365
366         NewArgv = &Argv[5];
367         NewArgc = Argc - 5;
368     } else if (Argc >= 4) {
369         pw.pw_name = Argv[1];
370         user_host = Argv[2];
371         user_cmnd = Argv[3];
372
373         NewArgv = &Argv[3];
374         NewArgc = Argc - 3;
375     } else {
376         (void) fprintf(stderr,
377             "usage: sudo [-u user] <user> <host> <command> [args]\n");
378         exit(1);
379     }
380
381     sudo_user.pw = &pw;         /* user_name needs to be defined */
382
383     if ((p = strchr(user_host, '.'))) {
384         *p = '\0';
385         user_shost = estrdup(user_host);
386         *p = '.';
387     } else {
388         user_shost = user_host;
389     }
390
391     /* Fill in user_args from NewArgv. */
392     if (NewArgc > 1) {
393         char *to, **from;
394         size_t size, n;
395
396         size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
397                 strlen(NewArgv[NewArgc-1]) + 1;
398         user_args = (char *) emalloc(size);
399         for (to = user_args, from = NewArgv + 1; *from; from++) {
400             n = strlcpy(to, *from, size - (to - user_args));
401             if (n >= size - (to - user_args))
402                     errx(1, "internal error, init_vars() overflow");
403             to += n;
404             *to++ = ' ';
405         }
406         *--to = '\0';
407     }
408
409     /* Initialize default values. */
410     init_defaults();
411
412     /* Warn about aliases that are used before being defined. */
413     pedantic = TRUE;
414
415     /* Need to keep aliases around for dumpaliases(). */
416     clearaliases = FALSE;
417
418     /* Load ip addr/mask for each interface. */
419     load_interfaces();
420
421     /* Allocate space for data structures in the parser. */
422     init_parser();
423
424     if (yyparse() || parse_error) {
425         (void) printf("doesn't parse.\n");
426     } else {
427         (void) printf("parses OK.\n\n");
428         if (top == 0)
429             (void) printf("User %s not found\n", pw.pw_name);
430         else while (top) {
431             (void) printf("[%d]\n", top-1);
432             (void) printf("user_match : %d\n", user_matches);
433             (void) printf("host_match : %d\n", host_matches);
434             (void) printf("cmnd_match : %d\n", cmnd_matches);
435             (void) printf("no_passwd  : %d\n", no_passwd);
436             (void) printf("runas_match: %d\n", runas_matches);
437             (void) printf("runas      : %s\n", *user_runas);
438             top--;
439         }
440     }
441
442     /* Dump aliases. */
443     (void) printf("Matching Aliases --\n");
444     dumpaliases();
445
446     exit(0);
447 }