/* 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 <HTAAFile.h>
#include <HTLex.h> /* Lexical analysor */
#include <HTAssoc.h> /* Association list */
#include <HTAAProt.h> /* Implemented here */
#include <LYLeaks.h>
/*
** Protection setup caching
*/
typedef struct {
char * prot_filename;
HTAAProt * prot;
} HTAAProtCache;
PRIVATE HTList * prot_cache = NULL; /* Protection setup cache. */
PRIVATE HTAAProt *default_prot = NULL; /* Default protection. */
PRIVATE HTAAProt *current_prot = NULL; /* Current protection mode */
/* which is set up by callbacks */
/* from the rule system when */
/* a "protect" rule is matched. */
/* PRIVATE isNumber()
** DOES A CHARACTER STRING REPRESENT A NUMBER
*/
PRIVATE BOOL isNumber ARGS1(CONST char *, s)
{
CONST char *cur = s;
if (!s || !*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;
}
#if defined (VMS) || defined (NOUSERS)
/* PUBLIC HTAA_getUidName()
** GET THE USER ID NAME (VMS ONLY)
** ON ENTRY:
** No arguments.
**
** ON EXIT:
** returns the user name
** Default is "" (nobody).
*/
PUBLIC char * HTAA_getUidName NOARGS
{
if (current_prot && current_prot->uid_name
&& (0 != strcmp(current_prot->uid_name,"nobody")) )
return(current_prot->uid_name);
else
return("");
}
/* PUBLIC HTAA_getFileName
** GET THE FILENAME (VMS ONLY)
** ON ENTRY:
** No arguments.
**
** ON EXIT:
** returns the filename
*/
PUBLIC char * HTAA_getFileName NOARGS
{
if (current_prot && current_prot->filename)
return(current_prot->filename);
else
return("");
}
#else /* not VMS */
/* 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).
*/
PUBLIC int HTAA_getUid NOARGS
{
struct passwd *pw = NULL;
if (current_prot && current_prot->uid_name) {
if (isNumber(current_prot->uid_name)) {
if (NULL != (pw = getpwuid(atoi(current_prot->uid_name)))) {
CTRACE(tfp, "%s(%s) returned (%s:%s:%d:%d:...)\n",
"HTAA_getUid: getpwuid",
current_prot->uid_name,
pw->pw_name,
#ifndef __MVS__ /* S/390 -- gil -- 0018 */
pw->pw_passwd,
#else
"(none)",
#endif /* __MVS __ */
(int) pw->pw_uid, (int) pw->pw_gid);
return pw->pw_uid;
}
}
else { /* User name (not a number) */
if (NULL != (pw = getpwnam(current_prot->uid_name))) {
CTRACE(tfp, "%s(\"%s\") %s (%s:%s:%d:%d:...)\n",
"HTAA_getUid: getpwnam",
current_prot->uid_name, "returned",
pw->pw_name,
#ifndef __MVS__ /* S/390 -- gil -- 0040 */
pw->pw_passwd,
#else
"(none)",
#endif /* __MVS __ */
(int) pw->pw_uid, (int) pw->pw_gid);
return pw->pw_uid;
}
}
}
/*
** Ok, then let's get uid for nobody.
*/
if (NULL != (pw = getpwnam("nobody"))) {
CTRACE(tfp, "HTAA_getUid: Uid for `nobody' is %d\n",
(int) pw->pw_uid);
return pw->pw_uid;
}
/*
** Ok, then use default.
*/
return 65534; /* 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).
*/
PUBLIC int HTAA_getGid NOARGS
{
struct group *gr = NULL;
if (current_prot && current_prot->gid_name) {
if (isNumber(current_prot->gid_name)) {
if (NULL != (gr = getgrgid(atoi(current_prot->gid_name)))) {
#if !defined(__EMX__) && !defined(__MVS__) /* no gr_passwd S/390 -- gil -- 0061 */
CTRACE(tfp, "%s(%s) returned (%s:%s:%d:...)\n",
"HTAA_getGid: getgrgid",
current_prot->gid_name,
gr->gr_name, gr->gr_passwd, (int) gr->gr_gid);
#endif
return gr->gr_gid;
}
}
else { /* Group name (not number) */
if (NULL != (gr = getgrnam(current_prot->gid_name))) {
#if !defined(__EMX__) && !defined(__MVS__) /* no gr_passwd S/390 -- gil -- 0078 */
CTRACE(tfp, "%s(\"%s\") returned (%s:%s:%d:...)\n",
"HTAA_getGid: getgrnam",
current_prot->gid_name,
gr->gr_name, gr->gr_passwd, (int) gr->gr_gid);
#endif
return gr->gr_gid;
}
}
}
/*
** Ok, then let's get gid for nogroup.
*/
if (NULL != (gr = getgrnam("nogroup"))) {
CTRACE(tfp, "HTAA_getGid: Gid for `nogroup' is %d\n",
(int) gr->gr_gid);
return gr->gr_gid;
}
/*
** Ok, then use default.
*/
return 65534; /* nogroup */
}
#endif /* not VMS */
/* PRIVATE 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.
*/
PRIVATE void HTAA_setIds ARGS2(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");
}
}
/* PRIVATE 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.
*/
PRIVATE void HTAA_parseProtFile ARGS2(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: %s\n",
gettext("Mask group syntax error"));
}
} /* 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 */
}
/* PRIVATE 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.
*/
PRIVATE HTAAProt *HTAAProt_new ARGS3(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 = (HTAAProt*)calloc(1, sizeof(HTAAProt))))
outofmem(__FILE__, "HTAAProt_new");
prot->template = 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, "r"))) {
HTAA_parseProtFile(prot, fp);
fclose(fp);
if (!(cache_item =
(HTAAProtCache*)calloc(1, sizeof(HTAAProtCache))))
outofmem(__FILE__, "HTAAProt_new");
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",
(prot_filename ? prot_filename : "(null)"));
}
}
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.
*/
PUBLIC void HTAA_setDefaultProtection ARGS3(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.
*/
PUBLIC void HTAA_setCurrentProtection ARGS3(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.
*/
PUBLIC HTAAProt *HTAA_getCurrentProtection NOARGS
{
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.
*/
PUBLIC HTAAProt *HTAA_getDefaultProtection NOARGS
{
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.
*/
PUBLIC void HTAA_clearProtections NOARGS
{
current_prot = NULL; /* These are not freed because */
default_prot = NULL; /* they are actually in cache. */
}