1 static const char rcsid[] = "$Id: checks.c,v 1.191 2020/08/03 17:13:04 will Exp $";
2 /* The code should compile with either ANSI C or K&R compilers. */
3 
4 /*
5  *      Copyright (c) 1993 by California Institute of Technology.
6  *      Written by William Deich.  Not derived from licensed software.
7 
8  *    You may distribute under the terms of either the GNU General Public
9  *    License or the Artistic License, as specified in the README file.
10  */
11 
12 #include "super.h"
13 #include "version.h"
14 #include "s_hsearch.h"
15 
16 #define MAX_KEYLEN 1000            /* Large enough? */
17 static char keybuf[MAX_KEYLEN];
18 
19 static char *YES = "Y";
20 static char *NO  = "N";
21 
22 #ifdef HAVE_INNETGR
23 /*
24 #define netgrp_u_compare(pattern, user) innetgr(pattern, NULL, user, NULL)
25 #define netgrp_h_compare(pattern, host) innetgr(pattern, host, NULL, NULL)
26  */
27 
28 static int created_user_table = 0;
netgrp_u_compare(pattern,user)29 static int netgrp_u_compare(pattern, user)
30   char *pattern;
31   char *user;
32 {
33     ENTRY item, *found_item;
34     int passed, klen;
35 
36     if (!created_user_table) {
37         if (!s_hcreate(HS_USER, 1000)) {
38 	    return Error(0, 0,
39 		"$$Couldn't allocate hash table for user ng processing\n");
40 	}
41         created_user_table = 1;
42     }
43     /* Catenate pattern and user for the key such that
44      * "abc" + "de" is not the same as "ab" + "cde".
45      * Use "\n", as that will have been stripped from input before we get here.
46      */
47     if (snprintf(keybuf, MAX_KEYLEN, "%s\n%s", pattern, user) >= MAX_KEYLEN) {
48          return Error(0, 0,
49              "$$pattern and user too large for user ng processing\n");
50     }
51 
52     item.key = keybuf;
53     if (found_item = s_hsearch(HS_USER, item, FIND)) {
54         return found_item->data == YES? 1: 0;
55     }
56     passed = innetgr(pattern, NULL, user, NULL);
57 
58     /* Have to make this key a permanent copy for the hash */
59 
60     klen = strlen(keybuf);
61     item.key = malloc(klen + 1);
62     if (!item.key) {
63         return Error(0, 0,
64            "$$out of memory for user ng processing\n");
65     }
66     strcpy(item.key, keybuf);
67     item.data = passed? YES: NO;
68     if (!s_hsearch(HS_USER, item, ENTER)) {
69         return Error(0, 0,
70            "$$hash set failure for user ng processing\n");
71     }
72     return passed;
73 }
74 
75 static int created_host_table = 0;
netgrp_h_compare(pattern,host)76 static int netgrp_h_compare(pattern, host)
77   char *pattern;
78   char *host;
79 {
80     ENTRY item, *found_item;
81     int passed, klen;
82 
83     if (!created_host_table) {
84         if (!s_hcreate(HS_HOST, 1000)) {
85 	    return Error(0, 0,
86 		"$$Couldn't allocate hash table for host ng processing\n");
87 	}
88         created_host_table = 1;
89     }
90     /* Catenate pattern and user for the key such that
91      * "abc" + "de" is not the same as "ab" + "cde".
92      * Use "\n", as that will have been stripped from input before we get here.
93      */
94     if (snprintf(keybuf, MAX_KEYLEN, "%s\n%s", pattern, host) >= MAX_KEYLEN) {
95          return Error(0, 0,
96              "$$pattern and host too large for host ng processing\n");
97     }
98 
99     item.key = keybuf;
100     if (found_item = s_hsearch(HS_HOST, item, FIND)) {
101         return found_item->data == YES? 1: 0;
102     }
103     passed = innetgr(pattern, host, NULL, NULL);
104 
105     /* Have to make this key a permanent copy for the hash */
106 
107     klen = strlen(keybuf);
108     item.key = malloc(klen + 1);
109     if (!item.key) {
110         return Error(0, 0,
111            "$$out of memory for host ng processing\n");
112     }
113     strcpy(item.key, keybuf);
114     item.data = passed? YES: NO;
115     if (!s_hsearch(HS_HOST, item, ENTER)) {
116         return Error(0, 0,
117            "$$hash set failure for host ng processing\n");
118     }
119     return passed;
120 }
121 
122 #else
123 #define netgrp_u_compare(p, u) 0
124 #define netgrp_h_compare(p, h) 0
125 #endif
126 
127 int get_setting P__ (());
128 
129 
130 #ifdef _HPUX_SOURCE
131 
132 #ifdef HAVE_ISCOMSEC
133     char *bigcrypt();
134 #else
135     /* If we don't have iscomsec() on this HP system, fake it -- otherwise
136      * we'll have to #ifdef the use of iscomsec(), and it's cleaner code
137      * if we don't.
138      */
iscomsec()139     int iscomsec() { return 0; }
140 #endif
141 #endif
142 
143 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
144 /* Check that an environment variable only includes allowed characters */
145 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
146 /* Returns 0 if pat matched; -1 otherwise.  */
147 int
checkenv(name,value,pat)148 checkenv(name, value, pat)
149 char *name;	/* variable name to check (e.g. "TERM") */
150 char *value;	/* contents of variable (e.g. "vt100") */
151 char *pat;	/* pattern that value must match */
152 {
153     int len;
154 
155     if (!value)
156 	return -1;
157 
158     if (debug)
159 	(void) fprintf(stderr,
160 	    "\tcheckenv args: name=\"%s\"; value=\"%s\"; pat=\"%s\"\n",
161 	    name, value, pat);
162 
163     /* Environment variables are always checked with s_re_comp/s_re_exec:
164      * the patterns are fixed internally, not supplied by the user.
165      */
166     if (s_re_comp(pat)) {
167 	return Error(0, 0,
168 	    "$$\n\tcheckenv(): couldn't compile pattern `%-.500s'.\n", pat);
169     }
170 
171     if (s_re_exec(value) != 1) {
172 	return Error(0, 0,
173 	    "checkenv(): $%.100s (=%.100s) doesn't match pattern %-.500s.\n",
174 			name, value, pat);
175     }
176 
177     /* Limit the value to a reasonable length (MAXENVLEN chars) */
178     len = strlen(value);
179     if (len > MAXENVLEN) {
180 	return Error(0, 0, "Imported environment variables may not \
181 exceed %d chars; you passed a string of length %d!\n", MAXENVLEN, len);
182     }
183 
184     return 0;
185 }
186 
187 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
188 /* Check the checkvar=name,... list.
189  * If the list isn't empty, use /dev/tty for input.
190  */
191 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
192 /* Returns 0 if list ok; -1 otherwise.  */
193 int
check_var_value()194 check_var_value()
195 {
196     int l, ntry, match;
197     char buf[500];
198     char *varname, *truevalue;
199     int ivar;
200     FILE *prompt_fp;
201     char *prompt_dev="/dev/tty";
202 
203     /* This is implemented as an inefficient operation, but it doesn't
204      * matter because we _assume_ the number of variables to be small
205      * and it's only invoked once.
206      */
207 
208     if (!localinfo.checkvar || !localinfo.checkvar[0])
209 	return 0;	/* no checkvar list */
210 
211     if (!(prompt_fp = fopen(prompt_dev, "r+"))) {
212 	return Error(0, 0,
213 	    "Can't open %s to test checkvar=name,...\n", prompt_dev);
214     }
215     if (localinfo.checkvar[1]) {
216 	/* There must be more than one variable */
217 	fprintf(prompt_fp, "%c%s needs you enter some variables \
218 before proceeding...\n", toupper(prog[0]), prog+1);
219     } else {
220 	fprintf(prompt_fp, "%c%s needs you to enter the %s variable \
221 before proceeding.\n", toupper(prog[0]), prog+1, localinfo.checkvar[0]);
222     }
223 
224     for (ivar=0; localinfo.checkvar[ivar]; ivar++) {
225 	varname = localinfo.checkvar[ivar];
226 	truevalue = get_variable(varname);
227 
228 	if (!truevalue) {
229 	    /* Ouch, checkvar=xxx is used, but xxx isn't defined! */
230 	    fclose(prompt_fp);
231 	    return Error(0, 0,
232 		"$$\n\tError in super.tab file: \
233 `checkvar=%s' is used, but variable %s isn't defined!\n", varname, varname);
234 	}
235 
236 	for (ntry=0, match=0; ntry < MAXTRY && !match; ntry++) {
237 	    if (ntry == 0) {
238 		fprintf(prompt_fp, "Enter %s (<Return> for reminder): ",
239 								    varname);
240 	    } else {
241 		fprintf(prompt_fp, "Enter %s (expecting `%s'): ",
242 	    						varname, truevalue);
243 	    }
244 	    fflush(prompt_fp);
245 	    if (!fgets(buf, sizeof(buf), prompt_fp)) {
246 		if (feof(prompt_fp)) {
247 		    fclose(prompt_fp);
248 		    return Error(0, 0, "can't read %s -- stream was closed!\n",
249 				prompt_dev);
250 		} else if (ferror(prompt_fp)) {
251 		    fclose(prompt_fp);
252 		    return Error(0, 0, "error reading %s\n", prompt_dev);
253 		} else {
254 		    fclose(prompt_fp);
255 		    return Error(0, 0, "??? feof() and ferror() return 0, \
256 but I can't read %s\n", prompt_dev);
257 		}
258 	    }
259 
260 	    if (strlen(buf) == sizeof(buf)-1) {
261 		fclose(prompt_fp);
262 		return Error(0, 0, "Ridiculously long value <%.300s...> \
263 returned for variable %s\n", buf, varname);
264 	    }
265 	    l = strlen(buf);
266 	    if (buf[l-1] == '\n')
267 		buf[l-1] = '\0';
268 	    match = (strcmp(buf, truevalue) == 0);
269 	    if (!match) {
270 		fprintf(prompt_fp, "Variable %s incorrect\n", varname);
271 	    }
272 	}
273 	if (!match) {
274 	    fclose(prompt_fp);
275 	    return Error(0, 0, "Never got variable %s entered correctly.\n",
276 	    							varname);
277 	}
278     }
279     fclose(prompt_fp);
280 
281     /* all variables are ok. */
282     return 0;
283 }
284 
285 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
286 /* Compare a value to a pattern */
287 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
288 /* Returns 0 if pat matched; -1 otherwise.  */
289 int
check_value(value,pat)290 check_value(value, pat)
291 char *value;	/* value to check */
292 char *pat;	/* pattern that value must match */
293 {
294 
295     if (!value)
296 	return -1;
297 
298     if (debug)
299 	(void) fprintf(stderr,
300 	    "\tcheck_value args: value=\"%s\"; pat=\"%s\"\n",
301 	    value, pat);
302 
303     if (s_re_comp(pat)) {
304 	return Error(0, 0,
305 	    "$$\n\tcheck_value(): couldn't compile pattern `%-.500s'.\n", pat);
306     }
307 
308     if (s_re_exec(value) != 1) {
309 	return Error(0, 0,
310 	    "check_value(): value doesn't match pattern %-.500s.\n", pat);
311     }
312     return 0;
313 }
314 
315 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
316 /* Option checking -- ensure that options to super are reasonable strings. */
317 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
318 /* Returns 0 if reasonable; -1 otherwise.  */
319 int
checkarg(str)320 checkarg(str)
321 char *str;
322 {
323     int len;
324     static int optlen_tot = 0;	/* running length of all options to super */
325 
326     /* Limit each option to a reasonable length (MAXOPTLEN chars) */
327     len = strlen(str);
328     if (len > MAXOPTLEN) {
329 	return Error(0, 0, "Command-line options may not \
330 exceed %d chars; you passed a string of length %d!\n", MAXOPTLEN, len);
331     }
332 
333     /* Limit the combined option to length MAXOPTLEN_TOT chars */
334     optlen_tot += len;
335     if (optlen_tot > MAXOPTLEN_TOT) {
336 	return Error(0, 0, "The total length of command-line options \
337 may not exceed %d chars!\n", MAXOPTLEN_TOT);
338     }
339 
340     /* Ensure the option pattern matches OPT_PATTERN */
341     if (check_value(str, OPT_PATTERN) != 0)
342 	return -1;
343 
344     return 0;
345 }
346 
347 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
348 /* Look up owner of a file, return uid and gid of owner */
349 /* Return 0 on success, -1 & print message on failure */
350 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
351 int
get_owner(file,uid_p,gid_p)352 get_owner(file, uid_p, gid_p)
353 char *file;
354 uid_t *uid_p;
355 gid_t *gid_p;
356 {
357     /* Return 0 if file ownership ok; -1 if not ok */
358     struct stat st;
359 
360     if (!file || *file == '\0')
361 	return Error(0, 0, "get_owner(): passed null ptr or empty string\n");
362 
363     if (stat(file, &st) == -1)
364 	return Error(1, 0, "stat() failed on file `%s': ", file);
365 
366     *uid_p = st.st_uid;
367     *gid_p = st.st_gid;
368 
369     return 0;
370 }
371 
372 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
373 /* Check ownership of the file */
374 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
375 int
check_owner()376 check_owner()
377 {
378     /* Return 0 if file ownership ok; -1 if not ok */
379     struct passwd *owner_pw;
380 
381     if (*localinfo.owner == '\0')
382 	return 0;	/* no checks required */
383 
384     /* Convert desired-owner string to a uid */
385     owner_pw = getpwentry(1, localinfo.owner);
386     if (!owner_pw)
387 	return -1;
388 
389     if (localinfo.file_uid != owner_pw->pw_uid)
390 	return Error(0, 0,
391 	    "Actual owner of `%s' is uid %d, but superfile \
392 requires owner to be %d (%s).\n",
393 	    localinfo.progs.cmd_file[localinfo.progs.match].File,
394 	    localinfo.file_uid, owner_pw->pw_uid, owner_pw->pw_name);
395 
396     return 0;
397 }
398 
399 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
400 /* Gets an entry from the password file.  Optionally accepts special bracketed
401  * name (e.g. <owner> or <caller>).  Accepts names or text uid's.
402  * Returns ptr to password entry.  The password entry may be that returned
403  * by getpwnam(), or it may be one already stored in a super-owned struct;
404  * you can't make any assumptions about it -- therefore don't modify it.
405  *
406  * The returned pointer points to an area that may be overwritten by later
407  * calls to the getpwxxx() routines; therefore if the caller wants to save
408  * the data, the data must be copied.
409  *
410  * On error, print message and return NULL.
411  */
412 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
413 struct passwd *
getpwentry(allow_brackets,username)414 getpwentry(allow_brackets, username)
415 int allow_brackets;	/* allow special names like <caller> */
416 char *username;		/* name to translate */
417 {
418     struct passwd *pw;
419     int l = strlen(username);
420 
421     if (allow_brackets && username[0] == '<' && username[l-1] == '>') {
422 	/* Translate special name */
423 	if (strcmp(username, "<owner>") == 0) {
424 	    if (localinfo.file_uid != UID_NOTSET) {
425 		pw = getpwuid(localinfo.file_uid);
426 	    } else {
427 		Error(0, 0,
428 	    "$$: getpwentry() Internal Error!\n\tFile owner not yet known!\n");
429 		return NULL;
430 	    }
431 	} else if (strcmp(username, "<caller>") == 0) {
432 	    pw = &userinfo.caller;
433 	} else {
434 	    Error(0, 0, "$$\n\t\tUnknown special name %s\n", username);
435 	    return NULL;
436 	}
437     } else {
438 	/* Regular name or number */
439 	pw = getpwnam(username);
440 	if (!pw) {
441 	    char c;
442 	    int i, numeric;
443 	    numeric = (sscanf(username, "%d%c", &i, &c) == 1);
444 	    if (numeric)
445 		pw = getpwuid(i);
446 	}
447     }
448     if (!pw) {
449 	Error(0, 0, "$$\n\tNo such user or uid as `%s' in password file.\n",
450 	    username);
451 	return NULL;
452     }
453     return pw;
454 }
455 
456 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
457 /* Gets a group entry.
458  * Accepts text gid's or names.
459  * Returns ptr to group entry.
460  *
461  * The returned pointer points to an area that may be overwritten by later
462  * calls to the getgrxxx() routines; therefore if the caller wants to save
463  * the data, the data must be copied.
464  *
465  * On error, print message and return NULL.
466  */
467 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
468 struct group *
getgrentry(name)469 getgrentry(name)
470 char *name;		/* name to translate */
471 {
472     struct group *gr;
473     char c;
474     int i, numeric;
475 
476     gr = getgrnam(name);
477     if (!gr) {
478 	numeric = (sscanf(name, "%d%c", &i, &c) == 1);
479 	if (numeric)
480 	    gr = getgrgid(i);
481     }
482     if (!gr) {
483 	Error(0, 0, "$$\n\tNo such group or gid as `%s'.\n", name);
484 	return NULL;
485     }
486     return gr;
487 }
488 
489 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
490 /* Set supplementary groups according to the specified args                */
491 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
492 #ifndef HAVE_GETGROUPS
493 int
set_suppl_groups()494 set_suppl_groups() { return 0; }
495 #else
496 int
set_suppl_groups()497 set_suppl_groups()
498 {
499     GETGROUPS_T *addgroups_p, *groups_p, groups[NGROUPS_MAX];
500     GETGROUPS_T gotten_groups[NGROUPS_MAX];
501     int naddgroups, ngroups;
502     int i;
503 
504     /* Set the supplementary groups */
505     if (geteuid() != 0) {
506 	/* Can't set supplementary groups if we've changed to some other uid */
507 	return 0;
508     }
509     if (*localinfo.u_g) {
510 	/* Default is to take from that user.  (Don't worry if there is no
511 	 * such user; it'll get noticed later.)  Conflicts with local
512 	 * option groups=xxx, which is disallowed.
513 	 */
514 	if (localinfo.ngroups != GROUPS_NOTSET && !localinfo.groups_added)
515 	    return Error(0, 0, "$$\n\t\tu+g=xxx conflicts with groups=yyy \
516 and may not be used in the same entry\n");
517 
518 	initgroups(localinfo.user, userinfo.new_gid);
519 	/* Check for local or global addgroups */
520 	if (localinfo.ngroups != GROUPS_NOTSET) {
521 	    addgroups_p = localinfo.groups;
522 	    naddgroups = localinfo.ngroups;
523 	} else if (globalinfo.ngroups != GROUPS_NOTSET &&
524 					globalinfo.groups_added) {
525 	    addgroups_p = globalinfo.groups;
526 	    naddgroups = globalinfo.ngroups;
527 	} else {
528 	    addgroups_p = NULL;
529 	    naddgroups = 0;
530 	}
531 	ngroups = Getgroups(NGROUPS_MAX, gotten_groups);
532 	if (ngroups == -1)
533 	    return Error(1, 0, "$$ Getgroups() failed: ");
534 	if (ngroups + naddgroups > NGROUPS_MAX)
535 	    return Error(1, 0,
536 			    "$$\n\t\taddgroups=xxx adds too many groups.");
537 	for (i = 0; i < ngroups; i++)
538 	    groups[i] = gotten_groups[i];
539 	for (groups_p = &groups[ngroups], i=0; i < naddgroups; i++)
540 	    *groups_p++ = *addgroups_p++;
541 	ngroups += naddgroups;
542 
543     }  else if (localinfo.ngroups != GROUPS_NOTSET) {
544 	/* There are some explicit local groups=xxx */
545 	if (Setgroups(localinfo.ngroups, localinfo.groups) == -1)
546 	    return Error(1, 0, "Failed to set supplementary groups list: ");
547 
548     } else if (globalinfo.ngroups != GROUPS_NOTSET) {
549 	/* There is an explicit global groups=xxx or addgroups=xxx */
550 	if (Setgroups(globalinfo.ngroups, globalinfo.groups) == -1)
551 	    return Error(1, 0, "Failed to set supplementary groups list: ");
552     } else {
553 	/* Default is no supplementary groups */
554 	if (Setgroups(0, localinfo.groups) == -1)
555 	    return Error(1, 0,
556 			    "Failed to clear supplementary groups list: ");
557     }
558     return 0;
559 }
560 #endif
561 
562 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
563 /* Set user, group, and supplementary groups according to the specified args */
564 /* Side effect: if localinfo.user is numeric, change it to a name */
565 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
566 int
set_u_g()567 set_u_g()
568 {
569     /* Return 0 on success, -1 on failure */
570     void endgrent();
571     int i, j, k = -1;
572     int found_gid = -1, found_egid = -1;
573     struct passwd *pw = NULL;
574     int use_setreuid;	/* true iff we need to use setreuid() */
575     int use_setregid;	/* true iff we need to use setregid() */
576     uid_t uid_pw_uid = -1, euid_pw_uid = -1;
577     gid_t u_g_pw_gid = -1;
578     char *uid_pw_name = NULL, *euid_pw_name = NULL;
579 
580     /*
581      * Start by looking up user id's and group id's.  We'll assign
582      * actual values after we've looked it all up.
583      */
584 
585     /* First, get the group id's specified by {e,}gid=xxx */
586     if (*localinfo.group) {
587 	found_gid = findgid(1, localinfo.group);
588 
589 	if (found_gid == -1) {
590 	    return Error(0, 0,
591 	    "$$\n\tCan't set gid: no such group as `%s' in group file.\n",
592 		localinfo.group);
593 	}
594     }
595     if (*localinfo.egroup) {
596 	found_egid = findgid(1, localinfo.egroup);
597 
598 	if (found_egid == -1) {
599 	    return Error(0, 0,
600 	    "$$\n\tCan't set egid: no such group as `%s' in group file.\n",
601 		localinfo.egroup);
602 	}
603     }
604 
605     /* Second, get the password entries specified by {e,}uid=xxx, u+g=xxx */
606     if (*localinfo.u_g && !*localinfo.user) {
607 	/* u+g=zzz was used, but uid=xxx was not.  So, set user from u+g. */
608 	strcpy(localinfo.user, localinfo.u_g);
609     }
610 
611     if (*localinfo.user) {
612 	pw = getpwentry(1, localinfo.user);
613 	if (!pw)
614 	    return -1;
615 	uid_pw_uid  = pw->pw_uid;
616 	uid_pw_name = strdup(pw->pw_name);
617 	if (!uid_pw_name)
618 		return Error(0, 0,
619 		    "$$\n\tfailed to malloc space for uid_pw_name=<%s>\n", pw->pw_name);
620 
621     }
622     if (*localinfo.euser) {
623 	pw = getpwentry(1, localinfo.euser);
624 	if (!pw)
625 	    return -1;
626 	euid_pw_uid  = pw->pw_uid;
627 	euid_pw_name = strdup(pw->pw_name);
628 	if (!euid_pw_name)
629 		return Error(0, 0,
630 		    "$$\n\tfailed to malloc space for euid_pw_name=<%s>\n", pw->pw_name);
631     }
632     if (*localinfo.u_g) {
633 	pw = getpwentry(1, localinfo.u_g);
634 	if (!pw)
635 	    return -1;
636 	u_g_pw_gid  = pw->pw_gid;
637     }
638 
639     pw = NULL;
640 
641     if (*localinfo.group && *localinfo.u_g) {
642 	return Error(0, 0,
643 		"$$\n\tCan't mix options gid=xxx and u+g=yyy in one entry.");
644     }
645 
646     /*
647      * OK, now we have all the uid/gid info.  Select uid's and gid's,
648      * and select the appropriate set of functions to do the assignments.
649      */
650 
651     /* UID defaults: */
652     userinfo.new_uid = userinfo.caller.pw_uid;
653     use_setreuid = 0;		/* don't have to use setreuid() */
654 
655     if (*localinfo.euser) {
656 	/* euid=xxx was used */
657 	userinfo.new_euid = euid_pw_uid;
658 	if (strcmp(euid_pw_name, localinfo.euser) != 0) {
659 	    /* localinfo.euser must be numeric; convert it to string */
660 	    strcpy(localinfo.euser, euid_pw_name);
661 	}
662 	use_setreuid = 1;	/* have to use setreuid() */
663     }
664 
665     if (*localinfo.user) {
666 	/* uid=xxx or u+g=xxx was used */
667 	userinfo.new_uid = uid_pw_uid;
668 	if (strcmp(uid_pw_name, localinfo.user) != 0) {
669 	    /* localinfo.user must be numeric; convert it to string */
670 	    strcpy(localinfo.user, uid_pw_name);
671 	}
672     }
673 
674     /* GID defaults: */
675     userinfo.new_gid = userinfo.caller.pw_gid;
676     use_setregid = 0;		/* don't have to use setreuid() */
677 
678     if (*localinfo.egroup) {
679 	/* egid=xxx was used */
680 	userinfo.new_egid = found_egid;
681 	use_setregid = 1;	/* have to use setreuid() */
682     }
683 
684     if (*localinfo.group) {
685 	/* gid=xxx was used */
686 	userinfo.new_gid = found_gid;
687     } else if (*localinfo.u_g) {
688 	userinfo.new_gid = u_g_pw_gid;
689     }
690 
691     /* Set supplementary groups */
692     if (set_suppl_groups() == -1)
693 	return -1;
694 
695 
696     /* Now set uid & gid */
697     if (use_setregid) {
698 #ifdef HAVE_SETREGID
699 	if ((i=setregid(userinfo.new_gid, userinfo.new_egid)) == -1) {
700 	    return Error(1, 0, "setregid(gid=%d, egid=%d) failed: ",
701 		    	userinfo.new_gid, userinfo.new_egid);
702 	} else if ((j=getgid()) != userinfo.new_gid ||
703 				    (k=getegid()) != userinfo.new_egid) {
704 	    return Error(0, 0,
705 	"setregid(gid=%d,egid=%d) returned 0, but getgid()=%d,getegid()=%d!",
706 		userinfo.new_gid, userinfo.new_egid, j, k);
707 	}
708 #else
709 	return Error(0, 0, "Can't use egid=xxx option, because this host doesn't offer the setregid() function");
710 #endif
711     } else if (*localinfo.group || *localinfo.u_g) {
712 	if ((i=setgid(userinfo.new_gid)) == -1) {
713 	    return Error(1, 0, "setgid(gid=%d) failed: ", userinfo.new_gid);
714 	} else if ((j=getgid()) != userinfo.new_gid) {
715 	    return Error(0, 0,
716 		"setgid(gid=%d) returned %d, but getgid() returned %d!",
717 		userinfo.new_gid, i, j);
718 	}
719     }
720 
721 
722     if (use_setreuid) {
723 #ifdef HAVE_SETREUID
724 	if ((i=setreuid(userinfo.new_uid, userinfo.new_euid)) == -1) {
725 	    return Error(1, 0, "setreuid(uid=%d, euid=%d) failed: ",
726 		    	userinfo.new_uid, userinfo.new_euid);
727 	} else if ((j=getuid()) != userinfo.new_uid ||
728 				    (k=geteuid()) != userinfo.new_euid) {
729 	    return Error(1, 0,
730 	"setreuid(uid=%d,euid=%d) returned 0, but getuid()=%d,geteuid()=%d!",
731 		userinfo.new_uid, userinfo.new_euid, j, k);
732 	}
733 #else
734 	return Error(1, 0, "Can't use euid=xxx option, because this host doesn't offer the setreuid() function");
735 #endif
736     } else if (*localinfo.user || *localinfo.u_g) {
737 	if ((i=setuid(userinfo.new_uid)) == -1) {
738 	    return Error(1, 0, "setuid(uid=%d) failed: ", userinfo.new_uid);
739 	} else if ((j=getuid()) != userinfo.new_uid) {
740 	    return Error(1, 0,
741 		"setuid(uid=%d) returned %d, but getuid() returned %d!",
742 		userinfo.new_uid, i, j);
743 	}
744     }
745 
746     if (uid_pw_name)
747 	    free(uid_pw_name);
748     if (euid_pw_name)
749 	    free(euid_pw_name);
750     return 0;
751 }
752 
753 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
754 /* Puts the encrypted password in userinfo.encr, and the salt in userinfo.salt.
755  * Returns 0 on success, -1 on failure to obtain the password.
756  */
757 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
758 
759 #ifdef SUNOS5
760 #define HAVE_G_E_PW
761 
762 int
get_encrypted_pw()763 get_encrypted_pw()
764 {
765     /* Shadow passwords are always used on Sunos 5.x */
766     struct spwd *caller_pw;
767     if (!(caller_pw = getspnam(userinfo.caller.pw_name))) {
768 	authInitErrno = errno;
769 	sprintf(authInitMsg1,
770 		"Failed to obtain shadow password entry for user %s",
771 		userinfo.caller.pw_name);
772 	return -1;
773     }
774     strcpy(userinfo.encr, caller_pw->sp_pwdp);
775     return get_setting();
776 }
777 #endif
778 
779 #ifdef _HPUX_SOURCE
780 #define HAVE_G_E_PW
781 
782 int
get_encrypted_pw()783 get_encrypted_pw()
784 {
785     if(iscomsec()) {
786 
787 	struct pr_passwd *caller_pw;
788 
789 	if (!(caller_pw = getprpwnam(userinfo.caller.pw_name))) {
790 	    authInitErrno = errno;
791 	    sprintf(authInitMsg1,
792 	      "Failed to obtain Trusted Computing Base entry for user %s",
793 		userinfo.caller.pw_name);
794 	    return -1;
795 	}
796 	strcpy(userinfo.encr, caller_pw->ufld.fd_encrypt);
797 
798     } else {
799 	struct passwd *caller_pw;
800 
801 	/* See if we can do shadow password lookup for HPUX 9.x.
802 	 * The rule is that if /.secure/etc/passwd exists, we have to use it;
803 	 * otherwise, fall through to regular password file lookup.
804 	 */
805 	static struct stat st;
806 	if (stat("/.secure/etc/passwd", &st) == 0) {
807 	    /* Shadow password file exists; use it */
808 	    struct s_passwd *caller_pw;
809 	    if (!(caller_pw = getspwnam(userinfo.caller.pw_name))) {
810 		authInitErrno = errno;
811 		sprintf(authInitMsg1,
812 		    "Failed to obtain shadow password entry for user %s",
813 		    userinfo.caller.pw_name);
814 		return -1;
815 	    }
816 	    strcpy(userinfo.encr, caller_pw->pw_passwd);
817 	} else {
818 
819 	    /* Fall through to regular password file lookup. */
820 	    strcpy(userinfo.encr, userinfo.caller.pw_passwd);
821 	}
822     }
823     return get_setting();
824 }
825 #endif
826 
827 #ifdef SCO
828 #define HAVE_G_E_PW
829 
830 int
get_encrypted_pw()831 get_encrypted_pw()
832 {
833     struct passwd *caller_pw;
834     struct spwd *caller_ps;
835 
836     if (!(caller_pw = getpwnam(userinfo.caller.pw_name))) {
837 	sprintf(authInitMsg1, "No password entry for user %s.\n",
838 		userinfo.caller.pw_name);
839 	return -1;
840     }
841 
842     /* SCO 3.2v4 has "x" in password field to indicate shadow password
843      * file has to be consulted.
844      */
845     if (strcmp(caller_pw->pw_passwd, "x") == 0) {
846 	/* Shadow password in use... */
847 	if (!(caller_ps = getspnam(userinfo.caller.pw_name))) {
848 	    authInitErrno = errno;
849 	    sprintf(authInitMsg1,
850 		"Failed to obtain shadow password entry for user %s",
851 		userinfo.caller.pw_name);
852 	    return -1;
853 	}
854 	strcpy(userinfo.encr, caller_ps->sp_pwdp);
855 
856     } else {
857 	/* Fall through to regular password file lookup. */
858 	strcpy(userinfo.encr, caller_pw->pw_passwd);
859     }
860     return get_setting();
861 }
862 #endif
863 
864 #ifdef Digital_UNIX
865 #define HAVE_G_E_PW
866 
867 int
get_encrypted_pw()868 get_encrypted_pw()
869 {
870     struct passwd *caller_pw;
871     struct pr_passwd *caller_prpass;
872 
873     if (!(caller_pw = getpwnam(userinfo.caller.pw_name))) {
874 	sprintf(authInitMsg1, "No password entry for user %s.\n",
875 		userinfo.caller.pw_name);
876 	return -1;
877     }
878 
879     /* Digital Unix has "x" in password field to indicate shadow password
880      * file has to be consulted.
881      */
882     if (strcmp(caller_pw->pw_passwd, "x") == 0) {
883 	/* Protected (shadow) password in use... */
884 	if (!(caller_prpass = getprpwnam(userinfo.caller.pw_name))) {
885 	    authInitErrno = errno;
886 	    sprintf(authInitMsg1,
887 		"Failed to obtain shadow password entry for user %s",
888 	    userinfo.caller.pw_name);
889 	    return -1;
890 	}
891 	strcpy(userinfo.encr, caller_prpass->ufld.fd_encrypt);
892 
893     } else {
894 	/* Fall through to regular password file lookup. */
895 	strcpy(userinfo.encr, caller_pw->pw_passwd);
896     }
897     return get_setting();
898 }
899 #endif
900 
901 #ifdef __linux__
902 #define HAVE_G_E_PW
903 
904 int
get_encrypted_pw()905 get_encrypted_pw()
906 {
907     /* Use /etc/shadow if it exists; else fall back on std */
908     static struct stat st;
909     if (stat("/etc/shadow", &st) == 0) {
910 	struct spwd *spwd = 0L;
911 	if (!(spwd = getspnam(userinfo.caller.pw_name))) {
912 	    /* Gordon Lack notes: Linux doesn't use the shadow file for NIS
913 	     * passwords, so if you get ENOENT or no error, then try the
914 	     * local password file.
915 	     */
916 	    if (errno == ENOENT || errno == 0) {
917 		strcpy(userinfo.encr, userinfo.caller.pw_passwd);
918 	    } else {
919 		authInitErrno = errno;
920 		sprintf(authInitMsg1,
921 		    "Failed to obtain shadow password entry for user %s",
922 		    userinfo.caller.pw_name);
923 		return -1;
924 	    }
925         } else {
926 	    /* got shadow entry */
927 	    strcpy(userinfo.encr, spwd->sp_pwdp);
928 	}
929     } else {
930 	/* Fall through to regular password file lookup. */
931 	strcpy(userinfo.encr, userinfo.caller.pw_passwd);
932     }
933 
934     return get_setting();
935 }
936 #endif
937 
938 #ifdef __FreeBSD__
939 #define HAVE_G_E_PW
940 
941 int
get_encrypted_pw()942 get_encrypted_pw()
943 {
944     /* getpwnam() and getpwuid() will have read the shadow passwd
945      * if our effective uid == root; no need to use getspnam() or the like.
946      * But if the encrypted pw is "*", then we didn't get the real pw.
947      */
948     strcpy(userinfo.encr, userinfo.caller.pw_passwd);
949     if (strcmp(userinfo.encr, "*") == 0) {
950 	sprintf(authInitMsg1, "Can't get encrypted password, \
951 or there is no password, for user %s", userinfo.caller.pw_name);
952 	return -1;
953     }
954 
955     return get_setting();
956 }
957 #endif
958 
959 #ifdef __OpenBSD__
960 #define HAVE_G_E_PW
961 
962 int
get_encrypted_pw()963 get_encrypted_pw()
964 {
965     /* getpwnam() and getpwuid() will have read the shadow passwd
966      * if our effective uid == root; no need to use getspnam() or the like.
967      * But if the encrypted pw is "*", then we didn't get the real pw.
968      */
969     strcpy(userinfo.encr, userinfo.caller.pw_passwd);
970     if (strcmp(userinfo.encr, "*") == 0) {
971 	sprintf(authInitMsg1, "Can't get encrypted password, \
972 or there is no password, for user %s", userinfo.caller.pw_name);
973 	return -1;
974     }
975 
976     return get_setting();
977 }
978 #endif
979 
980 #ifndef HAVE_G_E_PW
981 
982 int
get_encrypted_pw()983 get_encrypted_pw()
984 {
985     /* Vanilla password file lookup */
986 
987     strcpy(userinfo.encr, userinfo.caller.pw_passwd);
988     strncpy(userinfo.salt, userinfo.caller.pw_passwd, 2);
989     return get_setting();
990 }
991 #endif
992 
993 /*
994  * Read the userinfo.encr (encrypted passwd field) and pull out the data
995  * needed for the "salt" or "setting" argument to crypt().
996  * This version implements the following rule:
997  * o  if encr begins with "$", the entire encr field should be used
998  * 	(crypt() will be responsible for extracting the data it needs);
999  * o  otherwise, if encr begins with "_", use the first 9 chars
1000  * 	(crypt() will discard the leading "_" and use the next 8 characters);
1001  * o  otherwise, the salt is the first two chars.
1002  *
1003  * Returns:
1004  * -1 (plus Error()) on failure
1005  * 0 on success.
1006  */
1007 int
get_setting()1008 get_setting()
1009 {
1010     if (sizeof(userinfo.salt) <= strlen(userinfo.encr)) {
1011 	return Error(0, 0, "Compilation error: the userinfo.salt field is too short (%d chars) to hold a copy of the userinfo.encr field (%d chars) for user %s",
1012 	    (int) sizeof(userinfo.salt), (int) strlen(userinfo.encr),
1013 	    userinfo.caller.pw_name);
1014     }
1015 
1016     if (userinfo.encr[0] == '$') {
1017 	strcpy(userinfo.salt, userinfo.encr);
1018 
1019     } else if (userinfo.encr[0] == '_') {
1020 	/* crypt() _might_ parse the encr field to pull off the necessary
1021 	 * salt, but we don't make that assumption.
1022 	 */
1023 	strncpy(userinfo.salt, userinfo.encr, 9);
1024 	userinfo.salt[9] = '\0';
1025 
1026     } else {
1027 	/* Salt is first two chars. */
1028 	strncpy(userinfo.salt, userinfo.encr, 2);
1029 	userinfo.salt[2] = '\0';
1030     }
1031 
1032     return 0;
1033 }
1034 
1035 
1036 
1037 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1038 /* Returns ptr to string with name of desired authentication method. */
1039 char *
auth_name()1040 auth_name()
1041 {
1042     if (!localinfo.authinfo.required) {
1043 	return "None";
1044     } else if (localinfo.authinfo.method == SUPER_AUTH_PASSWORD) {
1045 	return "Password";
1046     } else if (localinfo.authinfo.method == SUPER_AUTH_PAM) {
1047 	return "PAM";
1048     } else {
1049 	Error(0, 1, "auth_name: don't recognize auth method %d!\n",
1050 						localinfo.authinfo.method);
1051     }
1052     /* NOTREACHED */
1053     return "Unknown!"; /* to make gcc -Wall happy... */
1054 }
1055 
1056 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1057 
1058 
1059 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1060 /* Checks if authentication is needed, and does so if needed.
1061  * Expects that the encrypted password is already in userinfo.{encr,salt},
1062  * so that this can be run after switching to unpriviledged uid.
1063  * Returns 0 on success, -1 on error.
1064 
1065  * The timestamp directory faces the same problem as the logfile: if the
1066  * administrator wants to share an NFS-mounted directory across hosts
1067  * on which root is translated to nobody for NFS access, we have to be
1068  * able to create the timestamp file under a special uid.  This is done
1069  * just as in open_writer(): we fork, setuid(), and do the file
1070  * manipulation in the child.  This allows us to implement a special uid
1071  * for the timestamp file, without needing the operating system to
1072  * offer saved uid's or interprocess file-descriptor passing, etc.
1073  */
1074 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1075 int
check_auth(cmd)1076 check_auth(cmd)
1077 char *cmd;
1078 {
1079     char file[MAXPATHLEN];
1080     struct stat st;
1081     int l, istat, file_exists, got_auth, err, timestamp_creatok;
1082     int timed_out = 0;
1083     int status;
1084     pid_t pid, child;
1085     char mkdirMsg[1000];
1086     char *authuser;
1087 
1088     if (!localinfo.authinfo.required)
1089 	return 0;			/* don't need authentication */
1090 
1091     if (*authInitMsg1 || *authInitMsg2) {
1092 	/* A message was generated during authentication initialization,
1093 	 * way back before the super.tab file was read.  It was only
1094 	 * relevant if the command required authorization... well, this
1095 	 * command requires user authentication, so print the message now.
1096 	 */
1097 	if (*authInitMsg1) {
1098 	    if (authInitErrno) {
1099 		errno = authInitErrno;
1100 		Error(1, 0, "%s: ", authInitMsg1);
1101 	    } else {
1102 		Error(0, 0, "%s", authInitMsg1);
1103 	    }
1104 	}
1105 	if (*authInitMsg2) {
1106 	    Error(0, 0, "%s", authInitMsg2);
1107 	}
1108     }
1109 
1110     /* Create or update the timestamp file even if the lifetime is 0
1111      * (always ask for password).  We do this because the user may
1112      * execute _another_ command which has a password expiry > 0,
1113      * and which will be happy to use the password that was already
1114      * entered with the 0-lifetime command.
1115      */
1116     child = fork();
1117     if (child == -1) {
1118 	Error(1, 0, "Failed to create child for timestamp processing: ");
1119 	return -1;
1120 
1121     } else if (child > 0) {
1122 	/* In parent -- wait to see if the child succeeded */
1123 	while ((pid = wait(&status)) > 0 && pid != child) {
1124 	    if (pid == globalinfo.log.pid) {
1125 		Error(0, 0,
1126 	    "Warning: logging process died -- logging to file has stopped.");
1127 		globalinfo.log.pid = -1;
1128 	    } else {
1129 		Error(0, 0,
1130 		    "Wait() surprised! check_pass() received pid %d;\n\t\
1131 expected child pid = %d; waiting for correct pid...\n", pid, child);
1132 	    }
1133 	}
1134 	if (pid == -1) {
1135 	    /* wait() failed */
1136 	    Error(1, 0, "Waiting for timestamp creation process: ");
1137 	    return -1;
1138 	} else if (status == 0) {
1139 	    /* child succeeded */
1140 	    return 0;
1141 	} else if (status != 0) {
1142 	    /* child failed to authenticate user */
1143 	    Error(0, 0, "Authentication failed\n");
1144 	    return -1;
1145 	}
1146 	/* NOTREACHED */
1147     }
1148 
1149     /*
1150      * If here, must be in child.
1151      * Unless this command specifies a timeout > 0, don't generate
1152      * error messages if we fail to store timestamp info.
1153      * We'll use exit code 1 for failure to authenticate;
1154      * otherwise we'll exit 0 (success).
1155      */
1156     if (child != 0) {
1157 	Error(0, 1, "Internal error: child=%d; should be 0!\n", child);
1158     }
1159 
1160     /*
1161      * setuid, then make and/or test the directory
1162      */
1163     if (*localinfo.authinfo.ts_user != '\0') {
1164 	stringcopy(localinfo.user, localinfo.authinfo.ts_user,
1165 					    sizeof(localinfo.user));
1166 	*localinfo.group = '\0';
1167 	*localinfo.u_g = '\0';
1168 	if (set_u_g() == -1) {
1169 	    if (localinfo.authinfo.timeout > 0) {
1170 		Error(1, 0,
1171 		"failed to setuid to user=%s before setting timestamp file: ",
1172 		    localinfo.user);
1173 	    }
1174 	    exit(2);
1175 	}
1176     }
1177     /* Make the timestamp directory name */
1178     timestamp_creatok = 1;
1179     mkdirMsg[0] = '\0';
1180     if (!makedirname(TIMESTAMP_DIR,
1181 		    globalinfo.authinfo.perhost ? userinfo.hostname : "",
1182 		    file, &err, mkdirMsg)) {
1183 	timestamp_creatok = 0;
1184 	if (localinfo.authinfo.timeout > 0 && mkdirMsg[0]) {
1185 	    if (err) {
1186 		errno = err;
1187 		Error(1, 0, "Warning: can't record timestamp: %s: ",
1188 			mkdirMsg);
1189 	    } else {
1190 		Error(0, 0, "Warning: can't record timestamp: %s",
1191 			mkdirMsg);
1192 	    }
1193 	}
1194     }
1195 
1196     /* Make the timestamp directory */
1197     if (timestamp_creatok) {
1198 	mkdirMsg[0] = '\0';
1199 	if (makedir(file, &err, mkdirMsg) == -1) {
1200 	    timestamp_creatok = 0;
1201 	    if (localinfo.authinfo.timeout > 0 && mkdirMsg[0]) {
1202 		if (err) {
1203 		    errno = err;
1204 		    Error(1, 0,
1205 			"Warning: can't record timestamp: %s: ",
1206 			mkdirMsg);
1207 		} else {
1208 		    Error(0, 0,
1209 			"Warning: can't record timestamp: %s",
1210 			mkdirMsg);
1211 		}
1212 	    }
1213 	}
1214     }
1215 
1216     /* Make the file in the timestamp directory */
1217     if (timestamp_creatok) {
1218 	l = strlen(file) + 1 + strlen(userinfo.caller.pw_name);
1219 	if (l >= MAXPATHLEN) {
1220 	    if (localinfo.authinfo.timeout > 0) {
1221 		Error(1, 0,
1222 		"Can't create timestamp file: would exceed MAXPATHLEN = %d\n",
1223 		MAXPATHLEN);
1224 	    }
1225 	    timestamp_creatok = 0;
1226 	}
1227     }
1228 
1229     if (timestamp_creatok) {
1230 	strcat(file, "/");
1231 	strcat(file, userinfo.caller.pw_name);
1232 
1233 	istat = stat(file, &st);
1234 	if (istat != 0 && errno != ENOENT) {
1235 	    if (localinfo.authinfo.timeout > 0) {
1236 		Error(1, 0, "Failed to stat timestamp file `%s': ", file);
1237 	    }
1238 	    timestamp_creatok = 0;
1239 	}
1240     }
1241 
1242     if (timestamp_creatok) {
1243 	file_exists = (istat == 0);
1244 	if (file_exists) {
1245 	    timed_out = (localinfo.authinfo.timeout < 1) ||
1246 			((time(NULL)-st.st_mtime) >
1247 			localinfo.authinfo.timeout*60);
1248 	}
1249     }
1250 
1251     got_auth=0;
1252     if (localinfo.authinfo.user[0]) {
1253 	authuser = localinfo.authinfo.user;	/* authuser=xxx local opt */
1254     } else if (globalinfo.authinfo.user[0]) {
1255 	authuser = globalinfo.authinfo.user;	/* authuser=xxx global opt */
1256     } else {
1257 	authuser = userinfo.caller.pw_name;	/* default: caller's pw */
1258     }
1259     if (!timestamp_creatok || !file_exists || timed_out) {
1260 	switch (localinfo.authinfo.method) {
1261 	case SUPER_AUTH_PASSWORD:
1262 	    got_auth = (get_password(cmd, userinfo.caller.pw_name, authuser,
1263 				    userinfo.salt, userinfo.encr) == 1);
1264 	    break;
1265 	case SUPER_AUTH_PAM:
1266 #if WITH_PAM
1267 	    got_auth = (get_pam(cmd, userinfo.caller.pw_name, authuser) == 1);
1268 #else
1269 	    Error(0, 1, "Auth method is PAM, but this copy of super \
1270 was compiled without PAM support!");
1271 #endif
1272 	    break;
1273 	default:
1274 	    Error(0, 1, "Internal error in check_auth: \
1275 unknown auth method %d!\n", localinfo.authinfo.method);
1276 	}
1277 	if (!got_auth)
1278 	    return -1;
1279     }
1280 
1281     /* NOTE: A race condition is possible between two super's, with the
1282      * worst-case effect of an error message and failure to run the
1283      * requested command.
1284      */
1285 
1286     /* If file exists, and we haven't (a) gotten the password again, or
1287      * (b) supposed to automatically refresh the timestamp, do nothing to
1288      * the file except ensure that we own it.
1289 
1290      * Otherwise create the file (unlink it first if it exists).
1291      */
1292     if (!timestamp_creatok) {
1293 	/* do nothing: we failed in setting up the timestamp file,
1294 	 * so don't try to update it.
1295 	 */
1296 
1297     } else if (file_exists && !(got_auth || localinfo.authinfo.renewtime)) {
1298 	if (st.st_uid != geteuid())
1299 	    return Error(0, 0,
1300 	"Timestamp file `%s' is owned by uid=%d, but expected owner=%d.\n\
1301 \tN.B. If you recently changed the value of timestampuid=xxx, all existing\n\
1302 \tfiles in the timestamp directory _may_ have the wrong owner; delete them.\n\
1303 \t(No security hole appears when you delete a timestamp file.)\n",
1304 	    file, st.st_uid, geteuid());
1305 
1306     } else {
1307 	if (file_exists) {
1308 	    if (unlink(file) != 0)
1309 		return Error(1, 0,
1310 		    "Failed to unlink() timestamp file `%s': ", file);
1311 	}
1312 	if (open(file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0200) == -1)
1313 	    return Error(1, 0,
1314 		"Failed to open() timestamp file `%s': ", file);
1315     }
1316     exit(0);
1317     /* UNREACHABLE */
1318     Error(0, 1, "Unreachable code!\n");
1319     return 0;
1320 }
1321 
1322 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1323 /* Encrypt a password.  Returns value from crypt() or similar. */
1324 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1325 char *
docrypt(prompt,salt)1326 docrypt(prompt, salt)
1327 char *prompt;
1328 char *salt;
1329 {
1330     char *crypt();
1331     char *getpass();
1332     char buf[300];
1333     int n;
1334 
1335     n = s_getpass(prompt, use_stdin, buf, sizeof(buf));
1336     if (n < 0) {
1337 	Error(0, 1, "Failed to get password.\n");
1338     } else if (n >= sizeof(buf)) {
1339 	Error(0, 1, "Buffer too small (%d chars) to hold input password!\n",
1340 							(int) sizeof(buf)-1);
1341     }
1342 
1343 #ifdef _HPUX_SOURCE
1344 
1345     if (iscomsec()) {
1346 	#if (HPUX_MAJOR == 10)
1347 	    return bigcrypt(buf, salt);
1348 	#endif
1349 	#if (HPUX_MAJOR == 11)
1350 	    return bigcrypt(buf, salt);
1351 	#endif
1352 	return crypt(buf, salt);
1353     } else {
1354 	return crypt(buf, salt);
1355     }
1356 
1357 #else
1358 
1359     return crypt(buf, salt);
1360 
1361 #endif
1362 }
1363 
1364 
1365 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1366 /* Gets a user's encrypted password. Returns -1 on failure, +1 on success */
1367 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1368 int
get_password(cmd,caller,user,salt,encr)1369 get_password(cmd, caller, user, salt, encr)
1370 char *cmd;
1371 char *caller;	/* the person who invoked super */
1372 char *user;	/* the person whose authentication is required */
1373 char *salt;
1374 char *encr;
1375 {
1376     /* No such file or password timed out -- get password */
1377     int ntry, match;
1378     char msg[500];
1379     char *encrypted = NULL;
1380 
1381     if (strcmp(encr, "") == 0) {
1382 	return Error(0, 0,
1383 	"Command requires a password, but user `%s' has no password\n", user);
1384     }
1385     for (ntry=0, match=0; ntry < MAXTRY && !match; ntry++) {
1386 	if (ntry == 0) {
1387 	    if (localinfo.authinfo.prompt && localinfo.authinfo.prompt[0]) {
1388 		stringcopy(msg,
1389 			do_variables(localinfo.authinfo.prompt), sizeof(msg));
1390 	    } else if (strcmp(caller, user) == 0) {
1391 		(void) sprintf(msg,
1392 	"Your password is required for super command `%.400s'...\nPassword: ",
1393 		cmd);
1394 	    } else {
1395 		(void) sprintf(msg,
1396 	"%c%s's password is required for super command `%.400s'...\nPassword: ",
1397 		toupper(*user), user+1, cmd);
1398 	    }
1399 	} else {
1400 	    strcpy(msg, "Password incorrect\nPassword: ");
1401 	}
1402         encrypted = docrypt(msg, salt);
1403 
1404 	if (encr && encrypted) {
1405 	    match = (strcmp(encr, encrypted) == 0);
1406 	} else {
1407 	    match = 0;
1408 	}
1409     }
1410     if (!match)
1411 	return Error(0, 0, "Password incorrect\n");
1412     return 1;
1413 }
1414 
1415 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1416 /* Looks up a group name or number (as a text string), returns gid.
1417  * Accepts special names (e.g. <caller> or <owner>).
1418  * Returns -1 if no such group.
1419  */
1420 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1421 int
findgid(allowbrackets,grouplabel)1422 findgid(allowbrackets, grouplabel)
1423 int allowbrackets;	/* accept names like <caller> */
1424 char *grouplabel;	/* name or numeric form */
1425 {
1426     struct group *gp;
1427     void endgrent();
1428     int numeric_gid;
1429     int found_gid, is_numeric=0;
1430     int l=strlen(grouplabel);
1431 
1432     char c;
1433 
1434     if (allowbrackets && grouplabel[0] == '<' && grouplabel[l-1] == '>') {
1435 	/* Translate special name */
1436 	struct passwd *pw = getpwentry(1, grouplabel);
1437 	if (!pw)
1438 	    return -1;
1439 	else
1440 	    return pw->pw_gid;
1441     }
1442 
1443     is_numeric = (sscanf(grouplabel, "%d%c", &numeric_gid, &c) == 1);
1444 
1445     if (is_numeric)
1446 	return numeric_gid;
1447 
1448     /* Grouplabel didn't look like a number (according to sscanf),
1449      * so look up its name.
1450      */
1451     setgrent();
1452     for (found_gid = -1, gp = getgrent(); gp; gp = getgrent()) {
1453 	if (strcmp(grouplabel, gp->gr_name) == 0) {
1454 	    /* Found the gid in the group file */
1455 	    found_gid = gp->gr_gid;
1456 	    break;
1457 	}
1458     }
1459     endgrent();
1460 
1461     return found_gid;
1462 }
1463 
1464 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1465 /* Adds condition to condition list.
1466  *	returns -1 on syntax error, malloc error, etc;
1467  *	returns 0 otherwise.
1468  */
1469 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1470 int
InsertCondition(condition,s,isglobal)1471 InsertCondition(condition, s, isglobal)
1472 char *condition;	/* condition to insert: xxx~yyy */
1473 char *s;		/* pts to yyy in condition */
1474 int isglobal;		/* Is this a per-command or global condition? */
1475 {
1476     char **globlist;
1477     TimeList *tl;
1478     int i;
1479     int invert = (*condition == '!');
1480 
1481     if ( invert )
1482 	condition++;
1483 
1484     /* All conditions accept {a,b,c}  or a,b,c  as lists.
1485      * Form the globlist and pass along...
1486      */
1487     /* Do brace globbing */
1488     if ((i=globbraces(s, 1, &globlist)) != 0) {
1489 	/* Local Condition */
1490 	return Error(0, 0, "$$Missing `%c'.\n", i);
1491     }
1492     if (balancedbraces(s) != 0) {
1493 	return Error(0, 0, "$$Unbalanced braces in %s.\n", s);
1494     }
1495 
1496     if (STRMATCH3("time", condition, s-1)) {
1497 	tl = isglobal ? &globalinfo.timeafter : &localinfo.time;
1498 	if (InsertTimeList(s, globlist,
1499 			tl, isglobal ? "global" : "local", invert) == -1)
1500 	    return -1;
1501 
1502     } else if (STRMATCH3("user", condition, s-1)) {
1503 	if (InsertUserList(s, globlist,
1504 		    &localinfo.userpats, &localinfo.origtext, invert) == -1)
1505 	    return -1;
1506 
1507     } else {
1508 	return Error(0, 0,
1509 		"$$\n\tInternal error: unrecognized condition <%s>.\n",
1510 		condition);
1511     }
1512     return 0;
1513 }
1514 
1515 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1516 /* Add a user/group/host pattern to a list.
1517  *	returns -1 on syntax error, malloc error, etc;
1518  *	returns 0 otherwise.
1519  */
1520 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1521 int
InsertUserList(wd,wdlist,sl,otl,invert)1522 InsertUserList(wd, wdlist, sl, otl, invert)
1523 char *wd;	/* Pattern to match; must NOT have leading '!'; braces ok;
1524 		 * disallow '<', '>'.
1525 		 */
1526 char **wdlist;	/* brace-expanded u/g/h list */
1527 Simple2List *sl;/* Insert user list elements (i.e. argument wdlist)
1528 		 * at sl->next.
1529 		 */
1530 SimpleList *otl;/* Insert original text (i.e. argument wd) * at otl->next.  */
1531 int invert;	/* Inverts the test */
1532 {
1533     int iwd;
1534     char *tok, *s;
1535     SimpleList *new;
1536     Simple2List *new2;
1537 
1538     /* Check for illegal characters */
1539     if ((s=strchr(wd, '>')) || (s=strchr(wd, '<'))) {
1540 	if (s-wd == 4 && strncmp(wd, "time", 4) == 0) {
1541 	    return Error(0, 0,
1542 	    "$$\n\tPermittedUser patterns may not use '>' or '<';\n\
1543 \tyou used '%s'; perhaps you meant to write 'time~%s'\n", wd, s);
1544 	} else {
1545 	    return Error(0, 0,
1546 	    "$$\n\tPermittedUser patterns may not use '>' or '<';\n\
1547 \tyou used '%s'.\n", wd);
1548 	}
1549     }
1550     new = (SimpleList *) malloc(sizeof(SimpleList));
1551     if (!new)
1552 	return Error(0, 0, "$$\n\tFailed to malloc space for PermittedUser\n");
1553     new->next = otl->next;
1554     new->pat = (char *) malloc(strlen(wd) + 1);
1555     if (!new->pat)
1556 	return Error(0, 0,
1557 		"$$\n\tFailed to malloc space for PermittedUser pat\n");
1558     strcpy(new->pat, wd);
1559     otl->next = new;
1560 
1561     for (iwd=0; (tok=wdlist[iwd]); iwd++) {
1562 	new2 = (Simple2List *) malloc(sizeof(Simple2List));
1563 	if (!new2)
1564 	    return Error(0, 0,
1565 		"$$\n\tFailed to malloc space for PermittedUser\n");
1566 	new2->next = sl->next;
1567 	new2->other = otl->next;
1568 	new2->pat = (char *) malloc(strlen(tok) + (invert ? 2 : 1));
1569 	if (!new2->pat)
1570 	    return Error(0, 0,
1571 		"$$\n\tFailed to malloc space for PermittedUser pat\n");
1572 	if (invert) {
1573 	    *new2->pat = '!';
1574 	    strcpy(new2->pat+1, tok);
1575 	} else {
1576 	    strcpy(new2->pat, tok);
1577 	}
1578 	sl->next = new2;
1579     }
1580     return 0;
1581 }
1582 
1583 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1584 /* Match a list of user/group/host pattern against the present user. */
1585 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1586 void
match_ugh_user(sl,isglobal)1587 match_ugh_user(sl, isglobal)
1588 Simple2List *sl;/* A list of user pats to match against, starting at sl->next */
1589 int isglobal;	/* !0 means its from a global def */
1590 {
1591     /* Checks if user is matched against each elt in sl.
1592      * Sets matches.match_user if user matches; sets it to 1 if the last
1593      * match is non-inverting, 0 otherwise.
1594 
1595      * BUT! the list created by the InsertUserList function
1596      * is in reverse order, so we only need to find the first
1597      * entry in the list that is a match (+ or -) and stop there!
1598      */
1599 
1600     int invert, match;
1601     int check_ugh P__((char *, char *));
1602 
1603     for (match=0, sl=sl->next; sl && !match; sl=sl->next) {
1604 	invert = *sl->pat == '!';
1605 	if (check_ugh(sl->other->pat, invert ? sl->pat+1 : sl->pat) == 0) {
1606 	    match = 1;
1607 	    matches.user = invert ? 0 : 1;
1608 	    if (debug || it_came_from_cmdline)
1609 		(void) fprintf(stderr,
1610 			"\tPermission %s: %s pattern %suser~%s\n",
1611 			invert ? "denied" : "allowed",
1612 			isglobal ? "global" : "per-cmd",
1613 			invert ? "!" : "", sl->other->pat);
1614 	} else if (debug || it_came_from_cmdline) {
1615 	    (void) fprintf(stderr, "\tNot applicable: %s pattern %suser~%s\n",
1616 			isglobal ? "global" : "per-cmd",
1617 			invert ? "!" : "", sl->other->pat);
1618 	}
1619     }
1620 }
1621 
1622 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1623 /* Check pattern against a hostname.  If the hostname is fully-qualified,
1624  * then try stripping off each of the domains to find a match.
1625  * Return -1 on failure to match; 0 on success.
1626  */
1627 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1628 int
check_host(pat,host)1629 check_host(pat, host)
1630 char *pat, *host;
1631 {
1632     int is_netgroup = 0;
1633     int match;
1634     char *p, *dotp;
1635 
1636     if (*pat == '+') {
1637        is_netgroup = 1;
1638        ++pat;
1639     }
1640 
1641     match = (is_netgroup ? netgrp_h_compare(pat, host)
1642 						: (*pat_compare)(host));
1643 
1644     dotp = strrchr (host, '.');
1645     while (dotp && !match) {
1646        *dotp = 0;
1647        match = (is_netgroup ? netgrp_h_compare(pat, host)
1648 						: (*pat_compare)(host));
1649        p = strrchr (host, '.');
1650        *dotp = '.';
1651        dotp = p;
1652     }
1653     return (match ? 0 : -1);
1654 }
1655 
1656 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1657 /* Try to match a string to a pattern. */
1658 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1659 int
match_pattern(match,do_glob,str,pattern)1660 match_pattern(match, do_glob, str, pattern)
1661 int match;	/* Return input value of match on failure; 1 on success */
1662 int do_glob;	/* 0: no brace globbing;
1663 		 * 1: brace glob;
1664 		 * >1: wrap in braces, then brace-glob.
1665 		 */
1666 char *str;
1667 char *pattern;
1668 {
1669     int i, ipat, wrap;
1670     char *tok, tokbuf[1000];
1671     char **patlist, *pat1list[2];
1672     char chkbuf[1024];
1673 
1674 
1675     if (do_glob != 0) {
1676 	/* Do brace globbing on the pattern */
1677 	wrap = (do_glob > 1) ? 1 : 0;
1678 	if ((i=globbraces(pattern, wrap, &patlist)) != 0) {
1679 	    Error(0, 0, "$$Missing `%c'.\n", i);
1680 	    return match;
1681 	}
1682     } else {
1683 	pat1list[0] = pattern;
1684 	pat1list[1] = NULL;
1685 	patlist = pat1list;
1686     }
1687     if (balancedbraces(pattern) != 0) {
1688 	Error(0, 0, "$$Unbalanced braces in %s.\n", pattern);
1689 	return match;
1690     }
1691 
1692     for (ipat=0; (tok=patlist[ipat]); ipat++) {
1693 	strcpy(tokbuf, tok);
1694 	anchor(tok, chkbuf);			/* Anchor all matches */
1695 	if ((*pat_compile)(chkbuf) != NULL) {
1696 	    Error(0, 0, "$$\n\tBad command pattern: `%s'.\n", pattern);
1697 	    return match;
1698 
1699 	} else if ((*pat_compare)(str) == 1) {
1700 		if (debug)
1701 		    (void) fprintf(stderr,
1702 			"\tMatched user's command=%s to CmdPattern=%s\n",
1703 			str, pattern);
1704 		return 1;
1705 
1706 	} else if (debug) {
1707 	    (void) fprintf(stderr,
1708 		    "\tNo match user's command=%s to CmdPattern=%s\n",
1709 			str, pattern);
1710 	}
1711     }
1712     return match;
1713 }
1714 
1715 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1716 /* Check a single user/group/host string
1717  * Return -1 on failure to match; 0 on success.
1718  */
1719 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1720 int
check_ugh(origtext,token)1721 check_ugh(origtext, token)
1722 char *origtext;		/* original text -- for error messages */
1723 char *token;		/* user/group/host pattern */
1724 {
1725     char chkbuf[1024];
1726     char *userpat, *grouppat, *hostpat;
1727     char *colon;
1728     int match, i;
1729 
1730     if (strlen(token) > sizeof(chkbuf)-4) {
1731 	return Error(0, 1, "$$Can't handle patterns larger than %d chars.\n",
1732 		(int) sizeof(chkbuf)-4);
1733     }
1734 
1735     /* Split into user:group@host; check host part first (if it exists) */
1736     if ((hostpat = strchr(token, '@'))) {
1737 	if (hostpat[1] == 0) {
1738 	    return Error(0, 0,
1739 	    	"$$Missing hostname in pattern `%s'.\n", origtext);
1740 	}
1741 	*hostpat++ = 0;
1742 
1743 	match = -1;
1744 	if (hostpat[0] == '+') {
1745 
1746 #ifdef HAVE_INNETGR
1747 	    if (hostpat[1] == 0) {
1748 		return Error(0, 0,
1749 			"$$Missing netgroupname in pattern `%s'.\n", origtext);
1750 	    }
1751 	    match = check_host(hostpat, userinfo.hostname);
1752 #else
1753 	    return Error(0, 0,
1754 	"$$hostnames may not begin with `+' since this super() was compiled\n\
1755 without -DHAVE_INNETGR.\n");
1756 #endif
1757 	} else {
1758 	    strtolower(hostpat);
1759 	    anchor(hostpat, chkbuf);    /* Force all matches to be anchored */
1760 	    if ((*pat_compile)(chkbuf) != NULL) {
1761 		return Error(0, 0, "$$bad host pattern: `%s'.\n", origtext);
1762 	    }
1763 	}
1764 
1765 	if (match == -1)
1766 	    match = check_host(hostpat, userinfo.lc_hostname);
1767 
1768 	if (debug > 1)
1769 	    fprintf(stderr,
1770 		"\thost pattern <%s> %s user's host <%s>\n",
1771 		hostpat, (match == -1) ? "did not match" : "matched",
1772 		userinfo.lc_hostname);
1773 
1774 	if (match == -1)
1775 	    return -1;
1776     }
1777 
1778     colon = grouppat = strchr(token, ':');
1779     userpat = token;
1780     if (*token == '\0' && !hostpat) {
1781 	/* Nothing in pattern?! */
1782 	return Error(0, 0, "$$\n\tUnacceptable pattern `%s'.\n", origtext);
1783 
1784     } else if (*token == '\0') {
1785 	userpat = grouppat = "^.*$";		/* only hostname given */
1786 
1787     } else if (grouppat && *(grouppat+1)) {	/* pat is "uuu:ggg or ":ggg" */
1788 	if (token == grouppat)
1789 	    userpat = "^.*$";			/* pat is ":ggg" */
1790 	*grouppat++ = '\0';
1791 
1792     } else {					/* pat is "uuu" or "uuu:" */
1793 	if (grouppat)
1794 	    *grouppat = '\0';			/* pat is "uuu:" */
1795 	grouppat = "^.*$";
1796     }
1797     if (strchr(grouppat, ':')) {
1798 	return Error(0, 0, "$$\n\tGroup pattern `%s' contains a colon!\n",
1799 			grouppat);
1800     }
1801 
1802     if (globalinfo.group_slash == 0 && strchr(grouppat, '/')) {
1803 	return Error(0, 0, "$$\n\tFormat error in super.tab file: \
1804 group pattern `%s' contains a slash.\n\
1805 \tPerhaps you meant to use Cmd::Filename, but forgot one colon,\n\
1806 \tso it looks like User:Group?  If you really need to allow\n\
1807 \tslashes in group patterns, use global option group_slash=y.\n", grouppat);
1808     }
1809 
1810 
1811 #ifdef HAVE_INNETGR
1812     if (userpat[0] == '+') {
1813 	if (userpat[1] == 0) {
1814 	    return Error(0, 0,
1815 			"$$Missing netgroupname in pattern `%s'.\n", origtext);
1816 	}
1817 	match = netgrp_u_compare(&userpat[1], userinfo.caller.pw_name);
1818     } else
1819 #endif
1820     {
1821 	anchor(userpat, chkbuf);		/* Anchor all matches */
1822 	if ((*pat_compile)(chkbuf) != NULL) {
1823 	    return Error(0, 0, "$$\n\tbad user pattern: `%s'.\n", origtext);
1824 	}
1825 	match = (*pat_compare)(userinfo.caller.pw_name);
1826 #ifdef MATCH_DECIMAL_UID
1827 	if (match != 1) {
1828 	    /* Enabling MATCH_DECIMAL_UID allows the userpat to be
1829 	     * numeric, as an alternative to being interpreted as a
1830 	     * user name: after checking the username, we check if the
1831 	     * user's uid, as a decimal text value, matches the user
1832 	     * pattern userpat.
1833 	     */
1834 	    char buf[20];
1835 	    (void) sprintf(buf, "%d", userinfo.caller.pw_uid);
1836 	    match = (*pat_compare)(buf);
1837 	}
1838 #endif
1839     }
1840     if (debug > 1)
1841 	fprintf(stderr,
1842 	    "\tuser pattern <%s> %s username <%s>\n",
1843 	    userpat, (match != 1) ? "did not match" : "matched",
1844 	    userinfo.caller.pw_name);
1845 
1846     if (match != 1)
1847 	return -1;
1848 
1849     anchor(grouppat, chkbuf);
1850     i = ingroup(userinfo.caller.pw_name, userinfo.caller.pw_gid, chkbuf);
1851     if (i == -1)
1852 	return Error(0, 0, "$$\n\tbad group pattern <%s>\n", origtext);
1853 
1854     if (debug > 1)
1855 	fprintf(stderr,
1856 	    "\tuser <%s> is %sa member of group <%s>\n",
1857 	    userinfo.caller.pw_name, (i != 1) ? "not " : "", grouppat);
1858 
1859     if (i != 1)
1860 	return -1;
1861 
1862     return 0;				/* Success! */
1863 }
1864 
1865 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1866 /* Determines if user's group matches a group pattern. */
1867 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1868 
1869 static int created_group_table = 0;
1870 
1871 int
ingroup(user,gid,gp_pat)1872 ingroup(user, gid, gp_pat)
1873 char *user;
1874 gid_t gid;
1875 char *gp_pat;	/* pattern to match */
1876 {
1877     /* Use:
1878      *	ingroup(user, gid, gp_pat)
1879      * Returns:
1880      *	1 if the user is in a group matching the regex pattern gp_pat.
1881      *	0 if the user isn't in a group matching the pattern.
1882      *	-1 if pattern failed to compile.
1883 
1884      * SIDE-EFFECT: uses pat_compile/pat_compare!
1885      *			-- messes up caller's use of same!
1886 
1887      * Examples:
1888      *	ingroup("joe", joes_gid, "xyz")
1889      * returns !0 if user joe is in group "xyz".
1890      *	ingroup("joe", joes_gid, "xy.*")
1891      * returns !0 if user joe is in any group matching "xy.*".
1892 
1893      */
1894 
1895     struct group *gp;
1896     char **mem;
1897     char buf[20];
1898     void endgrent();
1899 
1900     ENTRY item, *found_item;
1901     int passed, klen;
1902 
1903     if ((*pat_compile)(gp_pat) != (char *)0 )
1904 	return -1;
1905 
1906     if (!created_group_table) {
1907         if (!s_hcreate(HS_GROUP, 1000)) {
1908 	    return Error(0, 0,
1909 		"$$Couldn't allocate hash table for group processing\n");
1910 	}
1911         created_group_table = 1;
1912     }
1913     /* Catenate user, gid and gp_pat for the key such that
1914      * "abc" + 10 + "de" is not the same as "ab" + 1 + "0cde".
1915      * Use "\n", as that will have been stripped from input before we get here.
1916      */
1917     if (snprintf(keybuf, MAX_KEYLEN, "%s\n%d\n%s", user, gid, gp_pat)
1918              >= MAX_KEYLEN) {
1919          return Error(0, 0,
1920              "$$user/gid/gp_pat too large for user ng processing\n");
1921     }
1922 
1923     item.key = keybuf;
1924     if (found_item = s_hsearch(HS_GROUP, item, FIND)) {
1925         return found_item->data == YES? 1: 0;
1926     }
1927 
1928     passed = 0;     /* Assume the worst */
1929 
1930     /* Search group file for groups user is in.  For each group of which
1931      * the user is a member, test a match to the pattern.
1932      */
1933     setgrent();
1934     for (gp = getgrent(); gp; gp = getgrent()) {
1935 	/* The gr_mem list only shows usernames added in the /etc/group file,
1936 	 * and not any users assigned to the group in the passwd file.
1937 	 * Thus discover group membership by first checking the user's
1938 	 * group from the password file (gp->gr_gid) against this group's
1939 	 * gid, then check to see if this user is in the gp->gr_mem list.
1940 	 */
1941 	if (gid != gp->gr_gid) {
1942 	    for (mem = gp->gr_mem; *mem ; mem++)
1943 		if (strcmp(*mem, user) == 0)
1944 		    break;
1945 	    if (!*mem)
1946 		continue;			/* not in group */
1947 	}
1948 	/* if here, the user is in group gp; now check if group
1949 	 * name gp->gr_name matches group pattern gp_pat.
1950 	 */
1951 	if ((*pat_compare)(gp->gr_name) == 1) {
1952 	    /* successful match -- user is in a group that matches gp_pat */
1953 	    endgrent();
1954 	    /* return 1; */
1955             passed = 1;
1956             goto found_it;
1957 	}
1958 #ifdef MATCH_DECIMAL_GID
1959 	else {
1960 	    /* Enabling MATCH_DECIMAL_GID allows the gp_pat to be
1961 	     * numeric, as an alternative to being interpreted as a
1962 	     * group name: we check if the group id gp->gr_gid, as a
1963 	     * decimal text value, matches the group pattern gp_pat.
1964 	     */
1965 	    (void) sprintf(buf, "%d", gp->gr_gid);
1966 	    if ((*pat_compare)(buf) == 1){
1967 		/* successful match -- user is in a group that matches gp_pat */
1968 		endgrent();
1969 		/* return 1; */
1970                 passed = 1;
1971                 goto found_it;
1972 	    }
1973 	}
1974 #endif
1975     }
1976 
1977 #ifdef MATCH_DECIMAL_GID
1978     /* We haven't found any group from /etc/group to which we belong that
1979      * matches the pattern.  It is possible that the user's group id from the
1980      * password file isn't in the /etc/group file at all, in which case the
1981      * user's group won't have matched the pattern since we've only checked
1982      * /etc/group entries so far.  Now check the numeric id from the
1983      * /etc/passwd file against the pattern.
1984      */
1985     (void) sprintf(buf, "%d", gid);
1986     if ((*pat_compare)(buf) == 1){
1987 	endgrent();
1988 	/* return 1; */
1989         passed = 1;
1990         goto found_it;
1991     }
1992 #endif
1993 
1994     endgrent();
1995     /* return 0; */
1996 
1997 found_it:
1998 
1999     /* Have to make this key a permanent copy for the hash */
2000 
2001     klen = strlen(keybuf);
2002     item.key = malloc(klen + 1);
2003     if (!item.key) {
2004         return Error(0, 0,
2005            "$$out of memory for group processing\n");
2006     }
2007     strcpy(item.key, keybuf);
2008     item.data = passed? YES: NO;
2009     if (!s_hsearch(HS_GROUP, item, ENTER)) {
2010         return Error(0, 0,
2011            "$$hash set failure for group processing\n");
2012     }
2013     return passed;
2014 }
2015 
2016