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
has_meta(s)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
command_matches(path,sudoers_args)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
addr_matches(n)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
hostname_matches(shost,lhost,pattern)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
userpw_matches(sudoers_user,user,pw)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
usergr_matches(group,user,pw)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
netgr_matches(netgr,host,shost,user)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
set_perms_dummy(i)320 set_perms_dummy(i)
321 int i;
322 {
323 return;
324 }
325
326 void
set_fqdn()327 set_fqdn()
328 {
329 return;
330 }
331
332 int
set_runaspw(user)333 set_runaspw(user)
334 char *user;
335 {
336 return(TRUE);
337 }
338
339 void
init_envtables()340 init_envtables()
341 {
342 return;
343 }
344
345 int
main(argc,argv)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 }
448