/** BEGIN COPYRIGHT BLOCK
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
* Copyright (C) 2005 Red Hat, Inc.
* All rights reserved.
* END COPYRIGHT BLOCK **/
/*
* Description (aclparse.c)
*
* This module provides functions for parsing a file containing
* Access Control List (ACL) definitions. It builds a representation
* of the ACLs in memory, using the services of the aclbuild module.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef XP_UNIX
#include
#include /* ntohl */
#include
#endif
void * aclChTab = 0; /* character class table handle */
static char * classv[] = {
" \t\r\f\013", /* class 0 - whitespace */
"\n", /* class 1 - newline */
",.;@*()+{}\"\'", /* class 2 - special characters */
"0123456789", /* class 3 - digits */
/* class 4 - letters */
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"-", /* class 5 - hyphen */
"_", /* class 6 - underscore */
"/-_.:" /* class 7 - filename special characters */
};
static int classc = sizeof(classv)/sizeof(char *);
/*
* Description (aclAuthListParse)
*
* This function parses an auth-list. An auth-list specifies
* combinations of user/group names and host addresses/names.
* An auth-list entry can identify a collection of users and/or
* groups, a collection of hosts by IP addresses or DNS names,
* or a combination of the two. Each auth-spec adds another
* ACClients_t structure to the specified list.
*
* The syntax for an auth-list is:
*
* auth-list ::= auth-spec | auth-list "," auth-spec
* auth-spec ::= auth-users [at-token auth-hosts]
* auth-users - see aclAuthUsersParse()
* auth-hosts - see aclAuthHostsParse()
* at-token ::= "at" | "@"
*
* The caller provides a pointer to a ClientSpec_t structure,
* which is built up with new information as auth-specs are parsed.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* acc - pointer to ACL context object
* rlm - pointer to authentication realm object
* clsp - pointer to returned ACClients_t list head
*
* Returns:
*
* If successful, the return value is the token type of the token
* following the auth-list, i.e. the first token which is not
* recognized as the start of an auth-spec. It is the caller's
* responsibility to validate this token as a legitimate terminator
* of an auth-list. If a parsing error occurs in the middle of
* an auth-spec, the return value is ACLERRPARSE, and an error frame
* is generated if an error list is provided. For other kinds of
* errors a negative error code (from aclerror.h) is returned.
*/
int aclAuthListParse(NSErr_t * errp, ACLFile_t * acf,
ACContext_t * acc, Realm_t * rlm, ACClients_t **clsp)
{
void * token = acf->acf_token; /* token handle */
ACClients_t * csp; /* client spec pointer */
UserSpec_t * usp; /* user spec pointer */
HostSpec_t * hsp; /* host spec pointer */
int rv; /* result value */
int eid; /* error id */
/* Loop once for each auth-spec */
for (rv = acf->acf_ttype; ; rv = aclGetToken(errp, acf, 0)) {
usp = 0;
hsp = 0;
/* Parse auth-users into user and group lists in the ACClients_t */
rv = aclAuthUsersParse(errp, acf, rlm, &usp, 0);
if (rv < 0) break;
/* Is the at-token there? */
if ((rv == TOKEN_AT) || !strcasecmp(lex_token(token), KEYWORD_AT)) {
/* Step to the next token after the at-token */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) break;
/* Parse auth-hosts part, adding information to the HostSpec_t */
rv = aclAuthHostsParse(errp, acf, acc, &hsp);
if (rv < 0) break;
}
/* Create a new ACClients_t structure for the parsed information */
csp = (ACClients_t *)MALLOC(sizeof(ACClients_t));
if (csp == 0) goto err_nomem;
csp->cl_next = 0;
csp->cl_user = usp;
csp->cl_host = hsp;
/* Add it to the end of the list referenced by clsp */
while (*clsp != 0) clsp = &(*clsp)->cl_next;
*clsp = csp;
/* Need a "," to keep going */
if (rv != TOKEN_COMMA) break;
}
return rv;
err_nomem:
eid = ACLERR1000;
nserrGenerate(errp, ACLERRNOMEM, eid, ACL_Program, 0);
return ACLERRNOMEM;
}
/*
* Description (aclAuthHostsParse)
*
* This function parses a list of IP address and/or DNS name
* specifications, adding information to the IP and DNS filters
* associated with a specified HostSpec_t. The syntax of the
* auth-hosts construct is:
*
* auth-hosts ::= auth-host-elem | "(" auth-host-list ")"
* | "hosts" host-list-name
* auth-host-elem ::= auth-ip-spec | auth-dns-spec
* auth-ip-spec ::= ipaddr | ipaddr netmask
* auth-dns-spec ::= fqdn | dns-suffix
* auth-host-list ::= auth-host-elem | auth-host-list "," auth-host-elem
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* acc - pointer to ACL context object
* hspp - pointer to HostSpec_t pointer
*
* Returns:
*
* If successful, the return value is the token type of the token
* following the auth-hosts, i.e. either the first token after a
* single auth-host-elem or the first token after the closing ")"
* of a list of auth-host-elems. It is the caller's responsibility
* to validate this token as a legitimate successor of auth-hosts.
* If a parsing error occurs in the middle of auth-hosts, the return
* value is ACLERRPARSE, and an error frame is generated if an error
* list is provided. For other kinds of errors a negative error
* code (from aclerror.h) is returned.
*/
int aclAuthHostsParse(NSErr_t * errp,
ACLFile_t * acf, ACContext_t * acc, HostSpec_t **hspp)
{
void * token = acf->acf_token; /* token handle */
char * tokenstr; /* token string pointer */
int islist = 0; /* true if auth-host-list */
int fqdn; /* fully qualified domain name */
IPAddr_t ipaddr; /* IP address value */
IPAddr_t netmask; /* IP netmask value */
int arv; /* alternate result value */
int rv; /* result value */
int eid; /* error id */
char linestr[16]; /* line number string buffer */
rv = acf->acf_ttype;
/* Are we starting an auth-host-list? */
if (rv == TOKEN_LPAREN) {
/* Yes, it appears so */
islist = 1;
/* Step token to first auth-host-elem */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) goto punt;
}
else if (rv == TOKEN_IDENT) {
/* Could this be "hosts host-list-name"? */
tokenstr = lex_token(token);
if (!strcasecmp(tokenstr, KEYWORD_HOSTS)) {
/* We don't support lists of host lists yet */
if (*hspp != 0) goto err_unshl;
/* Get host-list-name */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) goto punt;
if (rv != TOKEN_IDENT) goto err_hlname;
tokenstr = lex_token(token);
/* Look up the host-list-name in the ACL symbol table */
rv = symTableFindSym(acc->acc_stp,
tokenstr, ACLSYMHOST, (void **)hspp);
if (rv < 0) goto err_undefhl;
/* Step to token after the host-list-name */
rv = aclGetToken(errp, acf, 0);
return rv;
}
}
/* Loop for each auth-host-elem */
for (rv = acf->acf_ttype; ; rv = aclGetToken(errp, acf, 0)) {
/* Does this look like an auth-ip-spec? */
if (rv == TOKEN_NUMBER) {
/* Yes, go parse it */
rv = aclGetIPAddr(errp, acf, &ipaddr, &netmask);
if (rv < 0) goto punt;
arv = aclAuthIPAdd(hspp, ipaddr, netmask);
if (arv < 0) goto err_ipadd;
}
else if ((rv == TOKEN_STAR) || (rv == TOKEN_IDENT)) {
/* Get fully qualified DNS name indicator value */
fqdn = (rv == TOKEN_IDENT) ? 1 : 0;
/* This looks like the start of an auth-dns-spec */
rv = aclGetDNSString(errp, acf);
if (rv < 0) goto punt;
tokenstr = lex_token(token);
/* If the DNS spec begins with "*.", strip the "*" */
if (tokenstr && (tokenstr[0] == '*') && (tokenstr[1] == '.')) {
tokenstr += 1;
}
arv = aclAuthDNSAdd(hspp, tokenstr, fqdn);
if (arv < 0) goto err_dnsadd;
/* Pick up the next token */
rv = aclGetToken(errp, acf, 0);
}
else break;
/* If this is a list, we need a "," to keep going */
if (!islist || (rv != TOKEN_COMMA)) break;
}
/* Were we parsing an auth-host-list? */
if (islist) {
/* Yes, check for closing ")" */
if (acf->acf_ttype != TOKEN_RPAREN) goto err_norp;
/* Got it. Step to next token for caller. */
rv = aclGetToken(errp, acf, 0);
}
punt:
return rv;
err_unshl:
eid = ACLERR1100;
goto err_parse;
err_hlname:
eid = ACLERR1120;
goto err_parse;
err_undefhl:
eid = ACLERR1140;
rv = ACLERRUNDEF;
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program,
3, acf->acf_filename, linestr, tokenstr);
goto punt;
err_ipadd:
eid = ACLERR1180;
rv = arv;
goto err_ret;
err_dnsadd:
eid = ACLERR1200;
rv = arv;
goto err_ret;
err_ret:
nserrGenerate(errp, rv, eid, ACL_Program, 0);
goto punt;
err_norp:
eid = ACLERR1220;
err_parse:
rv = ACLERRPARSE;
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr);
goto punt;
}
/*
* Description (aclAuthUsersParse)
*
* This function parses a list of users and groups subject to
* authorization, adding the information to a specified UserSpec_t.
* The syntax it parses is:
*
* auth-users ::= auth-user-elem | "(" auth-user-list ")"
* auth-user-elem ::= username | groupname
* | "all" | "anyone"
* auth-user-list ::= auth-user-elem | auth-user-list "," auth-user-elem
*
* If the 'elist' argument is non-null, an auth-user-list will be
* accepted without the enclosing parentheses. Any invalid user
* or group names will not cause a fatal error, but will be returned
* in an array of strings via 'elist'.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* rlm - pointer to authentication realm object
* uspp - pointer to UserSpec_t pointer
* elist - pointer to returned pointer to array
* of strings containing invalid user or
* group names (may be null)
*
* Returns:
*
* If successful, the return value is the token type of the token
* following the auth-users, i.e. either the first token after a
* single auth-user-elem or the first token after the closing ")"
* of a list of auth-user-elems. It is the caller's responsibility
* to validate this token as a legitimate successor of auth-users.
* If a parsing error occurs in the middle of auth-users, the return
* value is ACLERRPARSE, and an error frame is generated if an error
* list is provided. For other kinds of errors a negative error
* code (from aclerror.h) is returned.
*/
int aclAuthUsersParse(NSErr_t * errp, ACLFile_t * acf,
Realm_t * rlm, UserSpec_t **uspp, char ***elist)
{
void * token = acf->acf_token; /* token handle */
char * tokenstr; /* token string pointer */
UserSpec_t * usp; /* user list head structure */
int islist = 0; /* true if auth-user-list */
int inlist = 0; /* true if UserSpec_t was supplied */
int any = 0; /* true if KEYWORD_ANY seen */
int all = 0; /* true if KEYWORD_ALL seen */
int elemcnt = 0; /* count of auth-user-elem seen */
int elen = 0; /* length of evec in (char *) */
int ecnt = 0; /* entries used in evec */
char **evec = 0; /* list of bad user/group names */
int rv; /* result value */
int eid; /* error id */
char linestr[16]; /* line number string buffer */
int errc = 2;
usp = *uspp;
if ((usp != 0) && (usp->us_flags & ACL_USALL)) all = 1;
if (elist != 0) inlist = 1;
else {
/* Check for opening "(" */
if (acf->acf_ttype == TOKEN_LPAREN) {
/* Looks like an auth-user-list */
islist = 1;
/* Step token to first auth-user-elem */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) goto punt;
}
}
/* Loop for each auth-user-elem */
for (rv = acf->acf_ttype; ; rv = aclGetToken(errp, acf, 0)) {
/* Looking for a user or group identifier */
if ((rv == TOKEN_IDENT) || (rv == TOKEN_STRING)) {
/*
* If KEYWORD_ALL or KEYWORD_ANY has already appeared
* in this auth-spec, then return an error.
*/
if (all | any) goto err_allany;
/* Check for reserved words */
tokenstr = lex_token(token);
/* KEYWORD_AT begins auth-hosts, but is invalid here */
if (!strcasecmp(tokenstr, KEYWORD_AT)) break;
/* Check for special group names */
if (!strcasecmp(tokenstr, KEYWORD_ANY)) {
/*
* Any user, with no authentication needed. This can
* only appear once in an auth-spec, and cannot be used
* in combination with KEYWORD_ALL (or any other user or
* group identifiers, but that will get checked before
* we return).
*/
if ((elemcnt > 0) || (usp != 0)) goto err_any;
any = 1;
}
else if (!strcasecmp(tokenstr, KEYWORD_ALL)) {
/*
* Any authenticated user. This can only appear once in
* an auth-spec, and cannot be used in combination with
* KEYWORD_ANY (or any other user or group identifiers,
* but that will get checked before we return).
*/
if (elemcnt > 0) goto err_all;
/* Create a UserSpec_t structure if we haven't got one yet */
if (usp == 0) {
usp = aclUserSpecCreate();
if (usp == 0) goto err_nomem1;
*uspp = usp;
}
usp->us_flags |= ACL_USALL;
all = 1;
}
else {
/* Create a UserSpec_t structure if we haven't got one yet */
if (usp == 0) {
usp = aclUserSpecCreate();
if (usp == 0) goto err_nomem2;
*uspp = usp;
}
/* This should be a user or group name */
rv = aclAuthNameAdd(errp, usp, rlm, tokenstr);
if (rv <= 0) {
/* The name was not found in the authentication DB */
if (elist != 0) {
if (evec == 0) {
evec = (char **)MALLOC(4*sizeof(char *));
evec[0] = 0;
ecnt = 1;
elen = 4;
}
else if (ecnt >= elen) {
elen += 4;
evec = (char **)REALLOC(evec, elen*sizeof(char *));
}
evec[ecnt-1] = STRDUP(tokenstr);
evec[ecnt] = 0;
++ecnt;
}
else if (rv < 0) goto err_badgun;
}
/* Don't allow duplicate names */
if (rv & ANA_DUP) {
if (elist == 0) goto err_dupgun;
}
}
/* Count number of auth-user-elems seen */
elemcnt += 1;
/* Get the token after the auth-user-elem */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) goto punt;
}
/* If this is a list, we need a "," to keep going */
if (!(islist | inlist) || (rv != TOKEN_COMMA)) break;
}
/* Were we parsing an auth-user-list? */
if (islist) {
/* Yes, check for closing ")" */
if (acf->acf_ttype != TOKEN_RPAREN) goto err_norp;
/* Got it. Step to next token for caller. */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) goto punt;
}
/*
* If we didn't see any auth-user-elems, then the auth-user we were
* called to parse is missing. We will forgive and forget if the
* current token is a comma, however, so as to allow empty auth-specs.
*/
if ((elemcnt <= 0) && (rv != TOKEN_COMMA)) {
goto err_noelem;
}
punt:
/* Return list of bad names if indicated */
if (elist != 0) *elist = evec;
return rv;
err_badgun:
/* Encountered an unknown user or group name */
eid = ACLERR1360;
rv = ACLERRUNDEF;
goto err_retgun;
err_dupgun:
/* A user or group name was specified multiple times */
eid = ACLERR1380;
rv = ACLERRDUPSYM;
goto err_retgun;
err_retgun:
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program,
3, acf->acf_filename, linestr, tokenstr);
goto punt;
err_norp:
/* Missing ")" */
eid = ACLERR1400;
goto err_parse;
err_noelem:
eid = ACLERR1420;
goto err_parse;
err_all:
eid = ACLERR1440;
goto err_parse;
err_any:
eid = ACLERR1460;
goto err_parse;
err_allany:
eid = ACLERR1480;
goto err_parse;
err_nomem1:
eid = ACLERR1500;
rv = ACLERRNOMEM;
errc = 0;
goto err_ret;
err_nomem2:
eid = ACLERR1520;
rv = ACLERRNOMEM;
errc = 0;
goto err_ret;
err_parse:
rv = ACLERRPARSE;
err_ret:
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, errc, acf->acf_filename, linestr);
goto punt;
}
/*
* Description (aclDirectivesParse)
*
* This function parses the directives inside an ACL definition.
* The syntax for a directive list is:
*
* dir-list ::= directive | dir-list ";" directive
* directive ::= auth-directive | access-directive | exec-directive
* auth-directive ::= dir-force "authenticate" ["in" realm-spec]
* access-directive ::= dir-force dir-access auth-list
* exec-directive ::= dir-force "execute" ["if" exec-optlist]
* exec-optlist ::= exec-condition | exec-optlist "," exec-condition
* exec-condition ::= dir-access | "authenticate"
* dir-force ::= "Always" | "Default"
* dir-access ::= "allow" | "deny"
*
* See aclAuthListParse() for auth-list syntax.
* See aclRealmSpecParse() for realm-spec syntax.
*
* The caller provides a pointer to an ACL structure, which is
* built up with new information as directives are parsed.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* acl - pointer to ACL structure
*
* Returns:
*
* If successful, the return value is the token type of the token
* following the directive list, i.e. the first token which is not
* recognized as the start of a directive. It is the caller's
* responsibility to validate this token as a legitimate terminator
* of a directive list. If a parsing error occurs in the middle of
* a directive, the return value is ACLERRPARSE, and an error frame
* is generated if an error list is provided. For other kinds of
* errors a negative error code (from aclerror.h) is returned.
*/
int aclDirectivesParse(NSErr_t * errp, ACLFile_t * acf, ACL_t * acl)
{
void * token = acf->acf_token; /* token handle */
char * tokenstr; /* token string */
Realm_t * rlm = 0; /* current realm pointer */
ACDirective_t * acd; /* directive pointer */
int action; /* directive action code */
int flags; /* directive action flags */
int arv; /* alternate return value */
int rv; /* result value */
int eid; /* error id */
char linestr[16]; /* line number string buffer */
/* Look for top-level directives */
for (rv = acf->acf_ttype; ; rv = aclGetToken(errp, acf, 0)) {
action = 0;
flags = 0;
/* Check for beginning of directive */
if (rv == TOKEN_IDENT) {
/* Check identifier for directive dir-force keywords */
tokenstr = lex_token(token);
if (!strcasecmp(tokenstr, KEYWORD_DEFAULT)) {
flags = ACD_DEFAULT;
}
else if (!strcasecmp(tokenstr, "always")) {
flags = ACD_ALWAYS;
}
else break;
/*
* Now we're looking for dir-access, "authenticate",
* or "execute".
*/
rv = aclGetToken(errp, acf, 0);
/* An identifier would be nice ... */
if (rv != TOKEN_IDENT) goto err_access;
tokenstr = lex_token(token);
if (!strcasecmp(tokenstr, KEYWORD_AUTH)) {
/* process auth-directive */
action = ACD_AUTH;
/* Create a new directive object */
acd = aclDirectiveCreate();
if (acd == 0) goto err_nomem1;
/* Get the next token after KEYWORD_AUTH */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) break;
/* Could we have "in" realm-spec here? */
if (rv == TOKEN_IDENT) {
tokenstr = lex_token(token);
if (!strcasecmp(tokenstr, KEYWORD_IN)) {
/* Get the next token after KEYWORD_IN */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) break;
/* Parse the realm-spec */
rv = aclRealmSpecParse(errp, acf, acl->acl_acc,
&acd->acd_auth.au_realm);
if (rv < 0) break;
/* Set current realm */
if (acd->acd_auth.au_realm != 0) {
/* Close database in current realm if any */
if (rlm && rlm->rlm_authdb) {
(*rlm->rlm_aif->aif_close)(rlm->rlm_authdb, 0);
rlm->rlm_authdb = 0;
}
rlm = &acd->acd_auth.au_realm->rs_realm;
}
}
}
/* Add this directive to the ACL */
acd->acd_action = action;
acd->acd_flags = flags;
arv = aclDirectiveAdd(acl, acd);
if (arv < 0) goto err_diradd1;
}
else if (!strcasecmp(tokenstr, KEYWORD_EXECUTE)) {
/* process exec-directive */
action = ACD_EXEC;
/* Create a new directive object */
acd = aclDirectiveCreate();
if (acd == 0) goto err_nomem3;
/* Get the next token after KEYWORD_EXECUTE */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) break;
/* Could we have "if" exec-optlist here? */
if (rv == TOKEN_IDENT) {
tokenstr = lex_token(token);
if (!strcasecmp(tokenstr, KEYWORD_IF)) {
for (;;) {
/* Get the next token after KEYWORD_IF or "," */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) break;
/*
* Looking for "allow", "deny", or "authenticate"
*/
if (rv == TOKEN_IDENT) {
tokenstr = lex_token(token);
if (!strcasecmp(tokenstr, KEYWORD_ALLOW)) {
flags |= ACD_EXALLOW;
}
else if (!strcasecmp(tokenstr, KEYWORD_DENY)) {
flags |= ACD_EXDENY;
}
else if (!strcasecmp(tokenstr, KEYWORD_AUTH)) {
flags |= ACD_EXAUTH;
}
else goto err_exarg;
}
/* End of directive if no comma */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) break;
if (rv != TOKEN_COMMA) break;
}
}
}
else flags = (ACD_EXALLOW|ACD_EXDENY|ACD_EXAUTH);
if (rv < 0) break;
/* Add this directive to the ACL */
acd->acd_action = action;
acd->acd_flags = flags;
arv = aclDirectiveAdd(acl, acd);
if (arv < 0) goto err_diradd3;
}
else {
/* process access-directive */
if (!strcasecmp(tokenstr, KEYWORD_ALLOW)) {
action = ACD_ALLOW;
}
else if (!strcasecmp(tokenstr, KEYWORD_DENY)) {
action = ACD_DENY;
}
else goto err_acctype;
/* Get the next token after dir-access */
rv = aclGetToken(errp, acf, 0);
/* Create a new directive object */
acd = aclDirectiveCreate();
if (acd == 0) goto err_nomem2;
/* Parse a list of auth-specs */
rv = aclAuthListParse(errp, acf, acl->acl_acc, rlm,
&acd->acd_cl);
if (rv < 0) break;
/* Add this directive to the ACL */
acd->acd_action = action;
acd->acd_flags = flags;
arv = aclDirectiveAdd(acl, acd);
if (arv < 0) goto err_diradd2;
}
}
/* Need a ";" to keep going */
if (rv != TOKEN_EOS) break;
}
punt:
/* Close database in current realm if any */
if (rlm && rlm->rlm_authdb) {
(*rlm->rlm_aif->aif_close)(rlm->rlm_authdb, 0);
rlm->rlm_authdb = 0;
}
return rv;
err_access:
/* dir-access not present */
eid = ACLERR1600;
rv = ACLERRPARSE;
goto err_ret;
err_acctype:
/* dir-access identifier is invalid */
eid = ACLERR1620;
rv = ACLERRPARSE;
goto err_ret;
err_diradd1:
eid = ACLERR1640;
rv = arv;
tokenstr = 0;
goto err_ret;
err_diradd2:
eid = ACLERR1650;
rv = arv;
tokenstr = 0;
goto err_ret;
err_nomem1:
eid = ACLERR1660;
rv = ACLERRNOMEM;
tokenstr = 0;
goto err_ret;
err_nomem2:
eid = ACLERR1680;
rv = ACLERRNOMEM;
tokenstr = 0;
goto err_ret;
err_nomem3:
eid = ACLERR1685;
rv = ACLERRNOMEM;
tokenstr = 0;
goto err_ret;
err_diradd3:
eid = ACLERR1690;
rv = arv;
tokenstr = 0;
goto err_ret;
err_exarg:
eid = ACLERR1695;
rv = ACLERRSYNTAX;
goto err_ret;
err_ret:
sprintf(linestr, "%d", acf->acf_lineno);
if (tokenstr) {
nserrGenerate(errp, rv, eid, ACL_Program,
3, acf->acf_filename, linestr, tokenstr);
}
else {
nserrGenerate(errp, rv, eid, ACL_Program,
2, acf->acf_filename, linestr);
}
goto punt;
}
/*
* Description (aclACLParse)
*
* This function parses a data stream containing ACL definitions,
* and builds a representation of the ACLs in memory. Each ACL
* has a user-specified name, and a pointer to the ACL structure
* is stored under the name in a symbol table provided by the caller.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* acc - pointer to ACContext_t structure
* flags - bit flags (unused - must be zero)
*
* Returns:
*
* The return value is zero if the stream is parsed successfully.
* Otherwise it is a negative error code (ACLERRxxxx - see aclerror.h),
* and an error frame will be generated if an error list is provided.
*/
int aclACLParse(NSErr_t * errp, ACLFile_t * acf, ACContext_t * acc, int flags)
{
void * token = acf->acf_token; /* handle for current token */
char * tokenstr; /* current token string */
char * aclname; /* ACL name string */
ACL_t * aclp; /* pointer to ACL structure */
int rv; /* result value */
int eid; /* error id value */
char linestr[16]; /* line number string buffer */
/* Look for top-level statements */
for (;;) {
/* Get a token to begin a statement */
rv = aclGetToken(errp, acf, 0);
/* An identifier would be nice ... */
if (rv != TOKEN_IDENT) {
/* Empty statements are ok, if pointless */
if (rv == TOKEN_EOS) continue;
/* EOF is valid here */
if (rv == TOKEN_EOF) break;
/* Anything else is unacceptable */
goto err_nostmt;
}
/* Check identifier for statement keywords */
tokenstr = lex_token(token);
if (!strcasecmp(tokenstr, KEYWORD_ACL)) {
/* ACL name rights-list { acl-def-list }; */
/* Get the name of the ACL */
rv = aclGetToken(errp, acf, 0);
if (rv != TOKEN_IDENT) goto err_aclname;
aclname = lex_token(token);
/* Create the ACL structure */
rv = aclCreate(errp, acc, aclname, &aclp);
if (rv < 0) goto punt;
/* Get the next token after the ACL name */
rv = aclGetToken(errp, acf, 0);
/* Parse the rights specification */
rv = aclRightsParse(errp, acf, acc, &aclp->acl_rights);
/* Want a "{" to open the ACL directive list */
if (rv != TOKEN_LBRACE) {
if (rv < 0) goto punt;
goto err_aclopen;
}
/* Get the first token in the ACL directive list */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) goto punt;
/* Parse the ACL directive list */
rv = aclDirectivesParse(errp, acf, aclp);
/* Want a "}" to close the ACL directive list */
if (rv != TOKEN_RBRACE) {
if (rv < 0) goto punt;
goto err_aclclose;
}
}
else if (!strcasecmp(tokenstr, KEYWORD_INCLUDE)) {
/* Include "filename"; */
}
else if (!strcasecmp(tokenstr, KEYWORD_REALM)) {
/* Realm name realm-spec */
}
else if (!strcasecmp(tokenstr, KEYWORD_RIGHTS)) {
/* Rights name rights-def; */
}
else if (!strcasecmp(tokenstr, KEYWORD_HOSTS)) {
/* Hosts name auth-hosts; */
}
else goto err_syntax;
}
return 0;
err_nostmt:
eid = ACLERR1700;
rv = ACLERRPARSE;
goto err_ret;
err_aclname:
eid = ACLERR1720;
rv = ACLERRPARSE;
goto err_ret;
err_aclopen:
eid = ACLERR1740;
rv = ACLERRPARSE;
goto err_ret;
err_aclclose:
eid = ACLERR1760;
rv = ACLERRPARSE;
goto err_ret;
err_ret:
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr);
goto punt;
err_syntax:
eid = ACLERR1780;
rv = ACLERRPARSE;
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program,
3, acf->acf_filename, linestr, tokenstr);
punt:
return rv;
}
/*
* Description (aclFileClose)
*
* This function closes an ACL file previously opened by aclFileOpen(),
* and frees any associated data structures.
*
* Arguments:
*
* acf - pointer to ACL file information
* flags - bit flags (unused - must be zero)
*/
void aclFileClose(ACLFile_t * acf, int flags)
{
if (acf != 0) {
/* Destroy the associated lexer stream if any */
if (acf->acf_lst != 0) {
lex_stream_destroy(acf->acf_lst);
}
/* Close the file if it's open */
if (acf->acf_fd != SYS_ERROR_FD) {
system_fclose(acf->acf_fd);
}
/* Destroy any associated token */
if (acf->acf_token != 0) {
lex_token_destroy(acf->acf_token);
}
/* Free the filename string if any */
if (acf->acf_filename != 0) {
FREE(acf->acf_filename);
}
/* Free the ACLFile_t structure */
FREE(acf);
}
}
/*
* Description (aclFileOpen)
*
* This function opens a specified filename and creates a structure
* to contain information about the file during parsing. This
* includes a handle for a LEX data stream for the file.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* filename - name of file to be opened
* flags - bit flags (unused - must be zero)
* pacf - pointer to returned ACLFile_t pointer
*
* Returns:
*
* The return value is zero if the file is opened successfully, and
* a pointer to the ACLFile_t is returned in the location specified
* by 'pacf'. Otherwise a negative error code (ACLERRxxxx - see
* aclerror.h) is returned, and an error frame will be generated if
* an error list is provided.
*/
int aclFileOpen(NSErr_t * errp,
char * filename, int flags, ACLFile_t **pacf)
{
ACLFile_t * acf; /* pointer to ACL file structure */
int rv; /* return value */
int eid; /* error identifier */
char * errmsg; /* system error message string */
*pacf = 0;
/* Allocate the ACLFile_t structure */
acf = (ACLFile_t *)MALLOC(sizeof(ACLFile_t));
if (acf == 0) goto err_nomem1;
memset((void *)acf, 0, sizeof(ACLFile_t));
acf->acf_filename = STRDUP(filename);
acf->acf_lineno = 1;
acf->acf_flags = flags;
/* Create a LEX token object */
rv = lex_token_new((pool_handle_t *)0, 32, 8, &acf->acf_token);
if (rv < 0) goto err_nomem2;
/* Open the file */
acf->acf_fd = system_fopenRO(acf->acf_filename);
if (acf->acf_fd == SYS_ERROR_FD) goto err_open;
/* Create a LEX stream for the file */
acf->acf_lst = lex_stream_create(aclStreamGet,
(void *)acf->acf_fd, 0, 8192);
if (acf->acf_lst == 0) goto err_nomem3;
*pacf = acf;
return 0;
err_open: /* file open error */
rv = ACLERROPEN;
eid = ACLERR1900;
errmsg = system_errmsg();
nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg);
goto punt;
err_nomem1: /* MALLOC of ACLFile_t failed */
rv = ACLERRNOMEM;
eid = ACLERR1920;
goto err_mem;
err_nomem2: /* lex_token_new() failed */
rv = ACLERRNOMEM;
eid = ACLERR1940;
goto err_mem;
err_nomem3: /* lex_stream_create() failed */
system_fclose(acf->acf_fd);
rv = ACLERRNOMEM;
eid = ACLERR1960;
err_mem:
nserrGenerate(errp, rv, eid, ACL_Program, 0);
goto punt;
punt:
return rv;
}
/*
* Description (aclGetDNSString)
*
* This function parses a DNS name specification, which consists
* of a sequence of DNS name components separated by ".". Each
* name component must start with a letter, and contains only
* letters, digits, and hyphens. An exception is that the first
* component may be the wildcard indicator, "*". This function
* assumes that the current token already contains a TOKEN_STAR
* or TOKEN_IDENT. The complete DNS name specification is
* returned as the current token string.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
*
* Returns:
*
* The character terminating the DNS name specification is returned
* as the function value. The current token type is unchanged, but
* the string associated with the current token contains the
* complete DNS name specification. An error is indicated by a
* negative return value, and an error frame is generated if an
* error list is provided.
*/
int aclGetDNSString(NSErr_t * errp, ACLFile_t * acf)
{
LEXStream_t * lst = acf->acf_lst; /* LEX stream handle */
void * token = acf->acf_token; /* LEX token handle */
int rv; /* result value */
int eid; /* error id value */
char linestr[16]; /* line number string buffer */
/* The current token should be TOKEN_STAR or TOKEN_IDENT */
rv = acf->acf_ttype;
if ((rv != TOKEN_STAR) && (rv != TOKEN_IDENT)) goto err_dns1;
/* Loop to parse [ "." dns-component ]+ */
for (;;) {
/* Try to step over a "." */
rv = lex_next_char(lst, aclChTab, 0);
/* End of DNS string if there's not one there */
if (rv != '.') break;
/* Append the "." to the token string */
(void)lex_token_append(token, 1, ".");
/* Advance the input stream past the "." */
rv = lex_next_char(lst, aclChTab, CCM_SPECIAL);
/* Next we want to see a letter */
rv = lex_next_char(lst, aclChTab, 0);
/* Error if it's not there */
if (!lex_class_check(aclChTab, rv, CCM_LETTER)) goto err_dns2;
/* Append a string of letters, digits, hyphens to token */
rv = lex_scan_over(lst, aclChTab, (CCM_LETTER|CCM_DIGIT|CCM_HYPHEN),
token);
if (rv < 0) goto err_dns3;
}
punt:
return rv;
err_dns1:
eid = ACLERR2100;
rv = ACLERRPARSE;
goto err_ret;
err_dns2:
eid = ACLERR2120;
rv = ACLERRPARSE;
goto err_ret;
err_dns3:
eid = ACLERR2140;
rv = ACLERRPARSE;
goto err_ret;
err_ret:
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr);
goto punt;
}
int aclGetFileSpec(NSErr_t * errp, ACLFile_t * acf, int flags)
{
LEXStream_t * lst = acf->acf_lst; /* LEX stream handle */
void * token = acf->acf_token; /* LEX token handle */
char * tokenstr; /* token string pointer */
int rv; /* result value */
int eid; /* error id value */
char linestr[16]; /* line number string buffer */
/* Skip whitespace */
rv = lex_skip_over(lst, aclChTab, CCM_WS);
if (rv < 0) goto err_lex1;
/* Begin a new token string */
rv = lex_token_start(token);
rv = lex_scan_over(lst, aclChTab, CCM_FILENAME, token);
if (rv < 0) goto err_lex2;
tokenstr = lex_token(token);
if (!tokenstr || !*tokenstr) goto err_nofn;
punt:
return rv;
err_lex1:
eid = ACLERR2900;
goto err_parse;
err_lex2:
eid = ACLERR2920;
goto err_parse;
err_nofn:
eid = ACLERR2940;
err_parse:
rv = ACLERRPARSE;
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr);
goto punt;
}
/*
* Description (aclGetIPAddr)
*
* This function retrieves an IP address specification from a given
* input stream. The specification consists of an IP address expressed
* in the standard "." notation, possibly followed by whitespace and a
* netmask, also in "." form. The IP address and netmask values are
* returned. If no netmask is specified, a default value of 0xffffffff
* is returned.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* pip - pointer to returned IP address value
* pmask - pointer to returned IP netmask value
*
* Returns:
*
* If successful, the return value identifies the type of the token
* following the IP address specification. This token type value is
* also returned in acf_ttype. An error is indicated by a negative
* error code (ACLERRxxxx - see aclerror.h), and an error frame will
* be generated if an error list is provided. The token type code in
* acf_ttype is TOKEN_ERROR when an error code is returned.
*/
int aclGetIPAddr(NSErr_t * errp,
ACLFile_t * acf, IPAddr_t * pip, IPAddr_t * pmask)
{
LEXStream_t * lst = acf->acf_lst; /* LEX stream handle */
void * token = acf->acf_token; /* LEX token handle */
char * tokenstr; /* token string pointer */
IPAddr_t ipaddr; /* IP address */
IPAddr_t netmask; /* IP netmask */
int dotcnt; /* count of '.' seen in address */
int rv; /* result value */
int eid; /* error id value */
char linestr[16]; /* line number string buffer */
/* Set default return values */
*pip = 0;
*pmask = 0xffffffff;
rv = acf->acf_ttype;
/* The current token must be a number */
if (rv != TOKEN_NUMBER) {
/* No IP address present */
return rv;
}
/* Assume no netmask */
netmask = 0xffffffff;
for (dotcnt = 0;;) {
/* Append digits and letters to the current token */
rv = lex_scan_over(lst, aclChTab, (CCM_DIGIT|CCM_LETTER), token);
if (rv < 0) goto err_lex1;
/* Stop when no "." follows the digits and letters */
if (rv != '.') break;
/* Stop if we've already seen three "." */
if (++dotcnt > 3) break;
/* Advance past the "." */
(void)lex_next_char(lst, aclChTab, CCM_SPECIAL);
/* Check the next character for a "*" */
rv = lex_next_char(lst, aclChTab, 0);
if (rv == '*') {
/* Advance past the "*" */
(void)lex_next_char(lst, aclChTab, CCM_SPECIAL);
netmask <<= ((4-dotcnt)*8);
netmask = htonl(netmask);
while (dotcnt < 4) {
(void)lex_token_append(token, 2, ".0");
++dotcnt;
}
break;
}
else {
/* Append the "." to the token string */
(void)lex_token_append(token, 1, ".");
}
}
/* Get a pointer to the token string */
tokenstr = lex_token(token);
/* A NULL pointer or an empty string is an error */
if (!tokenstr || !*tokenstr) goto err_noip;
/* Convert IP address to binary */
ipaddr = inet_addr(tokenstr);
if (ipaddr == (unsigned long)-1) goto err_badip;
/* Skip whitespace */
rv = lex_skip_over(lst, aclChTab, CCM_WS);
if (rv < 0) goto err_lex2;
/* A digit is the start of a netmask */
if ((netmask == 0xffffffff) && lex_class_check(aclChTab, rv, CCM_DIGIT)) {
/* Initialize token for network mask */
rv = lex_token_start(token);
for (dotcnt = 0;;) {
/* Collect token including digits, letters, and periods */
rv = lex_scan_over(lst, aclChTab, (CCM_DIGIT|CCM_LETTER), token);
if (rv < 0) goto err_lex3;
/* Stop when no "." follows the digits and letters */
if (rv != '.') break;
/* Stop if we've already seen three "." */
if (++dotcnt > 3) break;
/* Append the "." to the token string */
(void)lex_token_append(token, 1, ".");
/* Advance past the "." */
(void)lex_next_char(lst, aclChTab, CCM_SPECIAL);
}
/* Get a pointer to the token string */
tokenstr = lex_token(token);
/* A NULL pointer or an empty string is an error */
if (!tokenstr || !*tokenstr) goto err_nonm;
/* Convert netmask to binary. */
netmask = inet_addr(tokenstr);
if (netmask == (unsigned long)-1) {
/*
* Unfortunately inet_addr() doesn't distinguish between an
* error and a valid conversion of "255.255.255.255". So
* we check for it explicitly. Too bad if "0xff.0xff.0xff.0xff"
* is specified. Don't do that!
*/
if (strcmp(tokenstr, "255.255.255.255")) goto err_badnm;
}
}
/* Return the IP address and netmask in host byte order */
*pip = ntohl(ipaddr);
*pmask = ntohl(netmask);
/* Get the token following the IP address (and netmask) */
rv = aclGetToken(errp, acf, 0);
punt:
acf->acf_ttype = (rv < 0) ? TOKEN_ERROR : rv;
return rv;
err_lex1:
eid = ACLERR2200;
rv = ACLERRPARSE;
goto err_ret;
err_lex2:
eid = ACLERR2220;
rv = ACLERRPARSE;
goto err_ret;
err_lex3:
eid = ACLERR2240;
rv = ACLERRPARSE;
goto err_ret;
err_noip:
eid = ACLERR2260;
rv = ACLERRPARSE;
goto err_ret;
err_badip:
eid = ACLERR2280;
rv = ACLERRPARSE;
goto err_ret;
err_nonm:
eid = ACLERR2300;
rv = ACLERRPARSE;
goto err_ret;
err_badnm:
eid = ACLERR2320;
rv = ACLERRPARSE;
goto err_ret;
err_ret:
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr);
goto punt;
}
/*
* Description (aclGetToken)
*
* This function retrieves the next token in an ACL definition file.
* It skips blank lines, comments, and white space. It updates
* the current line number as newlines are encountered.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* flags - bit flags:
* AGT_NOSKIP - don't skip leading whitespace
* AGT_APPEND - append to token buffer
* (else start new token)
*
* Returns:
*
* The return value is a code identifying the next token if successful.
* This token type value is also returned in acf_ttype. An error
* is indicated by a negative error code (ACLERRxxxx - see aclerror.h),
* and an error frame will be generated if an error list is provided.
* The token type code in acf_ttype is TOKEN_ERROR when an error code
* is returned.
*/
int aclGetToken(NSErr_t * errp, ACLFile_t * acf, int flags)
{
LEXStream_t * lst = acf->acf_lst; /* LEX stream handle */
void * token = acf->acf_token; /* LEX token handle */
int dospecial = 0; /* handle CCM_SPECIAL character */
int tv; /* token value */
int rv; /* result value */
int eid; /* error id */
char spech;
char linestr[16]; /* line number string buffer */
/* Begin a new token, unless AGT_APPEND is set */
if (!(flags & AGT_APPEND)) {
rv = lex_token_start(token);
}
/* Loop to read file */
tv = 0;
do {
/*
* If the AGT_NOSKIP flag is not set, skip whitespace (but not
* newline). If the flag is set, just get the next character.
*/
rv = lex_skip_over(lst, aclChTab, (flags & AGT_NOSKIP) ? 0 : CCM_WS);
if (rv <= 0) {
if (rv < 0) goto err_lex1;
/* Exit loop if EOF */
if (rv == 0) {
tv = TOKEN_EOF;
break;
}
}
/* Analyze character after whitespace */
switch (rv) {
case '\n': /* newline */
/* Keep count of lines as we're skipping whitespace */
acf->acf_lineno += 1;
(void)lex_next_char(lst, aclChTab, CCM_NL);
break;
case '#': /* Beginning of comment */
/* Skip to a newline if so */
rv = lex_skip_to(lst, aclChTab, CCM_NL);
break;
case ';': /* End of statement */
tv = TOKEN_EOS;
dospecial = 1;
break;
case '@': /* at sign */
tv = TOKEN_AT;
dospecial = 1;
break;
case '+': /* plus sign */
tv = TOKEN_PLUS;
dospecial = 1;
break;
case '*': /* asterisk */
tv = TOKEN_STAR;
dospecial = 1;
break;
case '.': /* period */
tv = TOKEN_PERIOD;
dospecial = 1;
break;
case ',': /* comma */
tv = TOKEN_COMMA;
dospecial = 1;
break;
case '(': /* left parenthesis */
tv = TOKEN_LPAREN;
dospecial = 1;
break;
case ')': /* right parenthesis */
tv = TOKEN_RPAREN;
dospecial = 1;
break;
case '{': /* left brace */
tv = TOKEN_LBRACE;
dospecial = 1;
break;
case '}': /* right brace */
tv = TOKEN_RBRACE;
dospecial = 1;
break;
case '\"': /* double quote */
case '\'': /* single quote */
/* Append string contents to token buffer */
rv = lex_scan_string(lst, token, 0);
tv = TOKEN_STRING;
break;
default:
/* Check for identifier, beginning with a letter */
if (lex_class_check(aclChTab, rv, CCM_LETTER)) {
/* Append valid identifier characters to token buffer */
rv = lex_scan_over(lst, aclChTab, CCM_IDENT, token);
tv = TOKEN_IDENT;
break;
}
/* Check for a number, beginning with a digit */
if (lex_class_check(aclChTab, rv, CCM_DIGIT)) {
char digit;
/* Save the first digit */
digit = (char)rv;
/* Append the first digit to the token */
rv = lex_token_append(token, 1, &digit);
/* Skip over the first digit */
rv = lex_next_char(lst, aclChTab, CCM_DIGIT);
/* If it's '0', we might have "0x.." */
if (rv == '0') {
/* Pick up the next character */
rv = lex_next_char(lst, aclChTab, 0);
/* Is it 'x'? */
if (rv == 'x') {
/* Yes, append it to the token */
digit = (char)rv;
rv = lex_token_append(token, 1, &digit);
/* Step over it */
rv = lex_next_char(lst, aclChTab, CCM_LETTER);
}
}
/* Get more digits, if any */
rv = lex_scan_over(lst, aclChTab, CCM_DIGIT, token);
tv = TOKEN_NUMBER;
break;
}
/* Unrecognized character */
spech = *lst->lst_cp;
lex_token_append(token, 1, &spech);
lst->lst_cp += 1;
lst->lst_len -= 1;
tv = TOKEN_HUH;
break;
}
/* Handle CCM_SPECIAL character? */
if (dospecial) {
/* Yes, clear the flag for next time */
dospecial = 0;
/* Get the character and advance past it */
rv = lex_next_char(lst, aclChTab, CCM_SPECIAL);
/* Append the character to the token buffer */
spech = (char)rv;
(void)lex_token_append(token, 1, &spech);
}
}
while ((tv == 0) && (rv > 0));
if (rv < 0) {
tv = TOKEN_ERROR;
}
else rv = tv;
acf->acf_ttype = tv;
return rv;
err_lex1:
rv = ACLERRPARSE;
eid = ACLERR2400;
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr);
acf->acf_ttype = TOKEN_ERROR;
return rv;
}
/*
* Description (aclParseInit)
*
* This function is called to initialize the ACL parser. It
* creates a LEX character class table to assist in parsing.
*
* Arguments:
*
* None.
*
* Returns:
*
* If successful, the return value is zero. An error is indicated
* by a negative return value.
*/
int aclParseInit()
{
int rv; /* result value */
/* Have we created the character class table yet? */
if (aclChTab == 0) {
/* No, initialize character classes for lexer processing */
rv = lex_class_create(classc, classv, &aclChTab);
if (rv < 0) goto err_nomem;
}
return 0;
err_nomem:
return ACLERRNOMEM;
}
/*
* Description (aclRealmSpecParse)
*
* This function parses an authentication realm specification. An
* authentication realm includes an authentication database and
* an authentication method. The syntax of a realm-spec is:
*
* realm-spec ::= "{" realm-directive-list "}" | "realm" realm-name
* realm-directive-list ::= realm-directive |
* realm-directive-list ";" realm-directive
* realm-directive ::= realm-db-directive | realm-meth-directive
* | realm-prompt-directive
* realm-db-directive ::= "database" db-file-path
* realm-meth-directive ::= "method" auth-method-name
* auth-method-name ::= "basic" | "SSL"
* realm-prompt-directive ::= "prompt" quote-char string quote-char
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* acc - pointer to ACContext_t structure
* rspp - pointer to RealmSpec_t pointer
*
* Returns:
*
* If successful, the return value is the token type of the token
* following the realm-spec, i.e. either the first token after a
* realm-name or the first token after the closing "}". It is the
* caller's responsibility to validate this token as a legitimate
* successor of a realm-spec. If a parsing error occurs in the
* middle of a realm-spec, the return value is ACLERRPARSE, and an
* error frame is generated if an error list is provided. For
* other kinds of errors a negative error code (from aclerror.h)
* is returned.
*/
int aclRealmSpecParse(NSErr_t * errp,
ACLFile_t * acf, ACContext_t * acc, RealmSpec_t **rspp)
{
void * token = acf->acf_token; /* handle for current token */
char * tokenstr; /* current token string */
RealmSpec_t * rsp; /* realm spec pointer */
RealmSpec_t * nrsp; /* named realm spec pointer */
int rv; /* result value */
int eid; /* error id value */
char linestr[16]; /* line number string buffer */
rv = acf->acf_ttype;
/* Is the current token a "{" ? */
if (rv != TOKEN_LBRACE) {
/* No, could it be KEYWORD_REALM? */
if (rv == TOKEN_IDENT) {
tokenstr = lex_token(token);
if (!strcasecmp(tokenstr, KEYWORD_REALM)) {
/* Yes, step to the realm name */
rv = aclGetToken(errp, acf, 0);
if (rv != TOKEN_IDENT) {
if (rv < 0) goto punt;
goto err_rlmname;
}
tokenstr = lex_token(token);
/* Look up the named realm specification */
rv = symTableFindSym(acc->acc_stp, tokenstr, ACLSYMREALM,
(void **)&nrsp);
if (rv < 0) goto err_undrlm;
/* Return the named realm specification */
*rspp = nrsp;
/* Step to the token after the realm name */
rv = aclGetToken(errp, acf, 0);
}
}
return rv;
}
/* Step to the token after the "{" */
rv = aclGetToken(errp, acf, 0);
if (rv < 0) goto punt;
rsp = *rspp;
if (rsp == 0) {
rsp = (RealmSpec_t *)MALLOC(sizeof(RealmSpec_t));
if (rsp == 0) goto err_nomem;
memset((void *)rsp, 0, sizeof(RealmSpec_t));
rsp->rs_sym.sym_type = ACLSYMREALM;
*rspp = rsp;
}
/* Loop for each realm-directive */
for (;; rv = aclGetToken(errp, acf, 0)) {
if (rv != TOKEN_IDENT) {
/* Exit loop on "}" */
if (rv == TOKEN_RBRACE) break;
/* Ignore null directives */
if (rv == TOKEN_EOS) continue;
/* Otherwise need an identifier to start a directive */
goto err_nodir;
}
tokenstr = lex_token(token);
/* Figure out which realm-directive this is */
if (!strcasecmp(tokenstr, KEYWORD_DATABASE)) {
/* Get a file specification for the database */
rv = aclGetToken(errp, acf, 0);
if (rv != TOKEN_STRING) {
if (rv < 0) goto punt;
goto err_nodb;
}
rsp->rs_realm.rlm_dbname = lex_token_take(token);
rsp->rs_realm.rlm_aif = &NSADB_AuthIF;
}
else if (!strcasecmp(tokenstr, KEYWORD_METHOD)) {
/* Step to the method identifier */
rv = aclGetToken(errp, acf, 0);
if (rv != TOKEN_IDENT) {
if (rv < 0) goto punt;
goto err_nometh;
}
tokenstr = lex_token(token);
/* Interpret method name and set method in realm structure */
if (!strcasecmp(tokenstr, KEYWORD_BASIC)) {
rsp->rs_realm.rlm_ameth = AUTH_METHOD_BASIC;
}
else if (!strcasecmp(tokenstr, KEYWORD_SSL) && server_enterprise) {
rsp->rs_realm.rlm_ameth = AUTH_METHOD_SSL;
}
else goto err_badmeth;
}
else if (!strcasecmp(tokenstr, KEYWORD_PROMPT)) {
/* Step to the realm prompt string */
rv = aclGetToken(errp, acf, 0);
if ((rv != TOKEN_STRING) && (rv != TOKEN_IDENT)) {
if (rv < 0) goto punt;
goto err_noprompt;
}
/* Reference a copy of the prompt string from the realm */
rsp->rs_realm.rlm_prompt = lex_token_take(token);
}
else goto err_baddir;
/* Get the token after the realm-directive */
rv = aclGetToken(errp, acf, 0);
/* Need a ";" to keep going */
if (rv != TOKEN_EOS) break;
}
if (rv != TOKEN_RBRACE) goto err_rbrace;
/* Get the token after the realm-spec */
rv = aclGetToken(errp, acf, 0);
punt:
return rv;
err_rlmname:
eid = ACLERR2500;
goto err_parse;
err_undrlm:
eid = ACLERR2520;
rv = ACLERRUNDEF;
goto err_sym;
err_nomem:
eid = ACLERR2540;
rv = ACLERRNOMEM;
goto ret_err;
err_nodir:
eid = ACLERR2560;
goto err_parse;
err_nodb:
eid = ACLERR2570;
goto err_parse;
err_nometh:
eid = ACLERR2580;
goto err_parse;
err_badmeth:
eid = ACLERR2600;
goto err_sym;
err_noprompt:
eid = ACLERR2605;
goto err_parse;
err_baddir:
eid = ACLERR2610;
goto err_sym;
err_rbrace:
eid = ACLERR2620;
goto err_parse;
err_sym:
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program,
3, acf->acf_filename, linestr, tokenstr);
goto punt;
err_parse:
rv = ACLERRPARSE;
ret_err:
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr);
goto punt;
}
/*
* Description (aclRightsParse)
*
* This function parse an access rights list. The syntax for an
* access rights list is:
*
* rights-list ::= "(" list-of-rights ")"
* list-of-rights ::= rights-elem | list-of-rights "," rights-elem
* rights-elem ::= right-name | "+" rights-def-name
* right-name ::= identifier
* rights-def-name ::= identifier
*
* An element of a rights list is either the name of a particular
* access right (e.g. Read), or the name associated with an
* external definition of an access rights list, preceded by "+"
* (e.g. +editor-rights). The list is enclosed in parentheses,
* and the elements are separated by commas.
*
* This function adds to a list of rights provided by the caller.
* Access rights are internally assigned unique integer identifiers,
* and a symbol table is maintained to map an access right name to
* its identifier.
*
* Arguments:
*
* errp - error frame list pointer (may be null)
* acf - pointer to ACLFile_t for ACL file
* acc - pointer to ACContext_t structure
* rights - pointer to rights list head
*
* Returns:
*
* The return value is a code identifying the next token if successful.
* End-of-stream is indicated by a return value of TOKEN_EOF. An error
* is indicated by a negative error code (ACLERRxxxx - see aclerror.h),
* and an error frame will be generated if an error list is provided.
*/
int aclRightsParse(NSErr_t * errp, ACLFile_t * acf, ACContext_t * acc,
RightSpec_t **rights)
{
void * token = acf->acf_token; /* LEX token handle */
char * ename; /* element name string pointer */
RightSpec_t * rsp; /* rights specification pointer */
RightSpec_t * nrsp; /* named rights spec pointer */
RightDef_t * rdp; /* right definition pointer */
int rv; /* result value */
int eid; /* error id */
char linestr[16]; /* line number string buffer */
/* Look for a left parenthesis */
if (acf->acf_ttype != TOKEN_LPAREN) {
/* No rights list present */
return 0;
}
rsp = *rights;
/* Create a RightSpec_t if we don't have one */
if (rsp == 0) {
rsp = (RightSpec_t *)MALLOC(sizeof(RightSpec_t));
if (rsp == 0) goto err_nomem1;
memset((void *)rsp, 0, sizeof(RightSpec_t));
rsp->rs_sym.sym_type = ACLSYMRDEF;
*rights = rsp;
}
/* Parse list elements */
for (;;) {
/* Look for an identifier */
rv = aclGetToken(errp, acf, 0);
if (rv != TOKEN_IDENT) {
/* No, maybe a "+" preceding a rights definition name? */
if (rv != TOKEN_PLUS) {
/* One more chance, we'll allow the closing ")" after "," */
if (rv != TOKEN_RPAREN) {
/* No, bad news */
if (rv < 0) goto punt;
goto err_elem;
}
/* Got right paren after comma */
break;
}
/* Got a "+", try for the rights definition name */
rv = aclGetToken(errp, acf, 0);
if (rv != TOKEN_IDENT) {
if (rv < 0) goto punt;
goto err_rdef;
}
/* Get a pointer to the token string */
ename = lex_token(token);
/* See if it matches a rights definition in the symbol table */
rv = symTableFindSym(acc->acc_stp, ename, ACLSYMRDEF,
(void **)&nrsp);
if (rv) goto err_undef;
/*
* Merge the rights from the named rights list into the
* current rights list.
*/
rv = uilMerge(&rsp->rs_list, &nrsp->rs_list);
if (rv < 0) goto err_nomem2;
}
else {
/* The current token is an access right name */
/* Get a pointer to the token string */
ename = lex_token(token);
/* Find or create an access right definition */
rv = aclRightDef(errp, acc, ename, &rdp);
if (rv < 0) goto err_radd;
/* Add the id for this right to the current rights list */
rv = usiInsert(&rsp->rs_list, rdp->rd_id);
if (rv < 0) goto err_nomem3;
}
rv = aclGetToken(errp, acf, 0);
/* Want a comma to continue the list */
if (rv != TOKEN_COMMA) {
/* A right parenthesis will end the list nicely */
if (rv == TOKEN_RPAREN) {
/* Get the first token after the rights list */
rv = aclGetToken(errp, acf, 0);
break;
}
/* Anything else is an error */
if (rv < 0) goto punt;
goto err_list;
}
}
return rv;
err_elem:
eid = ACLERR2700;
rv = ACLERRSYNTAX;
goto err_ret;
err_rdef:
eid = ACLERR2720;
rv = ACLERRSYNTAX;
goto err_ret;
err_undef:
eid = ACLERR2740;
rv = ACLERRUNDEF;
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program,
3, acf->acf_filename, linestr, ename);
return rv;
err_nomem1:
eid = ACLERR2760;
goto err_nomem;
err_nomem2:
eid = ACLERR2780;
goto err_nomem;
err_radd:
eid = ACLERR2800;
goto err_ret;
err_nomem3:
eid = ACLERR2820;
goto err_nomem;
err_nomem:
rv = ACLERRNOMEM;
goto err_ret;
err_list:
eid = ACLERR2840;
rv = ACLERRSYNTAX;
err_ret:
sprintf(linestr, "%d", acf->acf_lineno);
nserrGenerate(errp, rv, eid, ACL_Program, 2, acf->acf_filename, linestr);
punt:
return rv;
}
/*
* Description (aclStreamGet)
*
* This function is the stream read function designated by
* aclFileOpen() to read the file associated with the LEX stream
* it creates. It reads the next chunk of the file into the
* stream buffer.
*
* Arguments:
*
* lst - pointer to LEX stream structure
*
* Returns:
*
* The return value is the number of bytes read if successful.
* A return value of zero indicates end-of-file. An error is
* indicated by a negative return value.
*/
int aclStreamGet(LEXStream_t * lst)
{
SYS_FILE fd = (SYS_FILE)(lst->lst_strmid);
int len;
len = system_fread(fd, lst->lst_buf, lst->lst_buflen);
if (len >= 0) {
lst->lst_len = len;
lst->lst_cp = lst->lst_buf;
}
return len;
}