1 /*
2 * $LynxId: HTAAProt.c,v 1.32 2010/04/29 09:30:57 tom Exp $
3 *
4 * MODULE HTAAProt.c
5 * PROTECTION FILE PARSING MODULE
6 *
7 * AUTHORS:
8 * AL Ari Luotonen luotonen@dxcern.cern.ch
9 * MD Mark Donszelmann duns@vxdeop.cern.ch
10 *
11 * HISTORY:
12 * 20 Oct 93 AL Now finds uid/gid for nobody/nogroup by name
13 * (doesn't use default 65534 right away).
14 * Also understands negative uids/gids.
15 * 14 Nov 93 MD Added VMS compatibility
16 *
17 * BUGS:
18 *
19 *
20 */
21
22 #include <HTUtils.h>
23
24 #ifndef VMS
25 #ifndef NOUSERS
26 #include <pwd.h> /* Unix password file routine: getpwnam() */
27 #include <grp.h> /* Unix group file routine: getgrnam() */
28 #endif /* NOUSERS */
29 #endif /* not VMS */
30
31 #include <HTAAUtil.h>
32 #include <HTLex.h> /* Lexical analysor */
33 #include <HTAAProt.h> /* Implemented here */
34
35 #include <LYUtils.h>
36 #include <LYLeaks.h>
37
38 #define NOBODY 65534 /* -2 in 16-bit environment */
39 #define NONESUCH 65533 /* -3 in 16-bit environment */
40
41 /*
42 * Protection setup caching
43 */
44 typedef struct {
45 char *prot_filename;
46 HTAAProt *prot;
47 } HTAAProtCache;
48
49 static HTList *prot_cache = NULL; /* Protection setup cache. */
50 static HTAAProt *default_prot = NULL; /* Default protection. */
51 static HTAAProt *current_prot = NULL; /* Current protection mode */
52
53 /* which is set up by callbacks */
54 /* from the rule system when */
55 /* a "protect" rule is matched. */
56
57 #ifndef NOUSERS
58 /* static isNumber()
59 * DOES A CHARACTER STRING REPRESENT A NUMBER
60 */
isNumber(const char * s)61 static BOOL isNumber(const char *s)
62 {
63 const char *cur = s;
64
65 if (isEmpty(s))
66 return NO;
67
68 if (*cur == '-')
69 cur++; /* Allow initial minus sign in a number */
70
71 while (*cur) {
72 if (*cur < '0' || *cur > '9')
73 return NO;
74 cur++;
75 }
76 return YES;
77 }
78
79 /* PUBLIC HTAA_getUid()
80 * GET THE USER ID TO CHANGE THE PROCESS UID TO
81 * ON ENTRY:
82 * No arguments.
83 *
84 * ON EXIT:
85 * returns the uid number to give to setuid() system call.
86 * Default is 65534 (nobody).
87 */
HTAA_getUid(void)88 int HTAA_getUid(void)
89 {
90 int uid;
91
92 if (current_prot && current_prot->uid_name) {
93 if (isNumber(current_prot->uid_name)) {
94 uid = atoi(current_prot->uid_name);
95 if ((*HTAA_UidToName(uid)) != '\0') {
96 return uid;
97 }
98 } else { /* User name (not a number) */
99 if ((uid = HTAA_NameToUid(current_prot->uid_name)) != NONESUCH) {
100 return uid;
101 }
102 }
103 }
104 /*
105 * Ok, then let's get uid for nobody.
106 */
107 if ((uid = HTAA_NameToUid("nobody")) != NONESUCH) {
108 return uid;
109 }
110 /*
111 * Ok, then use default.
112 */
113 return NOBODY; /* nobody */
114 }
115
116 /* PUBLIC HTAA_getGid()
117 * GET THE GROUP ID TO CHANGE THE PROCESS GID TO
118 * ON ENTRY:
119 * No arguments.
120 *
121 * ON EXIT:
122 * returns the uid number to give to setgid() system call.
123 * Default is 65534 (nogroup).
124 */
HTAA_getGid(void)125 int HTAA_getGid(void)
126 {
127 int gid;
128
129 if (current_prot && current_prot->gid_name) {
130 if (isNumber(current_prot->gid_name)) {
131 gid = atoi(current_prot->gid_name);
132 if (*HTAA_GidToName(gid) != '\0') {
133 return gid;
134 }
135 } else { /* Group name (not number) */
136 if ((gid = HTAA_NameToGid(current_prot->gid_name)) != NONESUCH) {
137 return gid;
138 }
139 }
140 }
141 /*
142 * Ok, then let's get gid for nogroup.
143 */
144 if ((gid = HTAA_NameToGid("nogroup")) != NONESUCH) {
145 return gid;
146 }
147 /*
148 * Ok, then use default.
149 */
150 return NOBODY; /* nogroup */
151 }
152 #endif /* !NOUSERS */
153
154 /* static HTAA_setIds()
155 * SET UID AND GID (AS NAMES OR NUMBERS)
156 * TO HTAAProt STRUCTURE
157 * ON ENTRY:
158 * prot destination.
159 * ids is a string like "james.www" or "1422.69" etc.
160 * giving uid and gid.
161 *
162 * ON EXIT:
163 * returns nothing.
164 */
HTAA_setIds(HTAAProt * prot,const char * ids)165 static void HTAA_setIds(HTAAProt *prot, const char *ids)
166 {
167 if (ids) {
168 char *local_copy = NULL;
169 char *point;
170
171 StrAllocCopy(local_copy, ids);
172 point = strchr(local_copy, '.');
173 if (point) {
174 *(point++) = (char) 0;
175 StrAllocCopy(prot->gid_name, point);
176 } else {
177 StrAllocCopy(prot->gid_name, "nogroup");
178 }
179 StrAllocCopy(prot->uid_name, local_copy);
180 FREE(local_copy);
181 } else {
182 StrAllocCopy(prot->uid_name, "nobody");
183 StrAllocCopy(prot->gid_name, "nogroup");
184 }
185 }
186
187 /* static HTAA_parseProtFile()
188 * PARSE A PROTECTION SETUP FILE AND
189 * PUT THE RESULT IN A HTAAProt STRUCTURE
190 * ON ENTRY:
191 * prot destination structure.
192 * fp open protection file.
193 *
194 * ON EXIT:
195 * returns nothing.
196 */
HTAA_parseProtFile(HTAAProt * prot,FILE * fp)197 static void HTAA_parseProtFile(HTAAProt *prot, FILE *fp)
198 {
199 if (prot && fp) {
200 LexItem lex_item;
201 char *fieldname = NULL;
202
203 while (LEX_EOF != (lex_item = lex(fp))) {
204
205 while (lex_item == LEX_REC_SEP) /* Ignore empty lines */
206 lex_item = lex(fp);
207
208 if (lex_item == LEX_EOF) /* End of file */
209 break;
210
211 if (lex_item == LEX_ALPH_STR) { /* Valid setup record */
212
213 StrAllocCopy(fieldname, HTlex_buffer);
214
215 if (LEX_FIELD_SEP != (lex_item = lex(fp)))
216 unlex(lex_item); /* If someone wants to use colon */
217 /* after field name it's ok, but */
218 /* not required. Here we read it. */
219
220 if (0 == strncasecomp(fieldname, "Auth", 4)) {
221 lex_item = lex(fp);
222 while (lex_item == LEX_ALPH_STR) {
223 HTAAScheme scheme = HTAAScheme_enum(HTlex_buffer);
224
225 if (scheme != HTAA_UNKNOWN) {
226 if (!prot->valid_schemes)
227 prot->valid_schemes = HTList_new();
228 HTList_addObject(prot->valid_schemes, (void *) scheme);
229 CTRACE((tfp, "%s %s `%s'\n",
230 "HTAA_parseProtFile: valid",
231 "authentication scheme:",
232 HTAAScheme_name(scheme)));
233 } else {
234 CTRACE((tfp, "%s %s `%s'\n",
235 "HTAA_parseProtFile: unknown",
236 "authentication scheme:",
237 HTlex_buffer));
238 }
239
240 if (LEX_ITEM_SEP != (lex_item = lex(fp)))
241 break;
242 /*
243 * Here lex_item == LEX_ITEM_SEP; after item separator
244 * it is ok to have one or more newlines (LEX_REC_SEP)
245 * and they are ignored (continuation line).
246 */
247 do {
248 lex_item = lex(fp);
249 } while (lex_item == LEX_REC_SEP);
250 } /* while items in list */
251 }
252 /* if "Authenticate" */
253 else if (0 == strncasecomp(fieldname, "mask", 4)) {
254 prot->mask_group = HTAA_parseGroupDef(fp);
255 lex_item = LEX_REC_SEP; /*groupdef parser read this already */
256 if (TRACE) {
257 if (prot->mask_group) {
258 fprintf(tfp,
259 "HTAA_parseProtFile: Mask group:\n");
260 HTAA_printGroupDef(prot->mask_group);
261 } else
262 fprintf(tfp,
263 "HTAA_parseProtFile: Mask group syntax error\n");
264 }
265 }
266 /* if "Mask" */
267 else { /* Just a name-value pair, put it to assoclist */
268
269 if (LEX_ALPH_STR == (lex_item = lex(fp))) {
270 if (!prot->values)
271 prot->values = HTAssocList_new();
272 HTAssocList_add(prot->values, fieldname, HTlex_buffer);
273 lex_item = lex(fp); /* Read record separator */
274 CTRACE((tfp, "%s `%s' bound to value `%s'\n",
275 "HTAA_parseProtFile: Name",
276 fieldname, HTlex_buffer));
277 }
278 } /* else name-value pair */
279
280 }
281 /* if valid field */
282 if (lex_item != LEX_EOF && lex_item != LEX_REC_SEP) {
283 CTRACE((tfp, "%s %s %d (that line ignored)\n",
284 "HTAA_parseProtFile: Syntax error",
285 "in protection setup file at line",
286 HTlex_line));
287 do {
288 lex_item = lex(fp);
289 } while (lex_item != LEX_EOF && lex_item != LEX_REC_SEP);
290 } /* if syntax error */
291 } /* while not end-of-file */
292 FREE(fieldname);
293 } /* if valid parameters */
294 }
295
296 /* static HTAAProt_new()
297 * ALLOCATE A NEW HTAAProt STRUCTURE AND
298 * INITIALIZE IT FROM PROTECTION SETUP FILE
299 * ON ENTRY:
300 * cur_docname current filename after rule translations.
301 * prot_filename protection setup file name.
302 * If NULL, not an error.
303 * ids Uid and gid names or numbers,
304 * examples:
305 * james ( <=> james.nogroup)
306 * .www ( <=> nobody.www)
307 * james.www
308 * james.69
309 * 1422.69
310 * 1422.www
311 *
312 * May be NULL, defaults to nobody.nogroup.
313 * Should be NULL, if prot_file is NULL.
314 *
315 * ON EXIT:
316 * returns returns a new and initialized protection
317 * setup structure.
318 * If setup file is already read in (found
319 * in cache), only sets uid_name and gid
320 * fields, and returns that.
321 */
HTAAProt_new(const char * cur_docname,const char * prot_filename,const char * ids)322 static HTAAProt *HTAAProt_new(const char *cur_docname,
323 const char *prot_filename,
324 const char *ids)
325 {
326 HTList *cur = prot_cache;
327 HTAAProtCache *cache_item = NULL;
328 HTAAProt *prot;
329 FILE *fp;
330
331 if (!prot_cache)
332 prot_cache = HTList_new();
333
334 while (NULL != (cache_item = (HTAAProtCache *) HTList_nextObject(cur))) {
335 if (!strcmp(cache_item->prot_filename, prot_filename))
336 break;
337 }
338 if (cache_item) {
339 prot = cache_item->prot;
340 CTRACE((tfp, "%s `%s' already in cache\n",
341 "HTAAProt_new: Protection file", prot_filename));
342 } else {
343 CTRACE((tfp, "HTAAProt_new: Loading protection file `%s'\n",
344 prot_filename));
345
346 if ((prot = typecalloc(HTAAProt)) == 0)
347 outofmem(__FILE__, "HTAAProt_new");
348
349 assert(prot != NULL);
350
351 prot->ctemplate = NULL;
352 prot->filename = NULL;
353 prot->uid_name = NULL;
354 prot->gid_name = NULL;
355 prot->valid_schemes = HTList_new();
356 prot->mask_group = NULL; /* Masking disabled by defaults */
357 prot->values = HTAssocList_new();
358
359 if (prot_filename && NULL != (fp = fopen(prot_filename, TXT_R))) {
360 HTAA_parseProtFile(prot, fp);
361 fclose(fp);
362 if ((cache_item = typecalloc(HTAAProtCache)) == 0)
363 outofmem(__FILE__, "HTAAProt_new");
364
365 assert(cache_item != NULL);
366
367 cache_item->prot = prot;
368 cache_item->prot_filename = NULL;
369 StrAllocCopy(cache_item->prot_filename, prot_filename);
370 HTList_addObject(prot_cache, (void *) cache_item);
371 } else {
372 CTRACE((tfp, "HTAAProt_new: %s `%s'\n",
373 "Unable to open protection setup file",
374 NONNULL(prot_filename)));
375 }
376 }
377
378 if (cur_docname)
379 StrAllocCopy(prot->filename, cur_docname);
380 HTAA_setIds(prot, ids);
381
382 return prot;
383 }
384
385 /* PUBLIC HTAA_setDefaultProtection()
386 * SET THE DEFAULT PROTECTION MODE
387 * (called by rule system when a
388 * "defprot" rule is matched)
389 * ON ENTRY:
390 * cur_docname is the current result of rule translations.
391 * prot_filename is the protection setup file (second argument
392 * for "defprot" rule, optional)
393 * ids contains user and group names separated by
394 * a dot, corresponding to the uid
395 * gid under which the server should run,
396 * default is "nobody.nogroup" (third argument
397 * for "defprot" rule, optional; can be given
398 * only if protection setup file is also given).
399 *
400 * ON EXIT:
401 * returns nothing.
402 * Sets the module-wide variable default_prot.
403 */
HTAA_setDefaultProtection(const char * cur_docname,const char * prot_filename,const char * ids)404 void HTAA_setDefaultProtection(const char *cur_docname,
405 const char *prot_filename,
406 const char *ids)
407 {
408 default_prot = NULL; /* Not free()'d because this is in cache */
409
410 if (prot_filename) {
411 default_prot = HTAAProt_new(cur_docname, prot_filename, ids);
412 } else {
413 CTRACE((tfp, "%s %s\n",
414 "HTAA_setDefaultProtection: ERROR: Protection file",
415 "not specified (obligatory for DefProt rule)!!\n"));
416 }
417 }
418
419 /* PUBLIC HTAA_setCurrentProtection()
420 * SET THE CURRENT PROTECTION MODE
421 * (called by rule system when a
422 * "protect" rule is matched)
423 * ON ENTRY:
424 * cur_docname is the current result of rule translations.
425 * prot_filename is the protection setup file (second argument
426 * for "protect" rule, optional)
427 * ids contains user and group names separated by
428 * a dot, corresponding to the uid
429 * gid under which the server should run,
430 * default is "nobody.nogroup" (third argument
431 * for "protect" rule, optional; can be given
432 * only if protection setup file is also given).
433 *
434 * ON EXIT:
435 * returns nothing.
436 * Sets the module-wide variable current_prot.
437 */
HTAA_setCurrentProtection(const char * cur_docname,const char * prot_filename,const char * ids)438 void HTAA_setCurrentProtection(const char *cur_docname,
439 const char *prot_filename,
440 const char *ids)
441 {
442 current_prot = NULL; /* Not free()'d because this is in cache */
443
444 if (prot_filename) {
445 current_prot = HTAAProt_new(cur_docname, prot_filename, ids);
446 } else {
447 if (default_prot) {
448 current_prot = default_prot;
449 HTAA_setIds(current_prot, ids);
450 CTRACE((tfp, "%s %s %s\n",
451 "HTAA_setCurrentProtection: Protection file",
452 "not specified for Protect rule",
453 "-- using default protection"));
454 } else {
455 CTRACE((tfp, "%s %s %s\n",
456 "HTAA_setCurrentProtection: ERROR: Protection",
457 "file not specified for Protect rule, and",
458 "default protection is not set!!"));
459 }
460 }
461 }
462
463 /* PUBLIC HTAA_getCurrentProtection()
464 * GET CURRENT PROTECTION SETUP STRUCTURE
465 * (this is set up by callbacks made from
466 * the rule system when matching "protect"
467 * (and "defprot") rules)
468 * ON ENTRY:
469 * HTTranslate() must have been called before calling
470 * this function.
471 *
472 * ON EXIT:
473 * returns a HTAAProt structure representing the
474 * protection setup of the HTTranslate()'d file.
475 * This must not be free()'d.
476 */
HTAA_getCurrentProtection(void)477 HTAAProt *HTAA_getCurrentProtection(void)
478 {
479 return current_prot;
480 }
481
482 /* PUBLIC HTAA_getDefaultProtection()
483 * GET DEFAULT PROTECTION SETUP STRUCTURE
484 * AND SET IT TO CURRENT PROTECTION
485 * (this is set up by callbacks made from
486 * the rule system when matching "defprot"
487 * rules)
488 * ON ENTRY:
489 * HTTranslate() must have been called before calling
490 * this function.
491 *
492 * ON EXIT:
493 * returns a HTAAProt structure representing the
494 * default protection setup of the HTTranslate()'d
495 * file (if HTAA_getCurrentProtection() returned
496 * NULL, i.e., if there is no "protect" rule
497 * but ACL exists, and we need to know default
498 * protection settings).
499 * This must not be free()'d.
500 * IMPORTANT:
501 * As a side-effect this tells the protection system that
502 * the file is in fact protected and sets the current
503 * protection mode to default.
504 */
HTAA_getDefaultProtection(void)505 HTAAProt *HTAA_getDefaultProtection(void)
506 {
507 if (!current_prot) {
508 current_prot = default_prot;
509 default_prot = NULL;
510 }
511 return current_prot;
512 }
513
514 /* SERVER INTERNAL HTAA_clearProtections()
515 * CLEAR DOCUMENT PROTECTION MODE
516 * (ALSO DEFAULT PROTECTION)
517 * (called by the rule system)
518 * ON ENTRY:
519 * No arguments.
520 *
521 * ON EXIT:
522 * returns nothing.
523 * Frees the memory used by protection information.
524 */
HTAA_clearProtections(void)525 void HTAA_clearProtections(void)
526 {
527 current_prot = NULL; /* These are not freed because */
528 default_prot = NULL; /* they are actually in cache. */
529 }
530
531 typedef struct {
532 char *name;
533 int user;
534 } USER_DATA;
535
536 #ifndef NOUSERS
537 static HTList *known_grp = NULL;
538 static HTList *known_pwd = NULL;
539 static BOOL uidgid_cache_inited = NO;
540 #endif
541
542 #ifdef LY_FIND_LEAKS
clear_uidgid_cache(void)543 static void clear_uidgid_cache(void)
544 {
545 #ifndef NOUSERS
546 USER_DATA *data;
547
548 if (known_grp) {
549 while ((data = HTList_removeLastObject(known_grp)) != NULL) {
550 FREE(data->name);
551 FREE(data);
552 }
553 FREE(known_grp);
554 }
555 if (known_pwd) {
556 while ((data = HTList_removeLastObject(known_pwd)) != NULL) {
557 FREE(data->name);
558 FREE(data);
559 }
560 FREE(known_pwd);
561 }
562 #endif
563 }
564 #endif /* LY_FIND_LEAKS */
565
566 #ifndef NOUSERS
save_gid_info(const char * name,int user)567 static void save_gid_info(const char *name, int user)
568 {
569 USER_DATA *data = typecalloc(USER_DATA);
570
571 if (!data)
572 return;
573 if (!known_grp) {
574 known_grp = HTList_new();
575 if (!uidgid_cache_inited) {
576 #ifdef LY_FIND_LEAKS
577 atexit(clear_uidgid_cache);
578 #endif
579 uidgid_cache_inited = YES;
580 }
581 }
582 StrAllocCopy(data->name, name);
583 data->user = user;
584 HTList_addObject(known_grp, data);
585 }
586 #endif /* NOUSERS */
587
588 #ifndef NOUSERS
save_uid_info(const char * name,int user)589 static void save_uid_info(const char *name, int user)
590 {
591 USER_DATA *data = typecalloc(USER_DATA);
592
593 if (!data)
594 return;
595 if (!known_pwd) {
596 known_pwd = HTList_new();
597 if (!uidgid_cache_inited) {
598 #ifdef LY_FIND_LEAKS
599 atexit(clear_uidgid_cache);
600 #endif
601 uidgid_cache_inited = YES;
602 }
603 }
604 StrAllocCopy(data->name, name);
605 data->user = user;
606 HTList_addObject(known_pwd, data);
607 }
608 #endif /* !NOUSERS */
609
610 /* PUBLIC HTAA_UidToName
611 * GET THE USER NAME
612 * ON ENTRY:
613 * The user-id
614 *
615 * ON EXIT:
616 * returns the user name, or an empty string if not found.
617 */
HTAA_UidToName(int uid GCC_UNUSED)618 const char *HTAA_UidToName(int uid GCC_UNUSED)
619 {
620 #ifndef NOUSERS
621 struct passwd *pw;
622 HTList *me = known_pwd;
623
624 while (HTList_nextObject(me)) {
625 USER_DATA *data = (USER_DATA *) (me->object);
626
627 if (uid == data->user)
628 return data->name;
629 }
630
631 if ((pw = getpwuid((uid_t) uid)) != 0
632 && pw->pw_name != 0) {
633 CTRACE((tfp, "%s(%d) returned (%s:%d:...)\n",
634 "HTAA_UidToName: getpwuid",
635 uid,
636 pw->pw_name, (int) pw->pw_uid));
637 save_uid_info(pw->pw_name, (int) pw->pw_uid);
638 return pw->pw_name;
639 }
640 #endif
641 return "";
642 }
643
644 /* PUBLIC HTAA_NameToUid
645 * GET THE USER ID
646 * ON ENTRY:
647 * The user-name
648 *
649 * ON EXIT:
650 * returns the user id, or NONESUCH if not found.
651 */
HTAA_NameToUid(const char * name GCC_UNUSED)652 int HTAA_NameToUid(const char *name GCC_UNUSED)
653 {
654 #ifndef NOUSERS
655 struct passwd *pw;
656 HTList *me = known_pwd;
657
658 while (HTList_nextObject(me)) {
659 USER_DATA *data = (USER_DATA *) (me->object);
660
661 if (!strcmp(name, data->name))
662 return data->user;
663 }
664
665 if ((pw = getpwnam(name)) != 0) {
666 CTRACE((tfp, "%s(%s) returned (%s:%d:...)\n",
667 "HTAA_NameToUid: getpwnam",
668 name,
669 pw->pw_name, (int) pw->pw_uid));
670 save_uid_info(pw->pw_name, (int) pw->pw_uid);
671 return (int) pw->pw_uid;
672 }
673 #endif
674 return NONESUCH;
675 }
676
677 /* PUBLIC HTAA_GidToName
678 * GET THE GROUP NAME
679 * ON ENTRY:
680 * The group-id
681 *
682 * ON EXIT:
683 * returns the group name, or an empty string if not found.
684 */
HTAA_GidToName(int gid GCC_UNUSED)685 const char *HTAA_GidToName(int gid GCC_UNUSED)
686 {
687 #ifndef NOUSERS
688 struct group *gr;
689 HTList *me = known_grp;
690
691 while (HTList_nextObject(me)) {
692 USER_DATA *data = (USER_DATA *) (me->object);
693
694 if (gid == data->user)
695 return data->name;
696 }
697
698 if ((gr = getgrgid((gid_t) gid)) != 0
699 && gr->gr_name != 0) {
700 CTRACE((tfp, "%s(%d) returned (%s:%d:...)\n",
701 "HTAA_GidToName: getgrgid",
702 gid,
703 gr->gr_name, (int) gr->gr_gid));
704 save_gid_info(gr->gr_name, (int) gr->gr_gid);
705 return gr->gr_name;
706 }
707 #endif
708 return "";
709 }
710
711 /* PUBLIC HTAA_NameToGid
712 * GET THE GROUP ID
713 * ON ENTRY:
714 * The group-name
715 *
716 * ON EXIT:
717 * returns the group id, or NONESUCH if not found.
718 */
HTAA_NameToGid(const char * name GCC_UNUSED)719 int HTAA_NameToGid(const char *name GCC_UNUSED)
720 {
721 #ifndef NOUSERS
722 struct group *gr;
723 HTList *me = known_grp;
724
725 while (HTList_nextObject(me)) {
726 USER_DATA *data = (USER_DATA *) (me->object);
727
728 if (!strcmp(name, data->name))
729 return data->user;
730 }
731
732 if ((gr = getgrnam(name)) != 0) {
733 CTRACE((tfp, "%s(%s) returned (%s:%d:...)\n",
734 "HTAA_NameToGid: getgrnam",
735 name,
736 gr->gr_name, (int) gr->gr_gid));
737 save_gid_info(gr->gr_name, (int) gr->gr_gid);
738 return (int) gr->gr_gid;
739 }
740 #endif
741 return NONESUCH;
742 }
743