fix typo in changelog
[debian/sudo] / testsudoers.c
1 /*
2  * Copyright (c) 1996, 1998-2005 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 __unused static const char rcsid[] = "$Sudo: testsudoers.c,v 1.88.2.7 2008/02/09 14:44:49 millert Exp $";
79 #endif /* lint */
80
81
82 /*
83  * Prototypes
84  */
85 void init_parser        __P((void));
86 void dumpaliases        __P((void));
87
88 /*
89  * Globals
90  */
91 int  Argc, NewArgc;
92 char **Argv, **NewArgv;
93 int parse_error = FALSE;
94 int num_interfaces;
95 struct interface *interfaces;
96 struct sudo_user sudo_user;
97 extern int clearaliases;
98 extern int pedantic;
99
100 /*
101  * Returns TRUE if "s" has shell meta characters in it,
102  * else returns FALSE.
103  */
104 int
105 has_meta(s)
106     char *s;
107 {
108     char *t;
109
110     for (t = s; *t; t++) {
111         if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']')
112             return(TRUE);
113     }
114     return(FALSE);
115 }
116
117 /*
118  * Returns TRUE if user_cmnd matches, in the sudo sense,
119  * the pathname in path; otherwise, return FALSE
120  */
121 int
122 command_matches(path, sudoers_args)
123     char *path;
124     char *sudoers_args;
125 {
126     int clen, plen;
127     char *args;
128
129     if (user_cmnd == NULL)
130         return(FALSE);
131
132     if ((args = strchr(path, ' ')))
133         *args++ = '\0';
134
135     if (has_meta(path)) {
136         if (fnmatch(path, user_cmnd, FNM_PATHNAME))
137             return(FALSE);
138         if (!sudoers_args)
139             return(TRUE);
140         else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args))
141             return(TRUE);
142         else if (sudoers_args)
143             return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
144         else
145             return(FALSE);
146     } else {
147         plen = strlen(path);
148         if (path[plen - 1] != '/') {
149             if (strcmp(user_cmnd, path))
150                 return(FALSE);
151             if (!sudoers_args)
152                 return(TRUE);
153             else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args))
154                 return(TRUE);
155             else if (sudoers_args)
156                 return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0));
157             else
158                 return(FALSE);
159         }
160
161         clen = strlen(user_cmnd);
162         if (clen < plen + 1)
163             /* path cannot be the parent dir of user_cmnd */
164             return(FALSE);
165
166         if (strchr(user_cmnd + plen + 1, '/') != NULL)
167             /* path could only be an anscestor of user_cmnd -- */
168             /* ignoring, of course, things like // & /./  */
169             return(FALSE);
170
171         /* see whether path is the prefix of user_cmnd */
172         return((strncmp(user_cmnd, path, plen) == 0));
173     }
174 }
175
176 static int
177 addr_matches_if(n)
178     char *n;
179 {
180     int i;
181     struct in_addr addr;
182     struct interface *ifp;
183 #ifdef HAVE_IN6_ADDR
184     struct in6_addr addr6;
185     int j;
186 #endif
187     int family;
188
189 #ifdef HAVE_IN6_ADDR
190     if (inet_pton(AF_INET6, n, &addr6) > 0) {
191         family = AF_INET6;
192     } else
193 #endif
194     {
195         family = AF_INET;
196         addr.s_addr = inet_addr(n);
197     }
198
199     for (i = 0; i < num_interfaces; i++) {
200         ifp = &interfaces[i];
201         if (ifp->family != family)
202             continue;
203         switch(family) {
204             case AF_INET:
205                 if (ifp->addr.ip4.s_addr == addr.s_addr ||
206                     (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
207                     == addr.s_addr)
208                     return(TRUE);
209                 break;
210 #ifdef HAVE_IN6_ADDR
211             case AF_INET6:
212                 if (memcmp(ifp->addr.ip6.s6_addr, addr6.s6_addr,
213                     sizeof(addr6.s6_addr)) == 0)
214                     return(TRUE);
215                 for (j = 0; j < sizeof(addr6.s6_addr); j++) {
216                     if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr6.s6_addr[j])
217                         break;
218                 }
219                 if (j == sizeof(addr6.s6_addr))
220                     return(TRUE);
221 #endif /* HAVE_IN6_ADDR */
222         }
223     }
224
225     return(FALSE);
226 }
227
228 static int
229 addr_matches_if_netmask(n, m)
230     char *n;
231     char *m;
232 {
233     int i;
234     struct in_addr addr, mask;
235     struct interface *ifp;
236 #ifdef HAVE_IN6_ADDR
237     struct in6_addr addr6, mask6;
238     int j;
239 #endif
240     int family;
241
242 #ifdef HAVE_IN6_ADDR
243     if (inet_pton(AF_INET6, n, &addr6) > 0)
244         family = AF_INET6;
245     else
246 #endif
247     {
248         family = AF_INET;
249         addr.s_addr = inet_addr(n);
250     }
251
252     if (family == AF_INET) {
253         if (strchr(m, '.'))
254             mask.s_addr = inet_addr(m);
255         else {
256             i = 32 - atoi(m);
257             mask.s_addr = 0xffffffff;
258             mask.s_addr >>= i;
259             mask.s_addr <<= i;
260             mask.s_addr = htonl(mask.s_addr);
261         }
262     }
263 #ifdef HAVE_IN6_ADDR
264     else {
265         if (inet_pton(AF_INET6, m, &mask6) <= 0) {
266             j = atoi(m);
267             for (i = 0; i < 16; i++) {
268                 if (j < i * 8)
269                     mask6.s6_addr[i] = 0;
270                 else if (i * 8 + 8 <= j)
271                     mask6.s6_addr[i] = 0xff;
272                 else
273                     mask6.s6_addr[i] = 0xff00 >> (j - i * 8);
274             }
275         }
276     }
277 #endif /* HAVE_IN6_ADDR */
278
279     for (i = 0; i < num_interfaces; i++) {
280         ifp = &interfaces[i];
281         if (ifp->family != family)
282             continue;
283         switch(family) {
284             case AF_INET:
285                 if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr)
286                     return(TRUE);
287 #ifdef HAVE_IN6_ADDR
288             case AF_INET6:
289                 for (j = 0; j < sizeof(addr6.s6_addr); j++) {
290                     if ((ifp->addr.ip6.s6_addr[j] & mask6.s6_addr[j]) != addr6.s6_addr[j])
291                         break;
292                 }
293                 if (j == sizeof(addr6.s6_addr))
294                     return(TRUE);
295 #endif /* HAVE_IN6_ADDR */
296         }
297     }
298
299     return(FALSE);
300 }
301
302 /*
303  * Returns TRUE if "n" is one of our ip addresses or if
304  * "n" is a network that we are on, else returns FALSE.
305  */
306 int
307 addr_matches(n)
308     char *n;
309 {
310     char *m;
311     int retval;
312
313     /* If there's an explicit netmask, use it. */
314     if ((m = strchr(n, '/'))) {
315         *m++ = '\0';
316         retval = addr_matches_if_netmask(n, m);
317         *(m - 1) = '/';
318     } else
319         retval = addr_matches_if(n);
320
321     return(retval);
322 }
323
324 int
325 hostname_matches(shost, lhost, pattern)
326     char *shost;
327     char *lhost;
328     char *pattern;
329 {
330     if (has_meta(pattern)) {
331         if (strchr(pattern, '.'))
332             return(fnmatch(pattern, lhost, FNM_CASEFOLD));
333         else
334             return(fnmatch(pattern, shost, FNM_CASEFOLD));
335     } else {
336         if (strchr(pattern, '.'))
337             return(strcasecmp(lhost, pattern));
338         else
339             return(strcasecmp(shost, pattern));
340     }
341 }
342
343 int
344 userpw_matches(sudoers_user, user, pw)
345     char *sudoers_user;
346     char *user;
347     struct passwd *pw;
348 {
349     if (pw != NULL && *sudoers_user == '#') {
350         uid_t uid = atoi(sudoers_user + 1);
351         if (uid == pw->pw_uid)
352             return(1);
353     }
354     return(strcmp(sudoers_user, user) == 0);
355 }
356
357 int
358 usergr_matches(group, user, pw)
359     char *group;
360     char *user;
361     struct passwd *pw;
362 {
363     struct group *grp;
364     char **cur;
365
366     /* Make sure we have a valid usergroup, sudo style. */
367     if (*group++ != '%')
368         return(FALSE);
369
370     if ((grp = getgrnam(group)) == NULL)
371         return(FALSE);
372
373     /*
374      * Check against user's real gid as well as group's user list
375      */
376     if (getgid() == grp->gr_gid)
377         return(TRUE);
378
379     for (cur=grp->gr_mem; *cur; cur++) {
380         if (strcmp(*cur, user) == 0)
381             return(TRUE);
382     }
383
384     return(FALSE);
385 }
386
387 int
388 netgr_matches(netgr, host, shost, user)
389     char *netgr;
390     char *host;
391     char *shost;
392     char *user;
393 {
394 #ifdef HAVE_GETDOMAINNAME
395     static char *domain = (char *) -1;
396 #else
397     static char *domain = NULL;
398 #endif /* HAVE_GETDOMAINNAME */
399
400     /* Make sure we have a valid netgroup, sudo style. */
401     if (*netgr++ != '+')
402         return(FALSE);
403
404 #ifdef HAVE_GETDOMAINNAME
405     /* Get the domain name (if any). */
406     if (domain == (char *) -1) {
407         domain = (char *) emalloc(MAXHOSTNAMELEN);
408
409         if (getdomainname(domain, MAXHOSTNAMELEN) != 0 || *domain == '\0') {
410             efree(domain);
411             domain = NULL;
412         }
413     }
414 #endif /* HAVE_GETDOMAINNAME */
415
416 #ifdef HAVE_INNETGR
417     if (innetgr(netgr, host, user, domain))
418         return(TRUE);
419     else if (host != shost && innetgr(netgr, shost, user, domain))
420         return(TRUE);
421 #endif /* HAVE_INNETGR */
422
423     return(FALSE);
424 }
425
426 void
427 set_perms(i)
428     int i;
429 {
430     return;
431 }
432
433 void
434 set_fqdn()
435 {
436     return;
437 }
438
439 int
440 set_runaspw(user)
441     char *user;
442 {
443     return(TRUE);
444 }
445
446 void
447 init_envtables()
448 {
449     return;
450 }
451
452 int
453 main(argc, argv)
454     int argc;
455     char **argv;
456 {
457     struct passwd pw;
458     char *p;
459 #ifdef  YYDEBUG
460     extern int yydebug;
461     yydebug = 1;
462 #endif
463
464     Argv = argv;
465     Argc = argc;
466
467     if (Argc >= 6 && strcmp(Argv[1], "-u") == 0) {
468         user_runas = &Argv[2];
469         pw.pw_name = Argv[3];
470         user_host = Argv[4];
471         user_cmnd = Argv[5];
472
473         NewArgv = &Argv[5];
474         NewArgc = Argc - 5;
475     } else if (Argc >= 4) {
476         pw.pw_name = Argv[1];
477         user_host = Argv[2];
478         user_cmnd = Argv[3];
479
480         NewArgv = &Argv[3];
481         NewArgc = Argc - 3;
482     } else {
483         (void) fprintf(stderr,
484             "usage: sudo [-u user] <user> <host> <command> [args]\n");
485         exit(1);
486     }
487
488     sudo_user.pw = &pw;         /* user_name needs to be defined */
489
490     if ((p = strchr(user_host, '.'))) {
491         *p = '\0';
492         user_shost = estrdup(user_host);
493         *p = '.';
494     } else {
495         user_shost = user_host;
496     }
497
498     /* Fill in user_args from NewArgv. */
499     if (NewArgc > 1) {
500         char *to, **from;
501         size_t size, n;
502
503         size = (size_t) (NewArgv[NewArgc-1] - NewArgv[1]) +
504                 strlen(NewArgv[NewArgc-1]) + 1;
505         user_args = (char *) emalloc(size);
506         for (to = user_args, from = NewArgv + 1; *from; from++) {
507             n = strlcpy(to, *from, size - (to - user_args));
508             if (n >= size - (to - user_args))
509                     errx(1, "internal error, init_vars() overflow");
510             to += n;
511             *to++ = ' ';
512         }
513         *--to = '\0';
514     }
515
516     /* Initialize default values. */
517     init_defaults();
518
519     /* Warn about aliases that are used before being defined. */
520     pedantic = TRUE;
521
522     /* Need to keep aliases around for dumpaliases(). */
523     clearaliases = FALSE;
524
525     /* Load ip addr/mask for each interface. */
526     load_interfaces();
527
528     /* Allocate space for data structures in the parser. */
529     init_parser();
530
531     if (yyparse() || parse_error) {
532         (void) printf("doesn't parse.\n");
533     } else {
534         (void) printf("parses OK.\n\n");
535         if (top == 0)
536             (void) printf("User %s not found\n", pw.pw_name);
537         else while (top) {
538             (void) printf("[%d]\n", top-1);
539             (void) printf("user_match : %d\n", user_matches);
540             (void) printf("host_match : %d\n", host_matches);
541             (void) printf("cmnd_match : %d\n", cmnd_matches);
542             (void) printf("no_passwd  : %d\n", no_passwd);
543             (void) printf("runas_match: %d\n", runas_matches);
544             (void) printf("runas      : %s\n", *user_runas);
545             if (match[top-1].role)
546                 (void) printf("role       : %s\n", match[top-1].role);
547             if (match[top-1].type)
548                 (void) printf("type       : %s\n", match[top-1].type);
549             top--;
550         }
551     }
552
553     /* Dump aliases. */
554     (void) printf("Matching Aliases --\n");
555     dumpaliases();
556
557     exit(0);
558 }