about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation/HTAAProt.c
diff options
context:
space:
mode:
Diffstat (limited to 'WWW/Library/Implementation/HTAAProt.c')
-rw-r--r--WWW/Library/Implementation/HTAAProt.c742
1 files changed, 742 insertions, 0 deletions
diff --git a/WWW/Library/Implementation/HTAAProt.c b/WWW/Library/Implementation/HTAAProt.c
new file mode 100644
index 00000000..45f654a3
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAProt.c
@@ -0,0 +1,742 @@
+/*
+ * $LynxId: HTAAProt.c,v 1.32 2010/04/29 09:30:57 tom Exp $
+ *
+ * MODULE							HTAAProt.c
+ *		PROTECTION FILE PARSING MODULE
+ *
+ * AUTHORS:
+ *	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+ *	MD	Mark Donszelmann    duns@vxdeop.cern.ch
+ *
+ * HISTORY:
+ *	20 Oct 93  AL	Now finds uid/gid for nobody/nogroup by name
+ *			(doesn't use default 65534 right away).
+ *			Also understands negative uids/gids.
+ *	14 Nov 93  MD	Added VMS compatibility
+ *
+ * BUGS:
+ *
+ *
+ */
+
+#include <HTUtils.h>
+
+#ifndef VMS
+#ifndef NOUSERS
+#include <pwd.h>		/* Unix password file routine: getpwnam()       */
+#include <grp.h>		/* Unix group file routine: getgrnam()          */
+#endif /* NOUSERS */
+#endif /* not VMS */
+
+#include <HTAAUtil.h>
+#include <HTLex.h>		/* Lexical analysor     */
+#include <HTAAProt.h>		/* Implemented here     */
+
+#include <LYUtils.h>
+#include <LYLeaks.h>
+
+#define NOBODY    65534		/* -2 in 16-bit environment */
+#define NONESUCH  65533		/* -3 in 16-bit environment */
+
+/*
+ * Protection setup caching
+ */
+typedef struct {
+    char *prot_filename;
+    HTAAProt *prot;
+} HTAAProtCache;
+
+static HTList *prot_cache = NULL;	/* Protection setup cache.      */
+static HTAAProt *default_prot = NULL;	/* Default protection.          */
+static HTAAProt *current_prot = NULL;	/* Current protection mode      */
+
+					/* which is set up by callbacks */
+					/* from the rule system when    */
+					/* a "protect" rule is matched. */
+
+#ifndef NOUSERS
+/* static							isNumber()
+ *		DOES A CHARACTER STRING REPRESENT A NUMBER
+ */
+static BOOL isNumber(const char *s)
+{
+    const char *cur = s;
+
+    if (isEmpty(s))
+	return NO;
+
+    if (*cur == '-')
+	cur++;			/* Allow initial minus sign in a number */
+
+    while (*cur) {
+	if (*cur < '0' || *cur > '9')
+	    return NO;
+	cur++;
+    }
+    return YES;
+}
+
+/* PUBLIC							HTAA_getUid()
+ *		GET THE USER ID TO CHANGE THE PROCESS UID TO
+ * ON ENTRY:
+ *	No arguments.
+ *
+ * ON EXIT:
+ *	returns	the uid number to give to setuid() system call.
+ *		Default is 65534 (nobody).
+ */
+int HTAA_getUid(void)
+{
+    int uid;
+
+    if (current_prot && current_prot->uid_name) {
+	if (isNumber(current_prot->uid_name)) {
+	    uid = atoi(current_prot->uid_name);
+	    if ((*HTAA_UidToName(uid)) != '\0') {
+		return uid;
+	    }
+	} else {		/* User name (not a number) */
+	    if ((uid = HTAA_NameToUid(current_prot->uid_name)) != NONESUCH) {
+		return uid;
+	    }
+	}
+    }
+    /*
+     * Ok, then let's get uid for nobody.
+     */
+    if ((uid = HTAA_NameToUid("nobody")) != NONESUCH) {
+	return uid;
+    }
+    /*
+     * Ok, then use default.
+     */
+    return NOBODY;		/* nobody */
+}
+
+/* PUBLIC							HTAA_getGid()
+ *		GET THE GROUP ID TO CHANGE THE PROCESS GID TO
+ * ON ENTRY:
+ *	No arguments.
+ *
+ * ON EXIT:
+ *	returns	the uid number to give to setgid() system call.
+ *		Default is 65534 (nogroup).
+ */
+int HTAA_getGid(void)
+{
+    int gid;
+
+    if (current_prot && current_prot->gid_name) {
+	if (isNumber(current_prot->gid_name)) {
+	    gid = atoi(current_prot->gid_name);
+	    if (*HTAA_GidToName(gid) != '\0') {
+		return gid;
+	    }
+	} else {		/* Group name (not number) */
+	    if ((gid = HTAA_NameToGid(current_prot->gid_name)) != NONESUCH) {
+		return gid;
+	    }
+	}
+    }
+    /*
+     * Ok, then let's get gid for nogroup.
+     */
+    if ((gid = HTAA_NameToGid("nogroup")) != NONESUCH) {
+	return gid;
+    }
+    /*
+     * Ok, then use default.
+     */
+    return NOBODY;		/* nogroup */
+}
+#endif /* !NOUSERS */
+
+/* static							HTAA_setIds()
+ *		SET UID AND GID (AS NAMES OR NUMBERS)
+ *		TO HTAAProt STRUCTURE
+ * ON ENTRY:
+ *	prot		destination.
+ *	ids		is a string like "james.www" or "1422.69" etc.
+ *			giving uid and gid.
+ *
+ * ON EXIT:
+ *	returns		nothing.
+ */
+static void HTAA_setIds(HTAAProt *prot, const char *ids)
+{
+    if (ids) {
+	char *local_copy = NULL;
+	char *point;
+
+	StrAllocCopy(local_copy, ids);
+	point = strchr(local_copy, '.');
+	if (point) {
+	    *(point++) = (char) 0;
+	    StrAllocCopy(prot->gid_name, point);
+	} else {
+	    StrAllocCopy(prot->gid_name, "nogroup");
+	}
+	StrAllocCopy(prot->uid_name, local_copy);
+	FREE(local_copy);
+    } else {
+	StrAllocCopy(prot->uid_name, "nobody");
+	StrAllocCopy(prot->gid_name, "nogroup");
+    }
+}
+
+/* static						HTAA_parseProtFile()
+ *		PARSE A PROTECTION SETUP FILE AND
+ *		PUT THE RESULT IN A HTAAProt STRUCTURE
+ * ON ENTRY:
+ *	prot		destination structure.
+ *	fp		open protection file.
+ *
+ * ON EXIT:
+ *	returns		nothing.
+ */
+static void HTAA_parseProtFile(HTAAProt *prot, FILE *fp)
+{
+    if (prot && fp) {
+	LexItem lex_item;
+	char *fieldname = NULL;
+
+	while (LEX_EOF != (lex_item = lex(fp))) {
+
+	    while (lex_item == LEX_REC_SEP)	/* Ignore empty lines */
+		lex_item = lex(fp);
+
+	    if (lex_item == LEX_EOF)	/* End of file */
+		break;
+
+	    if (lex_item == LEX_ALPH_STR) {	/* Valid setup record */
+
+		StrAllocCopy(fieldname, HTlex_buffer);
+
+		if (LEX_FIELD_SEP != (lex_item = lex(fp)))
+		    unlex(lex_item);	/* If someone wants to use colon */
+		/* after field name it's ok, but */
+		/* not required. Here we read it. */
+
+		if (0 == strncasecomp(fieldname, "Auth", 4)) {
+		    lex_item = lex(fp);
+		    while (lex_item == LEX_ALPH_STR) {
+			HTAAScheme scheme = HTAAScheme_enum(HTlex_buffer);
+
+			if (scheme != HTAA_UNKNOWN) {
+			    if (!prot->valid_schemes)
+				prot->valid_schemes = HTList_new();
+			    HTList_addObject(prot->valid_schemes, (void *) scheme);
+			    CTRACE((tfp, "%s %s `%s'\n",
+				    "HTAA_parseProtFile: valid",
+				    "authentication scheme:",
+				    HTAAScheme_name(scheme)));
+			} else {
+			    CTRACE((tfp, "%s %s `%s'\n",
+				    "HTAA_parseProtFile: unknown",
+				    "authentication scheme:",
+				    HTlex_buffer));
+			}
+
+			if (LEX_ITEM_SEP != (lex_item = lex(fp)))
+			    break;
+			/*
+			 * Here lex_item == LEX_ITEM_SEP; after item separator
+			 * it is ok to have one or more newlines (LEX_REC_SEP)
+			 * and they are ignored (continuation line).
+			 */
+			do {
+			    lex_item = lex(fp);
+			} while (lex_item == LEX_REC_SEP);
+		    }		/* while items in list */
+		}
+		/* if "Authenticate" */
+		else if (0 == strncasecomp(fieldname, "mask", 4)) {
+		    prot->mask_group = HTAA_parseGroupDef(fp);
+		    lex_item = LEX_REC_SEP;	/*groupdef parser read this already */
+		    if (TRACE) {
+			if (prot->mask_group) {
+			    fprintf(tfp,
+				    "HTAA_parseProtFile: Mask group:\n");
+			    HTAA_printGroupDef(prot->mask_group);
+			} else
+			    fprintf(tfp,
+				    "HTAA_parseProtFile: Mask group syntax error\n");
+		    }
+		}
+		/* if "Mask" */
+		else {		/* Just a name-value pair, put it to assoclist */
+
+		    if (LEX_ALPH_STR == (lex_item = lex(fp))) {
+			if (!prot->values)
+			    prot->values = HTAssocList_new();
+			HTAssocList_add(prot->values, fieldname, HTlex_buffer);
+			lex_item = lex(fp);	/* Read record separator */
+			CTRACE((tfp, "%s `%s' bound to value `%s'\n",
+				"HTAA_parseProtFile: Name",
+				fieldname, HTlex_buffer));
+		    }
+		}		/* else name-value pair */
+
+	    }
+	    /* if valid field */
+	    if (lex_item != LEX_EOF && lex_item != LEX_REC_SEP) {
+		CTRACE((tfp, "%s %s %d (that line ignored)\n",
+			"HTAA_parseProtFile: Syntax error",
+			"in protection setup file at line",
+			HTlex_line));
+		do {
+		    lex_item = lex(fp);
+		} while (lex_item != LEX_EOF && lex_item != LEX_REC_SEP);
+	    }			/* if syntax error */
+	}			/* while not end-of-file */
+	FREE(fieldname);
+    }				/* if valid parameters */
+}
+
+/* static						HTAAProt_new()
+ *		ALLOCATE A NEW HTAAProt STRUCTURE AND
+ *		INITIALIZE IT FROM PROTECTION SETUP FILE
+ * ON ENTRY:
+ *	cur_docname	current filename after rule translations.
+ *	prot_filename	protection setup file name.
+ *			If NULL, not an error.
+ *	ids		Uid and gid names or numbers,
+ *			examples:
+ *				james	( <=> james.nogroup)
+ *				.www	( <=> nobody.www)
+ *				james.www
+ *				james.69
+ *				1422.69
+ *				1422.www
+ *
+ *			May be NULL, defaults to nobody.nogroup.
+ *			Should be NULL, if prot_file is NULL.
+ *
+ * ON EXIT:
+ *	returns		returns a new and initialized protection
+ *			setup structure.
+ *			If setup file is already read in (found
+ *			in cache), only sets uid_name and gid
+ *			fields, and returns that.
+ */
+static HTAAProt *HTAAProt_new(const char *cur_docname,
+			      const char *prot_filename,
+			      const char *ids)
+{
+    HTList *cur = prot_cache;
+    HTAAProtCache *cache_item = NULL;
+    HTAAProt *prot;
+    FILE *fp;
+
+    if (!prot_cache)
+	prot_cache = HTList_new();
+
+    while (NULL != (cache_item = (HTAAProtCache *) HTList_nextObject(cur))) {
+	if (!strcmp(cache_item->prot_filename, prot_filename))
+	    break;
+    }
+    if (cache_item) {
+	prot = cache_item->prot;
+	CTRACE((tfp, "%s `%s' already in cache\n",
+		"HTAAProt_new: Protection file", prot_filename));
+    } else {
+	CTRACE((tfp, "HTAAProt_new: Loading protection file `%s'\n",
+		prot_filename));
+
+	if ((prot = typecalloc(HTAAProt)) == 0)
+	      outofmem(__FILE__, "HTAAProt_new");
+
+	assert(prot != NULL);
+
+	prot->ctemplate = NULL;
+	prot->filename = NULL;
+	prot->uid_name = NULL;
+	prot->gid_name = NULL;
+	prot->valid_schemes = HTList_new();
+	prot->mask_group = NULL;	/* Masking disabled by defaults */
+	prot->values = HTAssocList_new();
+
+	if (prot_filename && NULL != (fp = fopen(prot_filename, TXT_R))) {
+	    HTAA_parseProtFile(prot, fp);
+	    fclose(fp);
+	    if ((cache_item = typecalloc(HTAAProtCache)) == 0)
+		outofmem(__FILE__, "HTAAProt_new");
+
+	    assert(cache_item != NULL);
+
+	    cache_item->prot = prot;
+	    cache_item->prot_filename = NULL;
+	    StrAllocCopy(cache_item->prot_filename, prot_filename);
+	    HTList_addObject(prot_cache, (void *) cache_item);
+	} else {
+	    CTRACE((tfp, "HTAAProt_new: %s `%s'\n",
+		    "Unable to open protection setup file",
+		    NONNULL(prot_filename)));
+	}
+    }
+
+    if (cur_docname)
+	StrAllocCopy(prot->filename, cur_docname);
+    HTAA_setIds(prot, ids);
+
+    return prot;
+}
+
+/* PUBLIC					HTAA_setDefaultProtection()
+ *		SET THE DEFAULT PROTECTION MODE
+ *		(called by rule system when a
+ *		"defprot" rule is matched)
+ * ON ENTRY:
+ *	cur_docname	is the current result of rule translations.
+ *	prot_filename	is the protection setup file (second argument
+ *			for "defprot" rule, optional)
+ *	ids		contains user and group names separated by
+ *			a dot, corresponding to the uid
+ *			gid under which the server should run,
+ *			default is "nobody.nogroup" (third argument
+ *			for "defprot" rule, optional; can be given
+ *			only if protection setup file is also given).
+ *
+ * ON EXIT:
+ *	returns		nothing.
+ *			Sets the module-wide variable default_prot.
+ */
+void HTAA_setDefaultProtection(const char *cur_docname,
+			       const char *prot_filename,
+			       const char *ids)
+{
+    default_prot = NULL;	/* Not free()'d because this is in cache */
+
+    if (prot_filename) {
+	default_prot = HTAAProt_new(cur_docname, prot_filename, ids);
+    } else {
+	CTRACE((tfp, "%s %s\n",
+		"HTAA_setDefaultProtection: ERROR: Protection file",
+		"not specified (obligatory for DefProt rule)!!\n"));
+    }
+}
+
+/* PUBLIC					HTAA_setCurrentProtection()
+ *		SET THE CURRENT PROTECTION MODE
+ *		(called by rule system when a
+ *		"protect" rule is matched)
+ * ON ENTRY:
+ *	cur_docname	is the current result of rule translations.
+ *	prot_filename	is the protection setup file (second argument
+ *			for "protect" rule, optional)
+ *	ids		contains user and group names separated by
+ *			a dot, corresponding to the uid
+ *			gid under which the server should run,
+ *			default is "nobody.nogroup" (third argument
+ *			for "protect" rule, optional; can be given
+ *			only if protection setup file is also given).
+ *
+ * ON EXIT:
+ *	returns		nothing.
+ *			Sets the module-wide variable current_prot.
+ */
+void HTAA_setCurrentProtection(const char *cur_docname,
+			       const char *prot_filename,
+			       const char *ids)
+{
+    current_prot = NULL;	/* Not free()'d because this is in cache */
+
+    if (prot_filename) {
+	current_prot = HTAAProt_new(cur_docname, prot_filename, ids);
+    } else {
+	if (default_prot) {
+	    current_prot = default_prot;
+	    HTAA_setIds(current_prot, ids);
+	    CTRACE((tfp, "%s %s %s\n",
+		    "HTAA_setCurrentProtection: Protection file",
+		    "not specified for Protect rule",
+		    "-- using default protection"));
+	} else {
+	    CTRACE((tfp, "%s %s %s\n",
+		    "HTAA_setCurrentProtection: ERROR: Protection",
+		    "file not specified for Protect rule, and",
+		    "default protection is not set!!"));
+	}
+    }
+}
+
+/* PUBLIC					HTAA_getCurrentProtection()
+ *		GET CURRENT PROTECTION SETUP STRUCTURE
+ *		(this is set up by callbacks made from
+ *		 the rule system when matching "protect"
+ *		 (and "defprot") rules)
+ * ON ENTRY:
+ *	HTTranslate() must have been called before calling
+ *	this function.
+ *
+ * ON EXIT:
+ *	returns	a HTAAProt structure representing the
+ *		protection setup of the HTTranslate()'d file.
+ *		This must not be free()'d.
+ */
+HTAAProt *HTAA_getCurrentProtection(void)
+{
+    return current_prot;
+}
+
+/* PUBLIC					HTAA_getDefaultProtection()
+ *		GET DEFAULT PROTECTION SETUP STRUCTURE
+ *		AND SET IT TO CURRENT PROTECTION
+ *		(this is set up by callbacks made from
+ *		 the rule system when matching "defprot"
+ *		 rules)
+ * ON ENTRY:
+ *	HTTranslate() must have been called before calling
+ *	this function.
+ *
+ * ON EXIT:
+ *	returns	a HTAAProt structure representing the
+ *		default protection setup of the HTTranslate()'d
+ *		file (if HTAA_getCurrentProtection() returned
+ *		NULL, i.e., if there is no "protect" rule
+ *		but ACL exists, and we need to know default
+ *		protection settings).
+ *		This must not be free()'d.
+ * IMPORTANT:
+ *	As a side-effect this tells the protection system that
+ *	the file is in fact protected and sets the current
+ *	protection mode to default.
+ */
+HTAAProt *HTAA_getDefaultProtection(void)
+{
+    if (!current_prot) {
+	current_prot = default_prot;
+	default_prot = NULL;
+    }
+    return current_prot;
+}
+
+/* SERVER INTERNAL					HTAA_clearProtections()
+ *		CLEAR DOCUMENT PROTECTION MODE
+ *		(ALSO DEFAULT PROTECTION)
+ *		(called by the rule system)
+ * ON ENTRY:
+ *	No arguments.
+ *
+ * ON EXIT:
+ *	returns	nothing.
+ *		Frees the memory used by protection information.
+ */
+void HTAA_clearProtections(void)
+{
+    current_prot = NULL;	/* These are not freed because  */
+    default_prot = NULL;	/* they are actually in cache.  */
+}
+
+typedef struct {
+    char *name;
+    int user;
+} USER_DATA;
+
+#ifndef NOUSERS
+static HTList *known_grp = NULL;
+static HTList *known_pwd = NULL;
+static BOOL uidgid_cache_inited = NO;
+#endif
+
+#ifdef LY_FIND_LEAKS
+static void clear_uidgid_cache(void)
+{
+#ifndef NOUSERS
+    USER_DATA *data;
+
+    if (known_grp) {
+	while ((data = HTList_removeLastObject(known_grp)) != NULL) {
+	    FREE(data->name);
+	    FREE(data);
+	}
+	FREE(known_grp);
+    }
+    if (known_pwd) {
+	while ((data = HTList_removeLastObject(known_pwd)) != NULL) {
+	    FREE(data->name);
+	    FREE(data);
+	}
+	FREE(known_pwd);
+    }
+#endif
+}
+#endif /* LY_FIND_LEAKS */
+
+#ifndef NOUSERS
+static void save_gid_info(const char *name, int user)
+{
+    USER_DATA *data = typecalloc(USER_DATA);
+
+    if (!data)
+	return;
+    if (!known_grp) {
+	known_grp = HTList_new();
+	if (!uidgid_cache_inited) {
+#ifdef LY_FIND_LEAKS
+	    atexit(clear_uidgid_cache);
+#endif
+	    uidgid_cache_inited = YES;
+	}
+    }
+    StrAllocCopy(data->name, name);
+    data->user = user;
+    HTList_addObject(known_grp, data);
+}
+#endif /* NOUSERS */
+
+#ifndef NOUSERS
+static void save_uid_info(const char *name, int user)
+{
+    USER_DATA *data = typecalloc(USER_DATA);
+
+    if (!data)
+	return;
+    if (!known_pwd) {
+	known_pwd = HTList_new();
+	if (!uidgid_cache_inited) {
+#ifdef LY_FIND_LEAKS
+	    atexit(clear_uidgid_cache);
+#endif
+	    uidgid_cache_inited = YES;
+	}
+    }
+    StrAllocCopy(data->name, name);
+    data->user = user;
+    HTList_addObject(known_pwd, data);
+}
+#endif /* !NOUSERS */
+
+/* PUBLIC							HTAA_UidToName
+ *		GET THE USER NAME
+ * ON ENTRY:
+ *      The user-id
+ *
+ * ON EXIT:
+ *      returns the user name, or an empty string if not found.
+ */
+const char *HTAA_UidToName(int uid GCC_UNUSED)
+{
+#ifndef NOUSERS
+    struct passwd *pw;
+    HTList *me = known_pwd;
+
+    while (HTList_nextObject(me)) {
+	USER_DATA *data = (USER_DATA *) (me->object);
+
+	if (uid == data->user)
+	    return data->name;
+    }
+
+    if ((pw = getpwuid((uid_t) uid)) != 0
+	&& pw->pw_name != 0) {
+	CTRACE((tfp, "%s(%d) returned (%s:%d:...)\n",
+		"HTAA_UidToName: getpwuid",
+		uid,
+		pw->pw_name, (int) pw->pw_uid));
+	save_uid_info(pw->pw_name, (int) pw->pw_uid);
+	return pw->pw_name;
+    }
+#endif
+    return "";
+}
+
+/* PUBLIC							HTAA_NameToUid
+ *		GET THE USER ID
+ * ON ENTRY:
+ *      The user-name
+ *
+ * ON EXIT:
+ *      returns the user id, or NONESUCH if not found.
+ */
+int HTAA_NameToUid(const char *name GCC_UNUSED)
+{
+#ifndef NOUSERS
+    struct passwd *pw;
+    HTList *me = known_pwd;
+
+    while (HTList_nextObject(me)) {
+	USER_DATA *data = (USER_DATA *) (me->object);
+
+	if (!strcmp(name, data->name))
+	    return data->user;
+    }
+
+    if ((pw = getpwnam(name)) != 0) {
+	CTRACE((tfp, "%s(%s) returned (%s:%d:...)\n",
+		"HTAA_NameToUid: getpwnam",
+		name,
+		pw->pw_name, (int) pw->pw_uid));
+	save_uid_info(pw->pw_name, (int) pw->pw_uid);
+	return (int) pw->pw_uid;
+    }
+#endif
+    return NONESUCH;
+}
+
+/* PUBLIC							HTAA_GidToName
+ *		GET THE GROUP NAME
+ * ON ENTRY:
+ *      The group-id
+ *
+ * ON EXIT:
+ *      returns the group name, or an empty string if not found.
+ */
+const char *HTAA_GidToName(int gid GCC_UNUSED)
+{
+#ifndef NOUSERS
+    struct group *gr;
+    HTList *me = known_grp;
+
+    while (HTList_nextObject(me)) {
+	USER_DATA *data = (USER_DATA *) (me->object);
+
+	if (gid == data->user)
+	    return data->name;
+    }
+
+    if ((gr = getgrgid((gid_t) gid)) != 0
+	&& gr->gr_name != 0) {
+	CTRACE((tfp, "%s(%d) returned (%s:%d:...)\n",
+		"HTAA_GidToName: getgrgid",
+		gid,
+		gr->gr_name, (int) gr->gr_gid));
+	save_gid_info(gr->gr_name, (int) gr->gr_gid);
+	return gr->gr_name;
+    }
+#endif
+    return "";
+}
+
+/* PUBLIC							HTAA_NameToGid
+ *		GET THE GROUP ID
+ * ON ENTRY:
+ *      The group-name
+ *
+ * ON EXIT:
+ *      returns the group id, or NONESUCH if not found.
+ */
+int HTAA_NameToGid(const char *name GCC_UNUSED)
+{
+#ifndef NOUSERS
+    struct group *gr;
+    HTList *me = known_grp;
+
+    while (HTList_nextObject(me)) {
+	USER_DATA *data = (USER_DATA *) (me->object);
+
+	if (!strcmp(name, data->name))
+	    return data->user;
+    }
+
+    if ((gr = getgrnam(name)) != 0) {
+	CTRACE((tfp, "%s(%s) returned (%s:%d:...)\n",
+		"HTAA_NameToGid: getgrnam",
+		name,
+		gr->gr_name, (int) gr->gr_gid));
+	save_gid_info(gr->gr_name, (int) gr->gr_gid);
+	return (int) gr->gr_gid;
+    }
+#endif
+    return NONESUCH;
+}