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