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