diff options
author | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
---|---|---|
committer | cvsadm <cvsadm> | 2005-01-21 00:44:34 +0000 |
commit | b2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch) | |
tree | cf58939393a9032182c4fbc4441164a9456e82f8 /lib/libaccess | |
download | ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.gz ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.xz ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.zip |
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'lib/libaccess')
106 files changed, 33248 insertions, 0 deletions
diff --git a/lib/libaccess/Makefile b/lib/libaccess/Makefile new file mode 100644 index 00000000..1ed3b58a --- /dev/null +++ b/lib/libaccess/Makefile @@ -0,0 +1,176 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# Makefile for libaccess.a +# +MCOM_ROOT=../../.. +MODULE=LibAccess +include ../../nsdefs.mk + +OBJDEST=$(OBJDIR)/lib/libaccess +UTESTDEST=$(OBJDIR)/lib/libaccess/utest +LEX=flex + +include ../../nsconfig.mk + +MCC_INCLUDE += $(ADMINUTIL_INCLUDE) + +ifeq ($(ARCH), WINNT) +LIBS=$(OBJDIR)/lib/libaccess.lib +CC=cl -nologo -MT +VALUES=$(OBJDEST)/values.h +else +VALUES= +LIBS=$(OBJDIR)/lib/libaccess.a +endif + +all: $(OBJDEST) $(LIBS) + +$(OBJDEST): + mkdir -p $(OBJDEST) + +$(UTESTDEST): + mkdir -p $(UTESTDEST) + +OSOBJS = + +OBJS=$(addprefix $(OBJDEST)/, usi.o \ + attrec.o \ + nseframe.o \ + nsdb.o \ + nsdbmgmt.o \ + nsuser.o \ + nsumgmt.o \ + nsgroup.o \ + nsgmgmt.o \ + nsadb.o \ + nscert.o \ + nsamgmt.o \ + nsautherr.o \ + symbols.o \ + acltools.o \ + aclutil.o \ + aclcache.o \ + aclflush.o \ + authdb.o \ + method.o \ + ldapacl.o \ + register.o \ + lasdns.o \ + lasip.o \ + lastod.o \ + usrcache.o \ + lasgroup.o \ + lasuser.o \ + lasprogram.o \ + aclspace.o \ + acl.tab.o \ + acl.yy.o \ + acleval.o \ + oneeval.o \ + access_plhash.o \ + aclparse.o \ + aclbuild.o \ + aclerror.o \ + acladmin.o \ + aclcgi.o \ + $(OSOBJS) \ + ) + +# +# AVA Mapping files. Currently not compiled in (FORTEZZA for reference only). +# +AVAMAPFILES = lex.yy.o y.tab.o avapfile.o avadb.o + +MODULE_CFLAGS=-I$(NSROOT)/include -DACL_LIB_INTERNAL $(TESTFLAGS) + +ifeq ($(LDAP_NO_LIBLCACHE),1) +MODULE_CFLAGS+=-DNO_LIBLCACHE +endif + +LOCAL_DEPS = $(LDAPSDK_DEP) + +$(LIBS): $(LOCAL_DEPS) $(OBJS) + rm -f $@ + $(AR) $(OBJS) + $(RANLIB) $@ + +include $(INCLUDE_DEPENDS) + +# +# acl.tab.c acl.tab.h and acl.yy.c should not be generated by the build, +# they are checked in and should be pulled from the tree by +# default. The following rules are provided in case the grammar or +# lexer needs changes. +# + +# +# Right now it's best to run yacc on a Solaris machine because the +# /usr/lib/yaccpar makes the NT compiler happier. It should work on +# other UNIX systems -- but that's what is checked in and tested. +# +yacc: + $(YACC) -d acltext.y + sed -f yy-sed y.tab.h > acl.tab.h + sed -f yy-sed y.tab.c > acl.tab.cpp + rm y.tab.h y.tab.c + +# +# Flex generates a case insenitive lexer. It also provides mechanisms +# that allow NSPR to replace it's standard IO routines. The standard UNIX +# lex wants to use some stupid library! One would think that lex could +# generate all the code it needs just like FLEX. +# +flex: + $(LEX) -i aclscan.l + sed -f yy-sed lex.yy.c > acl.yy.cpp + rm lex.yy.c + +# +# more AVA mapping stuff, needs to be made to work with the other lexx/yacc +# code added for 3.0 +# +#ifeq ($(ARCH), WINNT) +#$(OBJDEST)/y.tab.o: $(OBJDEST)/y.tab.c $(VALUES) $(OBJDEST)/y.tab.h +# $(CC) -c $(CFLAGS) $(MCC_INCLUDE) -I$(OBJDEST) -I. $< -Fo$*.o +#else +#$(OBJDEST)/y.tab.o: $(OBJDEST)/y.tab.c $(OBJDEST)/y.tab.h +# $(CC) -c $(CFLAGS) $(MCC_INCLUDE) -I. -o $*.o $< +#endif +# +#ifeq ($(ARCH), WINNT) +#$(OBJDEST)/y.tab.h: wintab.h +# cp wintab.h $(OBJDEST)/y.tab.h +# +#$(OBJDEST)/y.tab.c: winnt.y +# cp winnt.y $(OBJDEST)/y.tab.c +# +#$(OBJDEST)/values.h: winnt.v +# cp winnt.v $(OBJDEST)/values.h +#else +#$(OBJDEST)/y.tab.h $(OBJDEST)/y.tab.c: avaparse.y +# yacc -d avaparse.y +# mv y.tab.h $(OBJDEST)/ +# mv y.tab.c $(OBJDEST)/ +#endif +# +#$(OBJDEST)/lex.yy.o: $(OBJDEST)/lex.yy.c $(OBJDEST)/y.tab.h +#ifeq ($(ARCH), WINNT) +# $(CC) -c $(CFLAGS) $(MCC_INCLUDE) -I$(OBJDEST) -I. $< -Fo$*.o +#else +# $(CC) -c $(CFLAGS) $(MCC_INCLUDE) -I. -o $*.o $< +#endif +# +#ifeq ($(ARCH), WINNT) +#$(OBJDEST)/lex.yy.c: winnt.l +# cp winnt.l $(OBJDEST)/lex.yy.c +#else +#$(OBJDEST)/lex.yy.c: avascan.l +# lex avascan.l +# mv lex.yy.c $(OBJDEST)/ +#endif diff --git a/lib/libaccess/access_plhash.cpp b/lib/libaccess/access_plhash.cpp new file mode 100644 index 00000000..7666e95e --- /dev/null +++ b/lib/libaccess/access_plhash.cpp @@ -0,0 +1,65 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + This file contains a function which augments the standard nspr PL_HashTable + api. The problem is that the hash table lookup function in the standard NSPR + actually modifies the hash table being searched, which means that it cannot be + used with read locks in a multi threaded environment. This function is a + lookup function which is guaranteed not to modify the hash table passed in, + so that it can be used with read locks. +*/ + +#include "plhash.h" + +/* prototypes */ +NSPR_BEGIN_EXTERN_C +PR_IMPLEMENT(void *) +ACL_HashTableLookup_const(PLHashTable *ht, const void *key); +NSPR_END_EXTERN_C + +/* +** Multiplicative hash, from Knuth 6.4. +*/ +#define GOLDEN_RATIO 0x9E3779B9U + +PR_IMPLEMENT(PLHashEntry **) +ACL_HashTableRawLookup_const(PLHashTable *ht, PLHashNumber keyHash, const void *key) +{ + PLHashEntry *he, **hep; + PLHashNumber h; + +#ifdef HASHMETER + ht->nlookups++; +#endif + h = keyHash * GOLDEN_RATIO; + h >>= ht->shift; + hep = &ht->buckets[h]; + while ((he = *hep) != 0) { + if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) { + return hep; + } + hep = &he->next; +#ifdef HASHMETER + ht->nsteps++; +#endif + } + return hep; +} + +PR_IMPLEMENT(void *) +ACL_HashTableLookup_const(PLHashTable *ht, const void *key) +{ + PLHashNumber keyHash; + PLHashEntry *he, **hep; + + keyHash = (*ht->keyHash)(key); + hep = ACL_HashTableRawLookup_const(ht, keyHash, key); + if ((he = *hep) != 0) { + return he->value; + } + return 0; +} diff --git a/lib/libaccess/access_plhash.h b/lib/libaccess/access_plhash.h new file mode 100644 index 00000000..fb1d86a1 --- /dev/null +++ b/lib/libaccess/access_plhash.h @@ -0,0 +1,17 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +NSPR_BEGIN_EXTERN_C +/* + * access_plhash.cpp - supplement to NSPR plhash + */ +extern void * +ACL_HashTableLookup_const( + void *ht, /* really a PLHashTable */ + const void *key); + +NSPR_END_EXTERN_C + diff --git a/lib/libaccess/acl.tab.cpp b/lib/libaccess/acl.tab.cpp new file mode 100644 index 00000000..bc17be3d --- /dev/null +++ b/lib/libaccess/acl.tab.cpp @@ -0,0 +1,1718 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +# line 8 "acltext.y" +#include <string.h> +#include <netsite.h> +#include <base/util.h> +#include <base/plist.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/nserror.h> +#include "parse.h" +#include "aclscan.h" + +#define MAX_LIST_SIZE 255 +static ACLListHandle_t *curr_acl_list; /* current acl list */ +static ACLHandle_t *curr_acl; /* current acl */ +static ACLExprHandle_t *curr_expr; /* current expression */ +static PFlags_t pflags; /* current authorization flags */ +static char *curr_args_list[MAX_LIST_SIZE]; /* current args */ +static char *curr_user_list[MAX_LIST_SIZE]; /* current users v2 */ +static char *curr_ip_dns_list[MAX_LIST_SIZE]; /* current ip/dns v2 */ +static PList_t curr_auth_info; /* current authorization method */ +static int use_generic_rights; /* use generic rights for conversion */ + +int acl_PushListHandle(ACLListHandle_t *handle) +{ + curr_acl_list = handle; + return(0); +} + +static void +acl_string_lower(char *s) +{ +int ii; +int len; + + len = strlen(s); + for (ii = 0; ii < len; ii++) + s[ii] = tolower(s[ii]); + + return; +} + +static void +acl_clear_args(char **args_list) +{ + args_list[0] = NULL; +} + +static void +acl_add_arg(char **args_list, char *arg) +{ + static int args_index; + + if ( args_list[0] == NULL ) { + args_index = 0; + } + args_list[args_index] = arg; + args_index++; + args_list[args_index] = NULL; +} + +static void +acl_free_args(char **args_list) +{ + int ii; + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( args_list[ii] ) + PERM_FREE(args_list[ii]); + else + break; + } +} + +static int +acl_set_args(ACLExprHandle_t *expr, char **args_list) +{ + int ii; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( args_list[ii] ) { + if ( ACL_ExprAddArg(NULL, expr, args_list[ii]) < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + } else + break; + } + return(0); +} + +static int +acl_set_users(ACLExprHandle_t *expr, char **user_list) +{ + int ii; + int jj; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( user_list[ii] ) { + if ( ACL_ExprTerm(NULL, expr, "user", CMP_OP_EQ, + user_list[ii]) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + } else + break; + } + + acl_free_args(user_list); + + for (jj = 0; jj < ii - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + aclerror("ACL_ExprOr() failed"); + return(-1); + } + } + return(0); +} + +static int +acl_set_users_or_groups(ACLExprHandle_t *expr, char **user_list) +{ + int ii; + int jj; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( user_list[ii] ) { + if ( ACL_ExprTerm(NULL, expr, "user", CMP_OP_EQ, + user_list[ii]) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + if ( ACL_ExprTerm(NULL, expr, "group", CMP_OP_EQ, + user_list[ii]) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + } else + break; + } + + acl_free_args(user_list); + + for (jj = 0; jj < (ii * 2) - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + aclerror("ACL_ExprOr() failed"); + return(-1); + } + } + return(0); +} + +static int +acl_set_ip_dns(ACLExprHandle_t *expr, char **ip_dns) +{ + int ii; + int jj; + int len; + char *attr; + char *val; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( ip_dns[ii] ) { + + attr = "ip"; + val = ip_dns[ii]; + len = strlen(val); + + for (jj = 0; jj < len; jj++) { + if ( strchr("0123456789.*", val[jj]) == 0 ) { + attr = "dns"; + break; + } + } + + if ( ACL_ExprTerm(NULL, expr, attr, CMP_OP_EQ, + val) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + acl_free_args(ip_dns); + return(-1); + } + + } else + break; + } + + acl_free_args(ip_dns); + + for (jj = 0; jj < ii - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + aclerror("ACL_ExprOr() failed"); + return(-1); + } + } + + return(0); +} + + + +# line 223 "acltext.y" +typedef union +#ifdef __cplusplus + ACLSTYPE +#endif + { + char *string; + int ival; +} ACLSTYPE; +# define ACL_ABSOLUTE_TOK 257 +# define ACL_ACL_TOK 258 +# define ACL_ALLOW_TOK 259 +# define ACL_ALWAYS_TOK 260 +# define ACL_AND_TOK 261 +# define ACL_AT_TOK 262 +# define ACL_AUTHENTICATE_TOK 263 +# define ACL_CONTENT_TOK 264 +# define ACL_DEFAULT_TOK 265 +# define ACL_DENY_TOK 266 +# define ACL_GROUP_TOK 267 +# define ACL_IN_TOK 268 +# define ACL_INHERIT_TOK 269 +# define ACL_NOT_TOK 270 +# define ACL_NULL_TOK 271 +# define ACL_OR_TOK 272 +# define ACL_QSTRING_TOK 273 +# define ACL_READ_TOK 274 +# define ACL_TERMINAL_TOK 275 +# define ACL_VARIABLE_TOK 276 +# define ACL_VERSION_TOK 277 +# define ACL_WRITE_TOK 278 +# define ACL_WITH_TOK 279 +# define ACL_EQ_TOK 280 +# define ACL_GE_TOK 281 +# define ACL_GT_TOK 282 +# define ACL_LE_TOK 283 +# define ACL_LT_TOK 284 +# define ACL_NE_TOK 285 + +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#else +#include <netsite.h> +#include <memory.h> +#endif + + +#ifdef __cplusplus + +#ifndef aclerror + void aclerror(const char *); +#endif + +#ifndef acllex +#ifdef __EXTERN_C__ + extern "C" { int acllex(void); } +#else + int acllex(void); +#endif +#endif + int acl_Parse(void); + +#endif +#define aclclearin aclchar = -1 +#define aclerrok aclerrflag = 0 +extern int aclchar; +extern int aclerrflag; +ACLSTYPE acllval; +ACLSTYPE aclval; +typedef int acltabelem; +#ifndef ACLMAXDEPTH +#define ACLMAXDEPTH 150 +#endif +#if ACLMAXDEPTH > 0 +int acl_acls[ACLMAXDEPTH], *acls = acl_acls; +ACLSTYPE acl_aclv[ACLMAXDEPTH], *aclv = acl_aclv; +#else /* user does initial allocation */ +int *acls; +ACLSTYPE *aclv; +#endif +static int aclmaxdepth = ACLMAXDEPTH; +# define ACLERRCODE 256 + +# line 952 "acltext.y" + +acltabelem aclexca[] ={ +-1, 1, + 0, -1, + -2, 0, + }; +# define ACLNPROD 120 +# define ACLLAST 251 +acltabelem aclact[]={ + + 176, 177, 178, 180, 179, 181, 156, 109, 69, 53, + 160, 116, 76, 6, 185, 169, 118, 186, 170, 117, + 150, 78, 85, 149, 77, 18, 144, 29, 17, 86, + 28, 11, 3, 126, 10, 136, 140, 82, 89, 104, + 87, 101, 7, 129, 127, 171, 133, 79, 72, 40, + 132, 38, 102, 55, 108, 37, 172, 105, 39, 60, + 60, 107, 128, 63, 59, 45, 61, 61, 93, 23, + 46, 6, 131, 130, 158, 142, 137, 157, 125, 134, + 154, 147, 56, 122, 112, 30, 75, 94, 81, 111, + 139, 138, 88, 73, 165, 164, 155, 57, 50, 49, + 48, 27, 14, 41, 65, 58, 145, 97, 153, 146, + 98, 152, 120, 25, 184, 151, 119, 24, 99, 64, + 13, 32, 15, 21, 5, 175, 159, 106, 103, 8, + 100, 124, 84, 83, 66, 54, 52, 143, 80, 51, + 67, 90, 36, 35, 26, 34, 33, 22, 31, 20, + 135, 113, 62, 74, 96, 47, 92, 71, 44, 68, + 43, 70, 42, 95, 16, 91, 9, 4, 19, 12, + 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 110, 115, 114, 121, 123, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 95, 141, 0, + 0, 0, 0, 0, 0, 148, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 163, 0, 0, 0, 166, + 167, 168, 0, 0, 0, 0, 174, 0, 173, 0, + 161, 0, 0, 0, 118, 78, 162, 117, 77, 182, + 183 }; +acltabelem aclpact[]={ + + -245,-10000000,-10000000, -234, -187,-10000000, -242,-10000000,-10000000, 80, +-10000000,-10000000, 43, -248, -189, 76, 69,-10000000,-10000000,-10000000, + -189,-10000000, 42, -246, -38, -248,-10000000, -208,-10000000,-10000000, + -195,-10000000,-10000000, -208, 41, 40, 39,-10000000,-10000000, -270, + -213, -43, 38,-10000000,-10000000, -199, -200,-10000000,-10000000,-10000000, +-10000000, 79,-10000000,-10000000,-10000000, -271,-10000000, -195,-10000000, -220, +-10000000,-10000000, -28, -221, -239,-10000000, -235, -238,-10000000,-10000000, +-10000000, -28,-10000000,-10000000, -194,-10000000, -252,-10000000,-10000000,-10000000, + 66,-10000000,-10000000,-10000000, 78, -223, -218, -203,-10000000, -273, + -238,-10000000, -39, -29, 75, 68, -39, -40, -239, -243, +-10000000, -231, -202,-10000000, -232, -184,-10000000, -185, -214, -227, +-10000000,-10000000, -241,-10000000,-10000000,-10000000, -257, -240,-10000000,-10000000, + -252,-10000000, -250,-10000000, 65,-10000000,-10000000,-10000000,-10000000,-10000000, +-10000000,-10000000,-10000000,-10000000, -44, -241, -253, 74, 67, 64, +-10000000,-10000000, -45, 37, -274, -30, -243,-10000000,-10000000, 36, + 35,-10000000, -257, -257,-10000000, -250, -258,-10000000, -216,-10000000, + -30, -30, -280,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000, +-10000000, -30, -30, 73,-10000000, -259,-10000000,-10000000,-10000000,-10000000, +-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000 }; +acltabelem aclpgo[]={ + + 0, 171, 170, 169, 168, 167, 124, 166, 122, 103, + 164, 162, 160, 158, 105, 157, 93, 156, 89, 154, + 153, 151, 86, 87, 91, 90, 76, 79, 150, 149, + 123, 147, 121, 146, 145, 143, 142, 141, 92, 140, + 139, 138, 75, 88, 137, 136, 104, 135, 134, 133, + 132, 131, 77, 130, 128, 127, 78, 74, 126, 125 }; +acltabelem aclr1[]={ + + 0, 1, 1, 3, 1, 2, 5, 5, 6, 7, + 7, 8, 8, 10, 10, 9, 9, 11, 11, 15, + 13, 13, 14, 14, 17, 12, 19, 12, 16, 16, + 20, 20, 23, 23, 22, 22, 21, 21, 21, 24, + 24, 25, 26, 26, 26, 26, 18, 28, 28, 27, + 27, 4, 29, 29, 30, 30, 31, 31, 32, 32, + 33, 33, 33, 37, 36, 39, 36, 38, 40, 34, + 41, 41, 43, 42, 42, 44, 44, 45, 35, 47, + 35, 48, 46, 49, 50, 50, 50, 50, 50, 50, + 50, 55, 55, 55, 55, 53, 53, 53, 53, 54, + 54, 54, 54, 51, 51, 56, 52, 52, 52, 57, + 57, 57, 58, 58, 59, 59, 59, 59, 59, 59 }; +acltabelem aclr2[]={ + + 0, 0, 2, 1, 10, 2, 2, 4, 17, 3, + 3, 2, 6, 3, 3, 4, 6, 2, 2, 1, + 8, 6, 3, 3, 1, 10, 1, 10, 7, 3, + 2, 6, 2, 6, 3, 3, 2, 2, 6, 3, + 3, 5, 2, 2, 6, 6, 7, 7, 7, 2, + 4, 2, 2, 4, 6, 4, 5, 5, 2, 4, + 4, 4, 4, 1, 10, 1, 8, 7, 1, 17, + 2, 6, 3, 4, 6, 7, 7, 1, 6, 1, + 6, 1, 5, 10, 0, 3, 5, 3, 5, 3, + 5, 3, 3, 5, 5, 3, 3, 5, 5, 3, + 3, 5, 5, 2, 6, 3, 2, 7, 7, 2, + 6, 5, 7, 7, 2, 2, 2, 2, 2, 2 }; +acltabelem aclchk[]={ + +-10000000, -1, -2, 277, -5, -6, 258, 276, -6, -7, + 276, 273, -3, 40, 59, -8, -10, 276, 273, -4, + -29, -30, -31, 258, 41, 44, -30, 59, 276, 273, + 123, -8, -32, -33, -34, -35, -36, 263, 259, 266, + 257, -9, -11, -12, -13, 260, 265, -32, 59, 59, + 59, -40, -45, 279, -47, 266, 125, 59, -14, 263, + 259, 266, -14, 263, 40, -46, -48, -39, -46, 279, + -9, -15, 268, -16, -20, -22, 40, 276, 273, 268, + -41, -43, 276, -49, -50, 257, 264, 275, -38, 276, + -37, -16, -17, 262, -23, -22, -19, 41, 44, 40, + -53, 264, 275, -54, 257, 275, -55, 264, 257, 280, + -38, -18, 123, -21, -24, -25, 40, 276, 273, 41, + 44, -18, 123, -43, -51, -56, 276, 275, 264, 275, + 257, 257, 264, 273, -27, -28, 276, -26, -24, -25, + 276, -23, -42, -44, 276, 41, 44, 125, -27, 276, + 273, 41, 44, 44, 125, 59, 280, -52, -57, -58, + 40, 270, 276, -56, 59, 59, -26, -26, -42, 273, + 276, 261, 272, -52, -57, -59, 280, 281, 282, 284, + 283, 285, -52, -52, 41, 273, 276 }; +acltabelem acldef[]={ + + 1, -2, 2, 0, 5, 6, 0, 3, 7, 0, + 9, 10, 0, 0, 0, 0, 11, 13, 14, 4, + 51, 52, 0, 0, 0, 0, 53, 55, 56, 57, + 0, 12, 54, 58, 0, 0, 0, 68, 77, 79, + 0, 0, 0, 17, 18, 0, 0, 59, 60, 61, + 62, 0, 81, 65, 81, 0, 8, 15, 19, 0, + 22, 23, 0, 0, 0, 78, 84, 0, 80, 63, + 16, 0, 24, 21, 29, 30, 0, 34, 35, 26, + 0, 70, 72, 82, 0, 85, 87, 89, 66, 0, + 0, 20, 0, 0, 0, 32, 0, 0, 0, 0, + 86, 95, 96, 88, 99, 100, 90, 91, 92, 0, + 64, 25, 0, 28, 36, 37, 0, 39, 40, 31, + 0, 27, 0, 71, 0, 103, 105, 97, 98, 101, + 102, 93, 94, 67, 0, 49, 0, 0, 42, 43, + 41, 33, 0, 0, 0, 0, 0, 46, 50, 0, + 0, 38, 0, 0, 69, 73, 0, 83, 106, 109, + 0, 0, 0, 104, 47, 48, 44, 45, 74, 75, + 76, 0, 0, 0, 111, 0, 114, 115, 116, 117, + 118, 119, 107, 108, 110, 112, 113 }; +typedef struct +#ifdef __cplusplus + acltoktype +#endif +{ char *t_name; int t_val; } acltoktype; +#ifndef ACLDEBUG +# define ACLDEBUG 0 /* don't allow debugging */ +#endif + +#if ACLDEBUG + +acltoktype acltoks[] = +{ + "ACL_ABSOLUTE_TOK", 257, + "ACL_ACL_TOK", 258, + "ACL_ALLOW_TOK", 259, + "ACL_ALWAYS_TOK", 260, + "ACL_AND_TOK", 261, + "ACL_AT_TOK", 262, + "ACL_AUTHENTICATE_TOK", 263, + "ACL_CONTENT_TOK", 264, + "ACL_DEFAULT_TOK", 265, + "ACL_DENY_TOK", 266, + "ACL_GROUP_TOK", 267, + "ACL_IN_TOK", 268, + "ACL_INHERIT_TOK", 269, + "ACL_NOT_TOK", 270, + "ACL_NULL_TOK", 271, + "ACL_OR_TOK", 272, + "ACL_QSTRING_TOK", 273, + "ACL_READ_TOK", 274, + "ACL_TERMINAL_TOK", 275, + "ACL_VARIABLE_TOK", 276, + "ACL_VERSION_TOK", 277, + "ACL_WRITE_TOK", 278, + "ACL_WITH_TOK", 279, + "ACL_EQ_TOK", 280, + "ACL_GE_TOK", 281, + "ACL_GT_TOK", 282, + "ACL_LE_TOK", 283, + "ACL_LT_TOK", 284, + "ACL_NE_TOK", 285, + "-unknown-", -1 /* ends search */ +}; + +char * aclreds[] = +{ + "-no such reduction-", + "start : /* empty */", + "start : start_acl_v2", + "start : ACL_VERSION_TOK ACL_VARIABLE_TOK", + "start : ACL_VERSION_TOK ACL_VARIABLE_TOK ';' start_acl_v3", + "start_acl_v2 : acl_list_v2", + "acl_list_v2 : acl_v2", + "acl_list_v2 : acl_list_v2 acl_v2", + "acl_v2 : ACL_ACL_TOK acl_name_v2 '(' arg_list_v2 ')' '{' directive_list_v2 '}'", + "acl_name_v2 : ACL_VARIABLE_TOK", + "acl_name_v2 : ACL_QSTRING_TOK", + "arg_list_v2 : arg_v2", + "arg_list_v2 : arg_v2 ',' arg_list_v2", + "arg_v2 : ACL_VARIABLE_TOK", + "arg_v2 : ACL_QSTRING_TOK", + "directive_list_v2 : directive_v2 ';'", + "directive_list_v2 : directive_v2 ';' directive_list_v2", + "directive_v2 : auth_method_v2", + "directive_v2 : auth_statement_v2", + "auth_statement_v2 : ACL_ALWAYS_TOK auth_type_v2", + "auth_statement_v2 : ACL_ALWAYS_TOK auth_type_v2 host_spec_list_action_v2", + "auth_statement_v2 : ACL_DEFAULT_TOK auth_type_v2 host_spec_list_action_v2", + "auth_type_v2 : ACL_ALLOW_TOK", + "auth_type_v2 : ACL_DENY_TOK", + "auth_method_v2 : ACL_ALWAYS_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK", + "auth_method_v2 : ACL_ALWAYS_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK realm_definition_v2", + "auth_method_v2 : ACL_DEFAULT_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK", + "auth_method_v2 : ACL_DEFAULT_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK realm_definition_v2", + "host_spec_list_action_v2 : user_expr_v2 ACL_AT_TOK host_spec_list_v2", + "host_spec_list_action_v2 : user_expr_v2", + "user_expr_v2 : user_v2", + "user_expr_v2 : '(' user_list_v2 ')'", + "user_list_v2 : user_v2", + "user_list_v2 : user_v2 ',' user_list_v2", + "user_v2 : ACL_VARIABLE_TOK", + "user_v2 : ACL_QSTRING_TOK", + "host_spec_list_v2 : dns_spec_v2", + "host_spec_list_v2 : ip_spec_v2", + "host_spec_list_v2 : '(' dns_ip_spec_list_v2 ')'", + "dns_spec_v2 : ACL_VARIABLE_TOK", + "dns_spec_v2 : ACL_QSTRING_TOK", + "ip_spec_v2 : ACL_VARIABLE_TOK ACL_VARIABLE_TOK", + "dns_ip_spec_list_v2 : dns_spec_v2", + "dns_ip_spec_list_v2 : ip_spec_v2", + "dns_ip_spec_list_v2 : dns_spec_v2 ',' dns_ip_spec_list_v2", + "dns_ip_spec_list_v2 : ip_spec_v2 ',' dns_ip_spec_list_v2", + "realm_definition_v2 : '{' methods_list_v2 '}'", + "method_v2 : ACL_VARIABLE_TOK ACL_VARIABLE_TOK ';'", + "method_v2 : ACL_VARIABLE_TOK ACL_QSTRING_TOK ';'", + "methods_list_v2 : method_v2", + "methods_list_v2 : method_v2 methods_list_v2", + "start_acl_v3 : acl_list", + "acl_list : acl", + "acl_list : acl_list acl", + "acl : named_acl ';' body_list", + "acl : named_acl ';'", + "named_acl : ACL_ACL_TOK ACL_VARIABLE_TOK", + "named_acl : ACL_ACL_TOK ACL_QSTRING_TOK", + "body_list : body", + "body_list : body body_list", + "body : authenticate_statement ';'", + "body : authorization_statement ';'", + "body : deny_statement ';'", + "deny_statement : ACL_ABSOLUTE_TOK ACL_DENY_TOK ACL_WITH_TOK", + "deny_statement : ACL_ABSOLUTE_TOK ACL_DENY_TOK ACL_WITH_TOK deny_common", + "deny_statement : ACL_DENY_TOK ACL_WITH_TOK", + "deny_statement : ACL_DENY_TOK ACL_WITH_TOK deny_common", + "deny_common : ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK", + "authenticate_statement : ACL_AUTHENTICATE_TOK", + "authenticate_statement : ACL_AUTHENTICATE_TOK '(' attribute_list ')' '{' parameter_list '}'", + "attribute_list : attribute", + "attribute_list : attribute_list ',' attribute", + "attribute : ACL_VARIABLE_TOK", + "parameter_list : parameter ';'", + "parameter_list : parameter ';' parameter_list", + "parameter : ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK", + "parameter : ACL_VARIABLE_TOK ACL_EQ_TOK ACL_VARIABLE_TOK", + "authorization_statement : ACL_ALLOW_TOK", + "authorization_statement : ACL_ALLOW_TOK auth_common_action", + "authorization_statement : ACL_DENY_TOK", + "authorization_statement : ACL_DENY_TOK auth_common_action", + "auth_common_action : /* empty */", + "auth_common_action : auth_common", + "auth_common : flag_list '(' args_list ')' expression", + "flag_list : /* empty */", + "flag_list : ACL_ABSOLUTE_TOK", + "flag_list : ACL_ABSOLUTE_TOK content_static", + "flag_list : ACL_CONTENT_TOK", + "flag_list : ACL_CONTENT_TOK absolute_static", + "flag_list : ACL_TERMINAL_TOK", + "flag_list : ACL_TERMINAL_TOK content_absolute", + "content_absolute : ACL_CONTENT_TOK", + "content_absolute : ACL_ABSOLUTE_TOK", + "content_absolute : ACL_CONTENT_TOK ACL_ABSOLUTE_TOK", + "content_absolute : ACL_ABSOLUTE_TOK ACL_CONTENT_TOK", + "content_static : ACL_CONTENT_TOK", + "content_static : ACL_TERMINAL_TOK", + "content_static : ACL_CONTENT_TOK ACL_TERMINAL_TOK", + "content_static : ACL_TERMINAL_TOK ACL_CONTENT_TOK", + "absolute_static : ACL_ABSOLUTE_TOK", + "absolute_static : ACL_TERMINAL_TOK", + "absolute_static : ACL_ABSOLUTE_TOK ACL_TERMINAL_TOK", + "absolute_static : ACL_TERMINAL_TOK ACL_ABSOLUTE_TOK", + "args_list : arg", + "args_list : args_list ',' arg", + "arg : ACL_VARIABLE_TOK", + "expression : factor", + "expression : factor ACL_AND_TOK expression", + "expression : factor ACL_OR_TOK expression", + "factor : base_expr", + "factor : '(' expression ')'", + "factor : ACL_NOT_TOK factor", + "base_expr : ACL_VARIABLE_TOK relop ACL_QSTRING_TOK", + "base_expr : ACL_VARIABLE_TOK relop ACL_VARIABLE_TOK", + "relop : ACL_EQ_TOK", + "relop : ACL_GE_TOK", + "relop : ACL_GT_TOK", + "relop : ACL_LT_TOK", + "relop : ACL_LE_TOK", + "relop : ACL_NE_TOK", +}; +#endif /* ACLDEBUG */ + + +/* +** Skeleton parser driver for yacc output +*/ + +/* +** yacc user known macros and defines +*/ +#define ACLERROR goto aclerrlab +#define ACLACCEPT return(0) +#define ACLABORT return(1) +#define ACLBACKUP( newtoken, newvalue )\ +{\ + if ( aclchar >= 0 || ( aclr2[ acltmp ] >> 1 ) != 1 )\ + {\ + aclerror( "syntax error - cannot backup" );\ + goto aclerrlab;\ + }\ + aclchar = newtoken;\ + aclstate = *aclps;\ + acllval = newvalue;\ + goto aclnewstate;\ +} +#define ACLRECOVERING() (!!aclerrflag) +#define ACLNEW(type) PERM_MALLOC(sizeof(type) * aclnewmax) +#define ACLCOPY(to, from, type) \ + (type *) memcpy(to, (char *) from, aclnewmax * sizeof(type)) +#define ACLENLARGE( from, type) \ + (type *) PERM_REALLOC((char *) from, aclnewmax * sizeof(type)) +#ifndef ACLDEBUG +# define ACLDEBUG 1 /* make debugging available */ +#endif + +/* +** user known globals +*/ +int acldebug; /* set to 1 to get debugging */ + +/* +** driver internal defines +*/ +#define ACLFLAG (-10000000) + +/* +** global variables used by the parser +*/ +ACLSTYPE *aclpv; /* top of value stack */ +int *aclps; /* top of state stack */ + +int aclstate; /* current state */ +int acltmp; /* extra var (lasts between blocks) */ + +int aclnerrs; /* number of errors */ +int aclerrflag; /* error recovery flag */ +int aclchar; /* current input token number */ + + + +#ifdef ACLNMBCHARS +#define ACLLEX() aclcvtok(acllex()) +/* +** aclcvtok - return a token if i is a wchar_t value that exceeds 255. +** If i<255, i itself is the token. If i>255 but the neither +** of the 30th or 31st bit is on, i is already a token. +*/ +#if defined(__STDC__) || defined(__cplusplus) +int aclcvtok(int i) +#else +int aclcvtok(i) int i; +#endif +{ + int first = 0; + int last = ACLNMBCHARS - 1; + int mid; + wchar_t j; + + if(i&0x60000000){/*Must convert to a token. */ + if( aclmbchars[last].character < i ){ + return i;/*Giving up*/ + } + while ((last>=first)&&(first>=0)) {/*Binary search loop*/ + mid = (first+last)/2; + j = aclmbchars[mid].character; + if( j==i ){/*Found*/ + return aclmbchars[mid].tvalue; + }else if( j<i ){ + first = mid + 1; + }else{ + last = mid -1; + } + } + /*No entry in the table.*/ + return i;/* Giving up.*/ + }else{/* i is already a token. */ + return i; + } +} +#else/*!ACLNMBCHARS*/ +#define ACLLEX() acllex() +#endif/*!ACLNMBCHARS*/ + +/* +** acl_Parse - return 0 if worked, 1 if syntax error not recovered from +*/ +#if defined(__STDC__) || defined(__cplusplus) +int acl_Parse(void) +#else +int acl_Parse() +#endif +{ + register ACLSTYPE *aclpvt; /* top of value stack for $vars */ + +#if defined(__cplusplus) || defined(lint) +/* + hacks to please C++ and lint - goto's inside switch should never be + executed; aclpvt is set to 0 to avoid "used before set" warning. +*/ + static int __yaccpar_lint_hack__ = 0; + switch (__yaccpar_lint_hack__) + { + case 1: goto aclerrlab; + case 2: goto aclnewstate; + } + aclpvt = 0; +#endif + + /* + ** Initialize externals - acl_Parse may be called more than once + */ + aclpv = &aclv[-1]; + aclps = &acls[-1]; + aclstate = 0; + acltmp = 0; + aclnerrs = 0; + aclerrflag = 0; + aclchar = -1; + +#if ACLMAXDEPTH <= 0 + if (aclmaxdepth <= 0) + { + if ((aclmaxdepth = ACLEXPAND(0)) <= 0) + { + aclerror("yacc initialization error"); + ACLABORT; + } + } +#endif + + { + register ACLSTYPE *acl_pv; /* top of value stack */ + register int *acl_ps; /* top of state stack */ + register int acl_state; /* current state */ + register int acl_n; /* internal state number info */ + goto aclstack; /* moved from 6 lines above to here to please C++ */ + + /* + ** get globals into registers. + ** branch to here only if ACLBACKUP was called. + */ + aclnewstate: + acl_pv = aclpv; + acl_ps = aclps; + acl_state = aclstate; + goto acl_newstate; + + /* + ** get globals into registers. + ** either we just started, or we just finished a reduction + */ + aclstack: + acl_pv = aclpv; + acl_ps = aclps; + acl_state = aclstate; + + /* + ** top of for (;;) loop while no reductions done + */ + acl_stack: + /* + ** put a state and value onto the stacks + */ +#if ACLDEBUG + /* + ** if debugging, look up token value in list of value vs. + ** name pairs. 0 and negative (-1) are special values. + ** Note: linear search is used since time is not a real + ** consideration while debugging. + */ + if ( acldebug ) + { + register int acl_i; + + printf( "State %d, token ", acl_state ); + if ( aclchar == 0 ) + printf( "end-of-file\n" ); + else if ( aclchar < 0 ) + printf( "-none-\n" ); + else + { + for ( acl_i = 0; acltoks[acl_i].t_val >= 0; + acl_i++ ) + { + if ( acltoks[acl_i].t_val == aclchar ) + break; + } + printf( "%s\n", acltoks[acl_i].t_name ); + } + } +#endif /* ACLDEBUG */ + if ( ++acl_ps >= &acls[ aclmaxdepth ] ) /* room on stack? */ + { + /* + ** reallocate and recover. Note that pointers + ** have to be reset, or bad things will happen + */ + int aclps_index = (acl_ps - acls); + int aclpv_index = (acl_pv - aclv); + int aclpvt_index = (aclpvt - aclv); + int aclnewmax; +#ifdef ACLEXPAND + aclnewmax = ACLEXPAND(aclmaxdepth); +#else + aclnewmax = 2 * aclmaxdepth; /* double table size */ + if (aclmaxdepth == ACLMAXDEPTH) /* first time growth */ + { + char *newacls = (char *)ACLNEW(int); + char *newaclv = (char *)ACLNEW(ACLSTYPE); + if (newacls != 0 && newaclv != 0) + { + acls = ACLCOPY(newacls, acls, int); + aclv = ACLCOPY(newaclv, aclv, ACLSTYPE); + } + else + aclnewmax = 0; /* failed */ + } + else /* not first time */ + { + acls = ACLENLARGE(acls, int); + aclv = ACLENLARGE(aclv, ACLSTYPE); + if (acls == 0 || aclv == 0) + aclnewmax = 0; /* failed */ + } +#endif + if (aclnewmax <= aclmaxdepth) /* tables not expanded */ + { + aclerror( "yacc stack overflow" ); + ACLABORT; + } + aclmaxdepth = aclnewmax; + + acl_ps = acls + aclps_index; + acl_pv = aclv + aclpv_index; + aclpvt = aclv + aclpvt_index; + } + *acl_ps = acl_state; + *++acl_pv = aclval; + + /* + ** we have a new state - find out what to do + */ + acl_newstate: + if ( ( acl_n = aclpact[ acl_state ] ) <= ACLFLAG ) + goto acldefault; /* simple state */ +#if ACLDEBUG + /* + ** if debugging, need to mark whether new token grabbed + */ + acltmp = aclchar < 0; +#endif + if ( ( aclchar < 0 ) && ( ( aclchar = ACLLEX() ) < 0 ) ) + aclchar = 0; /* reached EOF */ +#if ACLDEBUG + if ( acldebug && acltmp ) + { + register int acl_i; + + printf( "Received token " ); + if ( aclchar == 0 ) + printf( "end-of-file\n" ); + else if ( aclchar < 0 ) + printf( "-none-\n" ); + else + { + for ( acl_i = 0; acltoks[acl_i].t_val >= 0; + acl_i++ ) + { + if ( acltoks[acl_i].t_val == aclchar ) + break; + } + printf( "%s\n", acltoks[acl_i].t_name ); + } + } +#endif /* ACLDEBUG */ + if ( ( ( acl_n += aclchar ) < 0 ) || ( acl_n >= ACLLAST ) ) + goto acldefault; + if ( aclchk[ acl_n = aclact[ acl_n ] ] == aclchar ) /*valid shift*/ + { + aclchar = -1; + aclval = acllval; + acl_state = acl_n; + if ( aclerrflag > 0 ) + aclerrflag--; + goto acl_stack; + } + + acldefault: + if ( ( acl_n = acldef[ acl_state ] ) == -2 ) + { +#if ACLDEBUG + acltmp = aclchar < 0; +#endif + if ( ( aclchar < 0 ) && ( ( aclchar = ACLLEX() ) < 0 ) ) + aclchar = 0; /* reached EOF */ +#if ACLDEBUG + if ( acldebug && acltmp ) + { + register int acl_i; + + printf( "Received token " ); + if ( aclchar == 0 ) + printf( "end-of-file\n" ); + else if ( aclchar < 0 ) + printf( "-none-\n" ); + else + { + for ( acl_i = 0; + acltoks[acl_i].t_val >= 0; + acl_i++ ) + { + if ( acltoks[acl_i].t_val + == aclchar ) + { + break; + } + } + printf( "%s\n", acltoks[acl_i].t_name ); + } + } +#endif /* ACLDEBUG */ + /* + ** look through exception table + */ + { + register int *aclxi = aclexca; + + while ( ( *aclxi != -1 ) || + ( aclxi[1] != acl_state ) ) + { + aclxi += 2; + } + while ( ( *(aclxi += 2) >= 0 ) && + ( *aclxi != aclchar ) ) + ; + if ( ( acl_n = aclxi[1] ) < 0 ) + ACLACCEPT; + } + } + + /* + ** check for syntax error + */ + if ( acl_n == 0 ) /* have an error */ + { + /* no worry about speed here! */ + switch ( aclerrflag ) + { + case 0: /* new error */ + aclerror( "syntax error" ); + goto skip_init; + aclerrlab: + /* + ** get globals into registers. + ** we have a user generated syntax type error + */ + acl_pv = aclpv; + acl_ps = aclps; + acl_state = aclstate; + skip_init: + aclnerrs++; + /* FALLTHRU */ + case 1: + case 2: /* incompletely recovered error */ + /* try again... */ + aclerrflag = 3; + /* + ** find state where "error" is a legal + ** shift action + */ + while ( acl_ps >= acls ) + { + acl_n = aclpact[ *acl_ps ] + ACLERRCODE; + if ( acl_n >= 0 && acl_n < ACLLAST && + aclchk[aclact[acl_n]] == ACLERRCODE) { + /* + ** simulate shift of "error" + */ + acl_state = aclact[ acl_n ]; + goto acl_stack; + } + /* + ** current state has no shift on + ** "error", pop stack + */ +#if ACLDEBUG +# define _POP_ "Error recovery pops state %d, uncovers state %d\n" + if ( acldebug ) + printf( _POP_, *acl_ps, + acl_ps[-1] ); +# undef _POP_ +#endif + acl_ps--; + acl_pv--; + } + /* + ** there is no state on stack with "error" as + ** a valid shift. give up. + */ + ACLABORT; + case 3: /* no shift yet; eat a token */ +#if ACLDEBUG + /* + ** if debugging, look up token in list of + ** pairs. 0 and negative shouldn't occur, + ** but since timing doesn't matter when + ** debugging, it doesn't hurt to leave the + ** tests here. + */ + if ( acldebug ) + { + register int acl_i; + + printf( "Error recovery discards " ); + if ( aclchar == 0 ) + printf( "token end-of-file\n" ); + else if ( aclchar < 0 ) + printf( "token -none-\n" ); + else + { + for ( acl_i = 0; + acltoks[acl_i].t_val >= 0; + acl_i++ ) + { + if ( acltoks[acl_i].t_val + == aclchar ) + { + break; + } + } + printf( "token %s\n", + acltoks[acl_i].t_name ); + } + } +#endif /* ACLDEBUG */ + if ( aclchar == 0 ) /* reached EOF. quit */ + ACLABORT; + aclchar = -1; + goto acl_newstate; + } + }/* end if ( acl_n == 0 ) */ + /* + ** reduction by production acl_n + ** put stack tops, etc. so things right after switch + */ +#if ACLDEBUG + /* + ** if debugging, print the string that is the user's + ** specification of the reduction which is just about + ** to be done. + */ + if ( acldebug ) + printf( "Reduce by (%d) \"%s\"\n", + acl_n, aclreds[ acl_n ] ); +#endif + acltmp = acl_n; /* value to switch over */ + aclpvt = acl_pv; /* $vars top of value stack */ + /* + ** Look in goto table for next state + ** Sorry about using acl_state here as temporary + ** register variable, but why not, if it works... + ** If aclr2[ acl_n ] doesn't have the low order bit + ** set, then there is no action to be done for + ** this reduction. So, no saving & unsaving of + ** registers done. The only difference between the + ** code just after the if and the body of the if is + ** the goto acl_stack in the body. This way the test + ** can be made before the choice of what to do is needed. + */ + { + /* length of production doubled with extra bit */ + register int acl_len = aclr2[ acl_n ]; + + if ( !( acl_len & 01 ) ) + { + acl_len >>= 1; + aclval = ( acl_pv -= acl_len )[1]; /* $$ = $1 */ + acl_state = aclpgo[ acl_n = aclr1[ acl_n ] ] + + *( acl_ps -= acl_len ) + 1; + if ( acl_state >= ACLLAST || + aclchk[ acl_state = + aclact[ acl_state ] ] != -acl_n ) + { + acl_state = aclact[ aclpgo[ acl_n ] ]; + } + goto acl_stack; + } + acl_len >>= 1; + aclval = ( acl_pv -= acl_len )[1]; /* $$ = $1 */ + acl_state = aclpgo[ acl_n = aclr1[ acl_n ] ] + + *( acl_ps -= acl_len ) + 1; + if ( acl_state >= ACLLAST || + aclchk[ acl_state = aclact[ acl_state ] ] != -acl_n ) + { + acl_state = aclact[ aclpgo[ acl_n ] ]; + } + } + /* save until reenter driver code */ + aclstate = acl_state; + aclps = acl_ps; + aclpv = acl_pv; + } + /* + ** code supplied by user is placed in this switch + */ + switch( acltmp ) + { + +case 3: +# line 266 "acltext.y" +{ + PERM_FREE(aclpvt[-0].string); + } break; +case 8: +# line 286 "acltext.y" +{ + acl_free_args(curr_args_list); + } break; +case 9: +# line 292 "acltext.y" +{ + curr_acl = ACL_AclNew(NULL, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + aclerror("Couldn't add ACL to list."); + return(-1); + } + acl_clear_args(curr_args_list); + use_generic_rights = 0; + if (strstr(curr_acl->tag, "READ")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("read")); + acl_add_arg(curr_args_list, PERM_STRDUP("execute")); + acl_add_arg(curr_args_list, PERM_STRDUP("list")); + acl_add_arg(curr_args_list, PERM_STRDUP("info")); + } if (strstr(curr_acl->tag, "WRITE")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("write")); + acl_add_arg(curr_args_list, PERM_STRDUP("delete")); + } + } break; +case 10: +# line 314 "acltext.y" +{ + curr_acl = ACL_AclNew(NULL, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + aclerror("Couldn't add ACL to list."); + return(-1); + } + acl_clear_args(curr_args_list); + use_generic_rights = 0; + if (strstr(curr_acl->tag, "READ")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("read")); + acl_add_arg(curr_args_list, PERM_STRDUP("execute")); + acl_add_arg(curr_args_list, PERM_STRDUP("list")); + acl_add_arg(curr_args_list, PERM_STRDUP("info")); + } if (strstr(curr_acl->tag, "WRITE")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("write")); + acl_add_arg(curr_args_list, PERM_STRDUP("delete")); + } + } break; +case 13: +# line 342 "acltext.y" +{ + char acl_tmp_arg[255]; + char *acl_new_arg; + + if (!use_generic_rights) { + acl_string_lower(aclpvt[-0].string); + strcpy(acl_tmp_arg, "http_"); + strcat(acl_tmp_arg, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + acl_new_arg = PERM_STRDUP(acl_tmp_arg); + acl_add_arg(curr_args_list, acl_new_arg); + } else { + PERM_FREE(aclpvt[-0].string); + } + } break; +case 14: +# line 358 "acltext.y" +{ + if (!use_generic_rights) { + acl_add_arg(curr_args_list, aclpvt[-0].string); + } else { + PERM_FREE(aclpvt[-0].string); + } + } break; +case 19: +# line 376 "acltext.y" +{ + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + aclerror("Could not set authorization processing flags"); + return(-1); + } + } break; +case 22: +# line 388 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_ALLOW) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(allow) failed"); + return(-1); + } + acl_clear_args(curr_user_list); + acl_clear_args(curr_ip_dns_list); + } break; +case 23: +# line 398 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_DENY) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(allow) failed"); + return(-1); + } + acl_clear_args(curr_user_list); + acl_clear_args(curr_ip_dns_list); + } break; +case 24: +# line 411 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(auth) failed"); + return(-1); + } + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + aclerror("Could not set authorization processing flags"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + aclerror("Could not set authorization info"); + return(-1); + } + } break; +case 26: +# line 430 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(auth) failed"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + aclerror("Could not set authorization info"); + return(-1); + } + } break; +case 28: +# line 446 "acltext.y" +{ + if ( acl_set_users_or_groups(curr_expr, curr_user_list) < 0 ) { + aclerror("acl_set_users_or_groups() failed"); + return(-1); + } + + if ( acl_set_ip_dns(curr_expr, curr_ip_dns_list) < 0 ) { + aclerror("acl_set_ip_dns() failed"); + return(-1); + } + + if ( ACL_ExprAnd(NULL, curr_expr) < 0 ) { + aclerror("ACL_ExprAnd() failed"); + return(-1); + } + + if ( acl_set_args(curr_expr, curr_args_list) < 0 ) { + aclerror("acl_set_args() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 29: +# line 473 "acltext.y" +{ + if ( acl_set_users_or_groups(curr_expr, curr_user_list) < 0 ) { + aclerror("acl_set_users_or_groups() failed"); + return(-1); + } + + if ( acl_set_args(curr_expr, curr_args_list) < 0 ) { + aclerror("acl_set_args() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 34: +# line 500 "acltext.y" +{ + acl_add_arg(curr_user_list, aclpvt[-0].string); + } break; +case 35: +# line 504 "acltext.y" +{ + acl_add_arg(curr_user_list, aclpvt[-0].string); + } break; +case 39: +# line 516 "acltext.y" +{ + acl_add_arg(curr_ip_dns_list, aclpvt[-0].string); + } break; +case 40: +# line 520 "acltext.y" +{ + acl_add_arg(curr_ip_dns_list, aclpvt[-0].string); + } break; +case 41: +# line 526 "acltext.y" +{ + char tmp_str[255]; + + util_sprintf(tmp_str, "%s+%s", aclpvt[-1].string, aclpvt[-0].string); + PERM_FREE(aclpvt[-1].string); + PERM_FREE(aclpvt[-0].string); + acl_add_arg(curr_ip_dns_list, PERM_STRDUP(tmp_str)); + } break; +case 46: +# line 543 "acltext.y" +{ + if ( ACL_ExprAddArg(NULL, curr_expr, "user") < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + + if ( ACL_ExprAddArg(NULL, curr_expr, "group") < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 47: +# line 562 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if (strcmp(aclpvt[-2].string, "database") == 0) { + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-1].string); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index(aclpvt[-2].string), aclpvt[-2].string, aclpvt[-1].string, NULL) < 0 ) { + } + PERM_FREE(aclpvt[-2].string); + } + } break; +case 48: +# line 575 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if (strcmp(aclpvt[-2].string, "database") == 0) { + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-1].string); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index(aclpvt[-2].string), aclpvt[-2].string, aclpvt[-1].string, NULL) < 0 ) { + } + PERM_FREE(aclpvt[-2].string); + } + } break; +case 56: +# line 611 "acltext.y" +{ + curr_acl = ACL_AclNew(NULL, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + aclerror("Couldn't add ACL to list."); + return(-1); + } + } break; +case 57: +# line 620 "acltext.y" +{ + curr_acl = ACL_AclNew(NULL, aclpvt[-0].string); + PERM_FREE(aclpvt[-0].string); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + aclerror("Couldn't add ACL to list."); + return(-1); + } + } break; +case 63: +# line 641 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_RESPONSE) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(deny) failed"); + return(-1); + } + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + aclerror("Could not set deny processing flags"); + return(-1); + } + } break; +case 65: +# line 659 "acltext.y" +{ + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_RESPONSE) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(deny) failed"); + return(-1); + } + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 67: +# line 674 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( ACL_ExprSetDenyWith(NULL, curr_expr, + aclpvt[-2].string, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprSetDenyWith() failed"); + return(-1); + } + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + } break; +case 68: +# line 687 "acltext.y" +{ + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(allow) failed"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + aclerror("Could not set authorization info"); + return(-1); + } + } break; +case 69: +# line 701 "acltext.y" +{ + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 72: +# line 713 "acltext.y" +{ + acl_string_lower(aclpvt[-0].string); + if ( ACL_ExprAddArg(NULL, curr_expr, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + PERM_FREE(aclpvt[-0].string); + } break; +case 75: +# line 728 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index(aclpvt[-2].string), aclpvt[-2].string, aclpvt[-0].string, NULL) < 0 ) { + } + PERM_FREE(aclpvt[-2].string); + } break; +case 76: +# line 736 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index(aclpvt[-2].string), aclpvt[-2].string, aclpvt[-0].string, NULL) < 0 ) { + } + PERM_FREE(aclpvt[-2].string); + } break; +case 77: +# line 746 "acltext.y" +{ + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_ALLOW) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(allow) failed"); + return(-1); + } + } break; +case 79: +# line 756 "acltext.y" +{ + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_DENY) ; + if ( curr_expr == NULL ) { + aclerror("ACL_ExprNew(deny) failed"); + return(-1); + } + } break; +case 81: +# line 768 "acltext.y" +{ + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + aclerror("Could not add authorization"); + return(-1); + } + } break; +case 82: +# line 775 "acltext.y" +{ + if ( ACL_ExprSetPFlags (NULL, curr_expr, pflags) < 0 ) { + aclerror("Could not set authorization processing flags"); + return(-1); + } +#ifdef DEBUG + if ( ACL_ExprDisplay(curr_expr) < 0 ) { + aclerror("ACL_ExprDisplay() failed"); + return(-1); + } + printf("Parsed authorization.\n"); +#endif + } break; +case 85: +# line 795 "acltext.y" +{ + pflags = ACL_PFLAG_ABSOLUTE; + } break; +case 86: +# line 799 "acltext.y" +{ + pflags = ACL_PFLAG_ABSOLUTE; + } break; +case 87: +# line 803 "acltext.y" +{ + pflags = ACL_PFLAG_CONTENT; + } break; +case 88: +# line 807 "acltext.y" +{ + pflags = ACL_PFLAG_CONTENT; + } break; +case 89: +# line 811 "acltext.y" +{ + pflags = ACL_PFLAG_TERMINAL; + } break; +case 90: +# line 815 "acltext.y" +{ + pflags = ACL_PFLAG_TERMINAL; + } break; +case 91: +# line 821 "acltext.y" +{ + pflags |= ACL_PFLAG_CONTENT; + } break; +case 92: +# line 825 "acltext.y" +{ + pflags |= ACL_PFLAG_ABSOLUTE; + } break; +case 93: +# line 829 "acltext.y" +{ + pflags |= ACL_PFLAG_ABSOLUTE | ACL_PFLAG_CONTENT; + } break; +case 94: +# line 833 "acltext.y" +{ + pflags |= ACL_PFLAG_ABSOLUTE | ACL_PFLAG_CONTENT; + } break; +case 95: +# line 839 "acltext.y" +{ + pflags |= ACL_PFLAG_CONTENT; + } break; +case 96: +# line 843 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL; + } break; +case 97: +# line 847 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_CONTENT; + } break; +case 98: +# line 851 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_CONTENT; + } break; +case 99: +# line 857 "acltext.y" +{ + pflags |= ACL_PFLAG_ABSOLUTE; + } break; +case 100: +# line 861 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL; + } break; +case 101: +# line 865 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_ABSOLUTE; + } break; +case 102: +# line 869 "acltext.y" +{ + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_ABSOLUTE; + } break; +case 105: +# line 879 "acltext.y" +{ + acl_string_lower(aclpvt[-0].string); + if ( ACL_ExprAddArg(NULL, curr_expr, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprAddArg() failed"); + return(-1); + } + PERM_FREE( aclpvt[-0].string ); + } break; +case 107: +# line 891 "acltext.y" +{ + if ( ACL_ExprAnd(NULL, curr_expr) < 0 ) { + aclerror("ACL_ExprAnd() failed"); + return(-1); + } + } break; +case 108: +# line 898 "acltext.y" +{ + if ( ACL_ExprOr(NULL, curr_expr) < 0 ) { + aclerror("ACL_ExprOr() failed"); + return(-1); + } + } break; +case 111: +# line 909 "acltext.y" +{ + if ( ACL_ExprNot(NULL, curr_expr) < 0 ) { + aclerror("ACL_ExprNot() failed"); + return(-1); + } + } break; +case 112: +# line 918 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( ACL_ExprTerm(NULL, curr_expr, + aclpvt[-2].string, (CmpOp_t) aclpvt[-1].ival, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + return(-1); + } + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + } break; +case 113: +# line 931 "acltext.y" +{ + acl_string_lower(aclpvt[-2].string); + if ( ACL_ExprTerm(NULL, curr_expr, + aclpvt[-2].string, (CmpOp_t) aclpvt[-1].ival, aclpvt[-0].string) < 0 ) { + aclerror("ACL_ExprTerm() failed"); + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + return(-1); + } + PERM_FREE(aclpvt[-2].string); + PERM_FREE(aclpvt[-0].string); + } break; + } + goto aclstack; /* reset registers in driver code */ +} + diff --git a/lib/libaccess/acl.tab.h b/lib/libaccess/acl.tab.h new file mode 100644 index 00000000..359f290a --- /dev/null +++ b/lib/libaccess/acl.tab.h @@ -0,0 +1,44 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +typedef union +#ifdef __cplusplus + ACLSTYPE +#endif + { + char *string; + int ival; +} ACLSTYPE; +extern ACLSTYPE acllval; +# define ACL_ABSOLUTE_TOK 257 +# define ACL_ACL_TOK 258 +# define ACL_ALLOW_TOK 259 +# define ACL_ALWAYS_TOK 260 +# define ACL_AND_TOK 261 +# define ACL_AT_TOK 262 +# define ACL_AUTHENTICATE_TOK 263 +# define ACL_CONTENT_TOK 264 +# define ACL_DEFAULT_TOK 265 +# define ACL_DENY_TOK 266 +# define ACL_GROUP_TOK 267 +# define ACL_IN_TOK 268 +# define ACL_INHERIT_TOK 269 +# define ACL_NOT_TOK 270 +# define ACL_NULL_TOK 271 +# define ACL_OR_TOK 272 +# define ACL_QSTRING_TOK 273 +# define ACL_READ_TOK 274 +# define ACL_TERMINAL_TOK 275 +# define ACL_VARIABLE_TOK 276 +# define ACL_VERSION_TOK 277 +# define ACL_WRITE_TOK 278 +# define ACL_WITH_TOK 279 +# define ACL_EQ_TOK 280 +# define ACL_GE_TOK 281 +# define ACL_GT_TOK 282 +# define ACL_LE_TOK 283 +# define ACL_LT_TOK 284 +# define ACL_NE_TOK 285 diff --git a/lib/libaccess/acl.yy.cpp b/lib/libaccess/acl.yy.cpp new file mode 100644 index 00000000..90821330 --- /dev/null +++ b/lib/libaccess/acl.yy.cpp @@ -0,0 +1,1995 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define ACL_FLEX_MAJOR_VERSION 2 +#define ACL_FLEX_MINOR_VERSION 5 + +#include <stdio.h> + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include <stdlib.h> + +/* Use prototypes in function declarations. */ +#define ACL_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define ACL_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define ACL_USE_PROTOS +#define ACL_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include <io.h> +#include <stdlib.h> +#define ACL_USE_CONST +#define ACL_USE_PROTOS +#endif + +#ifdef ACL_USE_CONST +#define aclconst const +#else +#define aclconst +#endif + + +#ifdef ACL_USE_PROTOS +#define ACL_PROTO(proto) proto +#else +#define ACL_PROTO(proto) () +#endif + +/* Returned upon end-of-file. */ +#define ACL_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define ACL_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN acl_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The ACLSTATE alias is for lex + * compatibility. + */ +#define ACL_START ((acl_start - 1) / 2) +#define ACLSTATE ACL_START + +/* Action number for EOF rule of a given start state. */ +#define ACL_STATE_EOF(state) (ACL_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define ACL_NEW_FILE aclrestart( aclin ) + +#define ACL_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define ACL_BUF_SIZE 16384 + +typedef struct acl_buffer_state *ACL_BUFFER_STATE; + +extern int aclleng; +extern FILE *aclin, *aclout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * aclless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the aclless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define aclless(n) \ + do \ + { \ + /* Undo effects of setting up acltext. */ \ + *acl_cp = acl_hold_char; \ + ACL_RESTORE_ACL_MORE_OFFSET \ + acl_c_buf_p = acl_cp = acl_bp + n - ACL_MORE_ADJ; \ + ACL_DO_BEFORE_ACTION; /* set up acltext again */ \ + } \ + while ( 0 ) + +#define unput(c) aclunput( c, acltext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int acl_size_t; + + +struct acl_buffer_state + { + FILE *acl_input_file; + + char *acl_ch_buf; /* input buffer */ + char *acl_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + acl_size_t acl_buf_size; + + /* Number of characters read into acl_ch_buf, not including EOB + * characters. + */ + int acl_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can PERM_REALLOC() it to grow it, and should PERM_FREE() it to + * delete it. + */ + int acl_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int acl_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int acl_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int acl_fill_buffer; + + int acl_buffer_status; +#define ACL_BUFFER_NEW 0 +#define ACL_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as ACL_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via aclrestart()), so that the user can continue scanning by + * just pointing aclin at a new input file. + */ +#define ACL_BUFFER_EOF_PENDING 2 + }; + +static ACL_BUFFER_STATE acl_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define ACL_CURRENT_BUFFER acl_current_buffer + + +/* acl_hold_char holds the character lost when acltext is formed. */ +static char acl_hold_char; + +static int acl_n_chars; /* number of characters read into acl_ch_buf */ + + +int aclleng; + +/* Points to current character in buffer. */ +static char *acl_c_buf_p = (char *) 0; +static int acl_init = 1; /* whether we need to initialize */ +static int acl_start = 0; /* start state number */ + +/* Flag which is used to allow aclwrap()'s to do buffer switches + * instead of setting up a fresh aclin. A bit of a hack ... + */ +static int acl_did_buffer_switch_on_eof; + +void aclrestart ACL_PROTO(( FILE *input_file )); + +void acl_switch_to_buffer ACL_PROTO(( ACL_BUFFER_STATE new_buffer )); +void acl_load_buffer_state ACL_PROTO(( void )); +ACL_BUFFER_STATE acl_create_buffer ACL_PROTO(( FILE *file, int size )); +void acl_delete_buffer ACL_PROTO(( ACL_BUFFER_STATE b )); +void acl_init_buffer ACL_PROTO(( ACL_BUFFER_STATE b, FILE *file )); +void acl_flush_buffer ACL_PROTO(( ACL_BUFFER_STATE b )); +#define ACL_FLUSH_BUFFER acl_flush_buffer( acl_current_buffer ) + +ACL_BUFFER_STATE acl_scan_buffer ACL_PROTO(( char *base, acl_size_t size )); +ACL_BUFFER_STATE acl_scan_string ACL_PROTO(( aclconst char *str )); +ACL_BUFFER_STATE acl_scan_bytes ACL_PROTO(( aclconst char *bytes, int len )); + +static void *ACL_FLEX_ALLOC ACL_PROTO(( acl_size_t )); +static void *ACL_FLEX_REALLOC ACL_PROTO(( void *, acl_size_t )); +static void ACL_FLEX_FREE ACL_PROTO(( void * )); + +#define acl_new_buffer acl_create_buffer + +#define acl_set_interactive(is_interactive) \ + { \ + if ( ! acl_current_buffer ) \ + acl_current_buffer = acl_create_buffer( aclin, ACL_BUF_SIZE ); \ + acl_current_buffer->acl_is_interactive = is_interactive; \ + } + +#define acl_set_bol(at_bol) \ + { \ + if ( ! acl_current_buffer ) \ + acl_current_buffer = acl_create_buffer( aclin, ACL_BUF_SIZE ); \ + acl_current_buffer->acl_at_bol = at_bol; \ + } + +#define ACL_AT_BOL() (acl_current_buffer->acl_at_bol) + +typedef unsigned char ACL_CHAR; +FILE *aclin = (FILE *) 0, *aclout = (FILE *) 0; +typedef int acl_state_type; +extern char *acltext; +#define acltext_ptr acltext + +static acl_state_type acl_get_previous_state ACL_PROTO(( void )); +static acl_state_type acl_try_NUL_trans ACL_PROTO(( acl_state_type current_state )); +static int acl_get_next_buffer ACL_PROTO(( void )); +static void acl_fatal_error ACL_PROTO(( aclconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up acltext. + */ +#define ACL_DO_BEFORE_ACTION \ + acltext_ptr = acl_bp; \ + aclleng = (int) (acl_cp - acl_bp); \ + acl_hold_char = *acl_cp; \ + *acl_cp = '\0'; \ + acl_c_buf_p = acl_cp; + +#define ACL_NUM_RULES 30 +#define ACL_END_OF_BUFFER 31 +static aclconst short int acl_accept[104] = + { 0, + 0, 0, 31, 30, 2, 1, 30, 30, 3, 28, + 29, 25, 22, 24, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 2, 1, 27, 0, 4, 3, 29, + 26, 23, 29, 29, 29, 29, 9, 29, 29, 29, + 14, 29, 21, 29, 29, 29, 29, 6, 29, 29, + 20, 29, 29, 29, 29, 29, 19, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 13, 29, 29, 29, + 18, 29, 7, 29, 29, 29, 29, 29, 29, 29, + 29, 8, 29, 29, 29, 29, 29, 29, 29, 29, + 11, 12, 15, 29, 17, 5, 29, 16, 29, 29, + + 29, 10, 0 + } ; + +static aclconst int acl_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 6, 1, 1, 1, 1, 7, + 7, 8, 1, 7, 8, 8, 1, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 1, 7, 9, + 10, 11, 1, 1, 12, 13, 14, 15, 16, 17, + 8, 18, 19, 8, 8, 20, 21, 22, 23, 8, + 8, 24, 25, 26, 27, 28, 29, 8, 30, 8, + 1, 1, 1, 1, 8, 1, 12, 13, 14, 15, + + 16, 17, 8, 18, 19, 8, 8, 20, 21, 22, + 23, 8, 8, 24, 25, 26, 27, 28, 29, 8, + 30, 8, 7, 1, 7, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static aclconst int acl_meta[31] = + { 0, + 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, + 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 + } ; + +static aclconst short int acl_base[108] = + { 0, + 0, 0, 118, 119, 115, 0, 106, 28, 0, 119, + 0, 105, 119, 104, 21, 90, 96, 89, 87, 85, + 92, 91, 87, 103, 0, 119, 33, 119, 0, 0, + 119, 119, 79, 83, 17, 87, 0, 75, 78, 22, + 81, 72, 0, 73, 72, 69, 71, 0, 70, 80, + 0, 73, 64, 77, 58, 71, 0, 65, 60, 66, + 63, 53, 51, 64, 63, 51, 0, 53, 57, 56, + 0, 47, 0, 48, 50, 49, 50, 50, 46, 44, + 40, 0, 39, 38, 37, 36, 49, 38, 43, 39, + 0, 0, 0, 36, 0, 0, 36, 0, 33, 16, + + 24, 0, 119, 48, 51, 54, 29 + } ; + +static aclconst short int acl_def[108] = + { 0, + 103, 1, 103, 103, 103, 104, 103, 105, 106, 103, + 107, 103, 103, 103, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 103, 104, 103, 105, 103, 106, 107, + 103, 103, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, + + 107, 107, 0, 103, 103, 103, 103 + } ; + +static aclconst short int acl_nxt[150] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 11, 16, 17, 11, 11, 11, 18, 11, + 11, 19, 20, 11, 11, 21, 11, 22, 23, 11, + 28, 30, 28, 33, 34, 28, 49, 28, 54, 102, + 35, 101, 36, 55, 100, 50, 37, 38, 25, 99, + 25, 27, 27, 27, 29, 98, 29, 97, 96, 95, + 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, + 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, + 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, + 64, 63, 62, 61, 60, 59, 58, 57, 56, 53, + + 52, 51, 48, 47, 24, 46, 45, 44, 43, 42, + 41, 40, 39, 32, 31, 26, 24, 103, 3, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103 + } ; + +static aclconst short int acl_chk[150] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 8, 107, 8, 15, 15, 27, 35, 27, 40, 101, + 15, 100, 15, 40, 99, 35, 15, 15, 104, 97, + 104, 105, 105, 105, 106, 94, 106, 90, 89, 88, + 87, 86, 85, 84, 83, 81, 80, 79, 78, 77, + 76, 75, 74, 72, 70, 69, 68, 66, 65, 64, + 63, 62, 61, 60, 59, 58, 56, 55, 54, 53, + 52, 50, 49, 47, 46, 45, 44, 42, 41, 39, + + 38, 36, 34, 33, 24, 23, 22, 21, 20, 19, + 18, 17, 16, 14, 12, 7, 5, 3, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, + 103, 103, 103, 103, 103, 103, 103, 103, 103 + } ; + +static acl_state_type acl_last_accepting_state; +static char *acl_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define aclmore() aclmore_used_but_not_detected +#define ACL_MORE_ADJ 0 +#define ACL_RESTORE_ACL_MORE_OFFSET +char *acltext; +#line 1 "aclscan.l" +#define INITIAL 0 +/* + * Lexical analyzer for ACL + */ +#line 6 "aclscan.l" +#include "acl.tab.h" /* token codes */ +#include <base/file.h> +#include <libaccess/acl.h> +#include <libaccess/nserror.h> +#include "aclpriv.h" +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <libaccess/aclerror.h> +#ifdef XP_WIN32 +#include <io.h> +#endif + +#include "parse.h" +#include "aclscan.h" + +static int acl_scanner_input(char *buffer, int max_size); + +#define ACL_NEVER_INTERACTIVE 1 +#undef ACL_INPUT +#define ACL_INPUT(buf, result, max) (result = acl_scanner_input(buf, max)) + +static int acl_lineno; +static int acl_tokenpos; +static char acl_filename[500]; +static NSErr_t *acl_errp; +static int acl_use_buffer; +static char *acl_buffer; +static int acl_buffer_length; +static int acl_buffer_offset; +static char *last_string; +static SYS_FILE acl_prfd; + + +#line 466 "acl.yy.cpp" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef ACL_SKIP_ACLWRAP +#ifdef __cplusplus +extern "C" int aclwrap ACL_PROTO(( void )); +#else +extern int aclwrap ACL_PROTO(( void )); +#endif +#endif + +#ifndef ACL_NO_UNPUT +static void aclunput ACL_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef acltext_ptr +static void acl_flex_strncpy ACL_PROTO(( char *, aclconst char *, int )); +#endif + +#ifdef ACL_NEED_STRLEN +static int acl_flex_strlen ACL_PROTO(( aclconst char * )); +#endif + +#ifndef ACL_NO_INPUT +#ifdef __cplusplus +static int aclinput ACL_PROTO(( void )); +#else +static int input ACL_PROTO(( void )); +#endif +#endif + +#if ACL_STACK_USED +static int acl_start_stack_ptr = 0; +static int acl_start_stack_depth = 0; +static int *acl_start_stack = 0; +#ifndef ACL_NO_PUSH_STATE +static void acl_push_state ACL_PROTO(( int new_state )); +#endif +#ifndef ACL_NO_POP_STATE +static void acl_pop_state ACL_PROTO(( void )); +#endif +#ifndef ACL_NO_TOP_STATE +static int acl_top_state ACL_PROTO(( void )); +#endif + +#else +#define ACL_NO_PUSH_STATE 1 +#define ACL_NO_POP_STATE 1 +#define ACL_NO_TOP_STATE 1 +#endif + +#ifdef ACL_MALLOC_DECL +ACL_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include <stdlib.h> +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef ACL_READ_BUF_SIZE +#define ACL_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( acltext, aclleng, 1, aclout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or ACL_NULL, + * is returned in "result". + */ +#ifndef ACL_INPUT +#define ACL_INPUT(buf,result,max_size) \ + if ( acl_current_buffer->acl_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( aclin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( aclin ) ) \ + ACL_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, aclin )) == 0) \ + && ferror( aclin ) ) \ + ACL_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "aclterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef aclterminate +#define aclterminate() return ACL_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef ACL_START_STACK_INCR +#define ACL_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef ACL_FATAL_ERROR +#define ACL_FATAL_ERROR(msg) acl_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef ACL_DECL +#define ACL_DECL int acllex ACL_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after acltext and aclleng + * have been set up. + */ +#ifndef ACL_USER_ACTION +#define ACL_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef ACL_BREAK +#define ACL_BREAK break; +#endif + +#define ACL_RULE_SETUP \ + ACL_USER_ACTION + +ACL_DECL + { + register acl_state_type acl_current_state; + register char *acl_cp, *acl_bp; + register int acl_act; + +#line 47 "aclscan.l" + + +#line 620 "acl.yy.cpp" + + if ( acl_init ) + { + acl_init = 0; + +#ifdef ACL_USER_INIT + ACL_USER_INIT; +#endif + + if ( ! acl_start ) + acl_start = 1; /* first start state */ + + if ( ! aclin ) + aclin = stdin; + + if ( ! aclout ) + aclout = stdout; + + if ( ! acl_current_buffer ) + acl_current_buffer = + acl_create_buffer( aclin, ACL_BUF_SIZE ); + + acl_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + acl_cp = acl_c_buf_p; + + /* Support of acltext. */ + *acl_cp = acl_hold_char; + + /* acl_bp points to the position in acl_ch_buf of the start of + * the current run. + */ + acl_bp = acl_cp; + + acl_current_state = acl_start; +acl_match: + do + { + register ACL_CHAR acl_c = acl_ec[ACL_SC_TO_UI(*acl_cp)]; + if ( acl_accept[acl_current_state] ) + { + acl_last_accepting_state = acl_current_state; + acl_last_accepting_cpos = acl_cp; + } + while ( acl_chk[acl_base[acl_current_state] + acl_c] != acl_current_state ) + { + acl_current_state = (int) acl_def[acl_current_state]; + if ( acl_current_state >= 104 ) + acl_c = acl_meta[(unsigned int) acl_c]; + } + acl_current_state = acl_nxt[acl_base[acl_current_state] + (unsigned int) acl_c]; + ++acl_cp; + } + while ( acl_base[acl_current_state] != 119 ); + +acl_find_action: + acl_act = acl_accept[acl_current_state]; + if ( acl_act == 0 ) + { /* have to back up */ + acl_cp = acl_last_accepting_cpos; + acl_current_state = acl_last_accepting_state; + acl_act = acl_accept[acl_current_state]; + } + + ACL_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( acl_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of ACL_DO_BEFORE_ACTION */ + *acl_cp = acl_hold_char; + acl_cp = acl_last_accepting_cpos; + acl_current_state = acl_last_accepting_state; + goto acl_find_action; + +case 1: +ACL_RULE_SETUP +#line 49 "aclscan.l" +{ + acl_lineno++; + acl_tokenpos = 0; + aclless(1); + } + ACL_BREAK +case 2: +ACL_RULE_SETUP +#line 55 "aclscan.l" +; + ACL_BREAK +case 3: +ACL_RULE_SETUP +#line 57 "aclscan.l" +; + ACL_BREAK +case ACL_STATE_EOF(INITIAL): +#line 59 "aclscan.l" +{ + acllval.string = NULL; + last_string = acllval.string; + return(0); + } + ACL_BREAK +case 4: +ACL_RULE_SETUP +#line 65 "aclscan.l" +{ + acllval.string = PERM_STRDUP( acltext+1 ); + last_string = acllval.string; + if ( acllval.string[aclleng-2] != '"' ) + fprintf(stderr, "Unterminated string\n") ; + else + acllval.string[aclleng-2] = '\0'; + acl_tokenpos += aclleng; + return ACL_QSTRING_TOK; + } + ACL_BREAK +case 5: +ACL_RULE_SETUP +#line 77 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_ABSOLUTE_TOK; + } + ACL_BREAK +case 6: +ACL_RULE_SETUP +#line 83 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_ACL_TOK; + } + ACL_BREAK +case 7: +ACL_RULE_SETUP +#line 89 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_ALLOW_TOK; + } + ACL_BREAK +case 8: +ACL_RULE_SETUP +#line 95 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_ALWAYS_TOK; + } + ACL_BREAK +case 9: +ACL_RULE_SETUP +#line 101 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_AT_TOK; + } + ACL_BREAK +case 10: +ACL_RULE_SETUP +#line 107 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_AUTHENTICATE_TOK; + } + ACL_BREAK +case 11: +ACL_RULE_SETUP +#line 113 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_CONTENT_TOK; + } + ACL_BREAK +case 12: +ACL_RULE_SETUP +#line 119 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_DEFAULT_TOK; + } + ACL_BREAK +case 13: +ACL_RULE_SETUP +#line 125 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_DENY_TOK; + } + ACL_BREAK +case 14: +ACL_RULE_SETUP +#line 131 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_IN_TOK; + } + ACL_BREAK +case 15: +ACL_RULE_SETUP +#line 137 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_INHERIT_TOK; + } + ACL_BREAK +case 16: +ACL_RULE_SETUP +#line 143 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_TERMINAL_TOK; + } + ACL_BREAK +case 17: +ACL_RULE_SETUP +#line 149 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_VERSION_TOK; + } + ACL_BREAK +case 18: +ACL_RULE_SETUP +#line 155 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_WITH_TOK; + } + ACL_BREAK +case 19: +ACL_RULE_SETUP +#line 161 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return ACL_NOT_TOK; + } + ACL_BREAK +case 20: +ACL_RULE_SETUP +#line 167 "aclscan.l" +{ + last_string = NULL; + acllval.ival = ACL_EXPR_OP_AND; + acl_tokenpos += aclleng; + return ACL_AND_TOK; + } + ACL_BREAK +case 21: +ACL_RULE_SETUP +#line 174 "aclscan.l" +{ + last_string = NULL; + acllval.ival = ACL_EXPR_OP_OR; + acl_tokenpos += aclleng; + return ACL_OR_TOK; + } + ACL_BREAK +case 22: +ACL_RULE_SETUP +#line 181 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_EQ; + acl_tokenpos += aclleng; + return ACL_EQ_TOK; + } + ACL_BREAK +case 23: +ACL_RULE_SETUP +#line 188 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_GE; + acl_tokenpos += aclleng; + return ACL_GE_TOK; + } + ACL_BREAK +case 24: +ACL_RULE_SETUP +#line 195 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_GT; + acl_tokenpos += aclleng; + return ACL_GT_TOK; + } + ACL_BREAK +case 25: +ACL_RULE_SETUP +#line 202 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_LT; + acl_tokenpos += aclleng; + return ACL_LT_TOK; + } + ACL_BREAK +case 26: +ACL_RULE_SETUP +#line 209 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_LE; + acl_tokenpos += aclleng; + return ACL_LE_TOK; + } + ACL_BREAK +case 27: +ACL_RULE_SETUP +#line 216 "aclscan.l" +{ + last_string = NULL; + acllval.ival = CMP_OP_NE; + acl_tokenpos += aclleng; + return ACL_NE_TOK; + } + ACL_BREAK +case 28: +ACL_RULE_SETUP +#line 223 "aclscan.l" +{ + last_string = NULL; + acl_tokenpos += aclleng; + return acltext[0]; + } + ACL_BREAK +case 29: +ACL_RULE_SETUP +#line 229 "aclscan.l" +{ + acl_tokenpos += aclleng; + acllval.string = PERM_STRDUP( acltext ); + last_string = acllval.string; + return ACL_VARIABLE_TOK; + } + ACL_BREAK +case 30: +ACL_RULE_SETUP +#line 235 "aclscan.l" +ECHO; + ACL_BREAK +#line 983 "acl.yy.cpp" + + case ACL_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int acl_amount_of_matched_text = (int) (acl_cp - acltext_ptr) - 1; + + /* Undo the effects of ACL_DO_BEFORE_ACTION. */ + *acl_cp = acl_hold_char; + ACL_RESTORE_ACL_MORE_OFFSET + + if ( acl_current_buffer->acl_buffer_status == ACL_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed aclin at a new source and called + * acllex(). If so, then we have to assure + * consistency between acl_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + acl_n_chars = acl_current_buffer->acl_n_chars; + acl_current_buffer->acl_input_file = aclin; + acl_current_buffer->acl_buffer_status = ACL_BUFFER_NORMAL; + } + + /* Note that here we test for acl_c_buf_p "<=" to the position + * of the first EOB in the buffer, since acl_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( acl_c_buf_p <= &acl_current_buffer->acl_ch_buf[acl_n_chars] ) + { /* This was really a NUL. */ + acl_state_type acl_next_state; + + acl_c_buf_p = acltext_ptr + acl_amount_of_matched_text; + + acl_current_state = acl_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * acl_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + acl_next_state = acl_try_NUL_trans( acl_current_state ); + + acl_bp = acltext_ptr + ACL_MORE_ADJ; + + if ( acl_next_state ) + { + /* Consume the NUL. */ + acl_cp = ++acl_c_buf_p; + acl_current_state = acl_next_state; + goto acl_match; + } + + else + { + acl_cp = acl_c_buf_p; + goto acl_find_action; + } + } + + else switch ( acl_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + acl_did_buffer_switch_on_eof = 0; + + if ( aclwrap() ) + { + /* Note: because we've taken care in + * acl_get_next_buffer() to have set up + * acltext, we can now set up + * acl_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * ACL_NULL, it'll still work - another + * ACL_NULL will get returned. + */ + acl_c_buf_p = acltext_ptr + ACL_MORE_ADJ; + + acl_act = ACL_STATE_EOF(ACL_START); + goto do_action; + } + + else + { + if ( ! acl_did_buffer_switch_on_eof ) + ACL_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + acl_c_buf_p = + acltext_ptr + acl_amount_of_matched_text; + + acl_current_state = acl_get_previous_state(); + + acl_cp = acl_c_buf_p; + acl_bp = acltext_ptr + ACL_MORE_ADJ; + goto acl_match; + + case EOB_ACT_LAST_MATCH: + acl_c_buf_p = + &acl_current_buffer->acl_ch_buf[acl_n_chars]; + + acl_current_state = acl_get_previous_state(); + + acl_cp = acl_c_buf_p; + acl_bp = acltext_ptr + ACL_MORE_ADJ; + goto acl_find_action; + } + break; + } + + default: + ACL_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of acllex */ + + +/* acl_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int acl_get_next_buffer() + { + register char *dest = acl_current_buffer->acl_ch_buf; + register char *source = acltext_ptr; + register int number_to_move, i; + int ret_val; + + if ( acl_c_buf_p > &acl_current_buffer->acl_ch_buf[acl_n_chars + 1] ) + ACL_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( acl_current_buffer->acl_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( acl_c_buf_p - acltext_ptr - ACL_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (acl_c_buf_p - acltext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( acl_current_buffer->acl_buffer_status == ACL_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + acl_n_chars = 0; + + else + { + int num_to_read = + acl_current_buffer->acl_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef ACL_USES_REJECT + ACL_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + ACL_BUFFER_STATE b = acl_current_buffer; + + int acl_c_buf_p_offset = + (int) (acl_c_buf_p - b->acl_ch_buf); + + if ( b->acl_is_our_buffer ) + { + int new_size = b->acl_buf_size * 2; + + if ( new_size <= 0 ) + b->acl_buf_size += b->acl_buf_size / 8; + else + b->acl_buf_size *= 2; + + b->acl_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + ACL_FLEX_REALLOC( (void *) b->acl_ch_buf, + b->acl_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->acl_ch_buf = 0; + + if ( ! b->acl_ch_buf ) + ACL_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + acl_c_buf_p = &b->acl_ch_buf[acl_c_buf_p_offset]; + + num_to_read = acl_current_buffer->acl_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > ACL_READ_BUF_SIZE ) + num_to_read = ACL_READ_BUF_SIZE; + + /* Read in more data. */ + ACL_INPUT( (&acl_current_buffer->acl_ch_buf[number_to_move]), + acl_n_chars, num_to_read ); + } + + if ( acl_n_chars == 0 ) + { + if ( number_to_move == ACL_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + aclrestart( aclin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + acl_current_buffer->acl_buffer_status = + ACL_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + acl_n_chars += number_to_move; + acl_current_buffer->acl_ch_buf[acl_n_chars] = ACL_END_OF_BUFFER_CHAR; + acl_current_buffer->acl_ch_buf[acl_n_chars + 1] = ACL_END_OF_BUFFER_CHAR; + + acltext_ptr = &acl_current_buffer->acl_ch_buf[0]; + + return ret_val; + } + + +/* acl_get_previous_state - get the state just before the EOB char was reached */ + +static acl_state_type acl_get_previous_state() + { + register acl_state_type acl_current_state; + register char *acl_cp; + + acl_current_state = acl_start; + + for ( acl_cp = acltext_ptr + ACL_MORE_ADJ; acl_cp < acl_c_buf_p; ++acl_cp ) + { + register ACL_CHAR acl_c = (*acl_cp ? acl_ec[ACL_SC_TO_UI(*acl_cp)] : 1); + if ( acl_accept[acl_current_state] ) + { + acl_last_accepting_state = acl_current_state; + acl_last_accepting_cpos = acl_cp; + } + while ( acl_chk[acl_base[acl_current_state] + acl_c] != acl_current_state ) + { + acl_current_state = (int) acl_def[acl_current_state]; + if ( acl_current_state >= 104 ) + acl_c = acl_meta[(unsigned int) acl_c]; + } + acl_current_state = acl_nxt[acl_base[acl_current_state] + (unsigned int) acl_c]; + } + + return acl_current_state; + } + + +/* acl_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = acl_try_NUL_trans( current_state ); + */ + +#ifdef ACL_USE_PROTOS +static acl_state_type acl_try_NUL_trans( acl_state_type acl_current_state ) +#else +static acl_state_type acl_try_NUL_trans( acl_current_state ) +acl_state_type acl_current_state; +#endif + { + register int acl_is_jam; + register char *acl_cp = acl_c_buf_p; + + register ACL_CHAR acl_c = 1; + if ( acl_accept[acl_current_state] ) + { + acl_last_accepting_state = acl_current_state; + acl_last_accepting_cpos = acl_cp; + } + while ( acl_chk[acl_base[acl_current_state] + acl_c] != acl_current_state ) + { + acl_current_state = (int) acl_def[acl_current_state]; + if ( acl_current_state >= 104 ) + acl_c = acl_meta[(unsigned int) acl_c]; + } + acl_current_state = acl_nxt[acl_base[acl_current_state] + (unsigned int) acl_c]; + acl_is_jam = (acl_current_state == 103); + + return acl_is_jam ? 0 : acl_current_state; + } + + +#ifndef ACL_NO_UNPUT +#ifdef ACL_USE_PROTOS +static void aclunput( int c, register char *acl_bp ) +#else +static void aclunput( c, acl_bp ) +int c; +register char *acl_bp; +#endif + { + register char *acl_cp = acl_c_buf_p; + + /* undo effects of setting up acltext */ + *acl_cp = acl_hold_char; + + if ( acl_cp < acl_current_buffer->acl_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = acl_n_chars + 2; + register char *dest = &acl_current_buffer->acl_ch_buf[ + acl_current_buffer->acl_buf_size + 2]; + register char *source = + &acl_current_buffer->acl_ch_buf[number_to_move]; + + while ( source > acl_current_buffer->acl_ch_buf ) + *--dest = *--source; + + acl_cp += (int) (dest - source); + acl_bp += (int) (dest - source); + acl_n_chars = acl_current_buffer->acl_buf_size; + + if ( acl_cp < acl_current_buffer->acl_ch_buf + 2 ) + ACL_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--acl_cp = (char) c; + + + acltext_ptr = acl_bp; + acl_hold_char = *acl_cp; + acl_c_buf_p = acl_cp; + } +#endif /* ifndef ACL_NO_UNPUT */ + + +#ifdef __cplusplus +static int aclinput() +#else +static int input() +#endif + { + int c; + + *acl_c_buf_p = acl_hold_char; + + if ( *acl_c_buf_p == ACL_END_OF_BUFFER_CHAR ) + { + /* acl_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( acl_c_buf_p < &acl_current_buffer->acl_ch_buf[acl_n_chars] ) + /* This was really a NUL. */ + *acl_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = acl_c_buf_p - acltext_ptr; + ++acl_c_buf_p; + + switch ( acl_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( aclwrap() ) + { + acl_c_buf_p = acltext_ptr + offset; + return EOF; + } + + if ( ! acl_did_buffer_switch_on_eof ) + ACL_NEW_FILE; +#ifdef __cplusplus + return aclinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + acl_c_buf_p = acltext_ptr + offset; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + ACL_FATAL_ERROR( + "unexpected last match in aclinput()" ); +#else + ACL_FATAL_ERROR( + "unexpected last match in input()" ); +#endif + } + } + } + + c = *(unsigned char *) acl_c_buf_p; /* cast for 8-bit char's */ + *acl_c_buf_p = '\0'; /* preserve acltext */ + acl_hold_char = *++acl_c_buf_p; + + + return c; + } + + +#ifdef ACL_USE_PROTOS +void aclrestart( FILE *input_file ) +#else +void aclrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! acl_current_buffer ) + acl_current_buffer = acl_create_buffer( aclin, ACL_BUF_SIZE ); + + acl_init_buffer( acl_current_buffer, input_file ); + acl_load_buffer_state(); + } + + +#ifdef ACL_USE_PROTOS +void acl_switch_to_buffer( ACL_BUFFER_STATE new_buffer ) +#else +void acl_switch_to_buffer( new_buffer ) +ACL_BUFFER_STATE new_buffer; +#endif + { + if ( acl_current_buffer == new_buffer ) + return; + + if ( acl_current_buffer ) + { + /* Flush out information for old buffer. */ + *acl_c_buf_p = acl_hold_char; + acl_current_buffer->acl_buf_pos = acl_c_buf_p; + acl_current_buffer->acl_n_chars = acl_n_chars; + } + + acl_current_buffer = new_buffer; + acl_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (aclwrap()) processing, but the only time this flag + * is looked at is after aclwrap() is called, so it's safe + * to go ahead and always set it. + */ + acl_did_buffer_switch_on_eof = 1; + } + + +#ifdef ACL_USE_PROTOS +void acl_load_buffer_state( void ) +#else +void acl_load_buffer_state() +#endif + { + acl_n_chars = acl_current_buffer->acl_n_chars; + acltext_ptr = acl_c_buf_p = acl_current_buffer->acl_buf_pos; + aclin = acl_current_buffer->acl_input_file; + acl_hold_char = *acl_c_buf_p; + } + + +#ifdef ACL_USE_PROTOS +ACL_BUFFER_STATE acl_create_buffer( FILE *file, int size ) +#else +ACL_BUFFER_STATE acl_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + ACL_BUFFER_STATE b; + + b = (ACL_BUFFER_STATE) ACL_FLEX_ALLOC( sizeof( struct acl_buffer_state ) ); + if ( ! b ) + ACL_FATAL_ERROR( "out of dynamic memory in acl_create_buffer()" ); + + b->acl_buf_size = size; + + /* acl_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->acl_ch_buf = (char *) ACL_FLEX_ALLOC( b->acl_buf_size + 2 ); + if ( ! b->acl_ch_buf ) + ACL_FATAL_ERROR( "out of dynamic memory in acl_create_buffer()" ); + + b->acl_is_our_buffer = 1; + + acl_init_buffer( b, file ); + + return b; + } + + +#ifdef ACL_USE_PROTOS +void acl_delete_buffer( ACL_BUFFER_STATE b ) +#else +void acl_delete_buffer( b ) +ACL_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == acl_current_buffer ) + acl_current_buffer = (ACL_BUFFER_STATE) 0; + + if ( b->acl_is_our_buffer ) + ACL_FLEX_FREE( (void *) b->acl_ch_buf ); + + ACL_FLEX_FREE( (void *) b ); + } + + +#ifndef ACL_ALWAYS_INTERACTIVE +#ifndef ACL_NEVER_INTERACTIVE +extern int isatty ACL_PROTO(( int )); +#endif +#endif + +#ifdef ACL_USE_PROTOS +void acl_init_buffer( ACL_BUFFER_STATE b, FILE *file ) +#else +void acl_init_buffer( b, file ) +ACL_BUFFER_STATE b; +FILE *file; +#endif + + + { + acl_flush_buffer( b ); + + b->acl_input_file = file; + b->acl_fill_buffer = 1; + +#if ACL_ALWAYS_INTERACTIVE + b->acl_is_interactive = 1; +#else +#if ACL_NEVER_INTERACTIVE + b->acl_is_interactive = 0; +#else + b->acl_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef ACL_USE_PROTOS +void acl_flush_buffer( ACL_BUFFER_STATE b ) +#else +void acl_flush_buffer( b ) +ACL_BUFFER_STATE b; +#endif + + { + b->acl_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->acl_ch_buf[0] = ACL_END_OF_BUFFER_CHAR; + b->acl_ch_buf[1] = ACL_END_OF_BUFFER_CHAR; + + b->acl_buf_pos = &b->acl_ch_buf[0]; + + b->acl_at_bol = 1; + b->acl_buffer_status = ACL_BUFFER_NEW; + + if ( b == acl_current_buffer ) + acl_load_buffer_state(); + } + + +#ifndef ACL_NO_SCAN_BUFFER +#ifdef ACL_USE_PROTOS +ACL_BUFFER_STATE acl_scan_buffer( char *base, acl_size_t size ) +#else +ACL_BUFFER_STATE acl_scan_buffer( base, size ) +char *base; +acl_size_t size; +#endif + { + ACL_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != ACL_END_OF_BUFFER_CHAR || + base[size-1] != ACL_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (ACL_BUFFER_STATE) ACL_FLEX_ALLOC( sizeof( struct acl_buffer_state ) ); + if ( ! b ) + ACL_FATAL_ERROR( "out of dynamic memory in acl_scan_buffer()" ); + + b->acl_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->acl_buf_pos = b->acl_ch_buf = base; + b->acl_is_our_buffer = 0; + b->acl_input_file = 0; + b->acl_n_chars = b->acl_buf_size; + b->acl_is_interactive = 0; + b->acl_at_bol = 1; + b->acl_fill_buffer = 0; + b->acl_buffer_status = ACL_BUFFER_NEW; + + acl_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef ACL_NO_SCAN_STRING +#ifdef ACL_USE_PROTOS +ACL_BUFFER_STATE acl_scan_string( aclconst char *str ) +#else +ACL_BUFFER_STATE acl_scan_string( str ) +aclconst char *str; +#endif + { + int len; + for ( len = 0; str[len]; ++len ) + ; + + return acl_scan_bytes( str, len ); + } +#endif + + +#ifndef ACL_NO_SCAN_BYTES +#ifdef ACL_USE_PROTOS +ACL_BUFFER_STATE acl_scan_bytes( aclconst char *bytes, int len ) +#else +ACL_BUFFER_STATE acl_scan_bytes( bytes, len ) +aclconst char *bytes; +int len; +#endif + { + ACL_BUFFER_STATE b; + char *buf; + acl_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) ACL_FLEX_ALLOC( n ); + if ( ! buf ) + ACL_FATAL_ERROR( "out of dynamic memory in acl_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = ACL_END_OF_BUFFER_CHAR; + + b = acl_scan_buffer( buf, n ); + if ( ! b ) + ACL_FATAL_ERROR( "bad buffer in acl_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->acl_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef ACL_NO_PUSH_STATE +#ifdef ACL_USE_PROTOS +static void acl_push_state( int new_state ) +#else +static void acl_push_state( new_state ) +int new_state; +#endif + { + if ( acl_start_stack_ptr >= acl_start_stack_depth ) + { + acl_size_t new_size; + + acl_start_stack_depth += ACL_START_STACK_INCR; + new_size = acl_start_stack_depth * sizeof( int ); + + if ( ! acl_start_stack ) + acl_start_stack = (int *) ACL_FLEX_ALLOC( new_size ); + + else + acl_start_stack = (int *) ACL_FLEX_REALLOC( + (void *) acl_start_stack, new_size ); + + if ( ! acl_start_stack ) + ACL_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + acl_start_stack[acl_start_stack_ptr++] = ACL_START; + + BEGIN(new_state); + } +#endif + + +#ifndef ACL_NO_POP_STATE +static void acl_pop_state() + { + if ( --acl_start_stack_ptr < 0 ) + ACL_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(acl_start_stack[acl_start_stack_ptr]); + } +#endif + + +#ifndef ACL_NO_TOP_STATE +static int acl_top_state() + { + return acl_start_stack[acl_start_stack_ptr - 1]; + } +#endif + +#ifndef ACL_EXIT_FAILURE +#define ACL_EXIT_FAILURE 2 +#endif + +#ifdef ACL_USE_PROTOS +static void acl_fatal_error( aclconst char msg[] ) +#else +static void acl_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( ACL_EXIT_FAILURE ); + } + + + +/* Redefine aclless() so it works in section 3 code. */ + +#undef aclless +#define aclless(n) \ + do \ + { \ + /* Undo effects of setting up acltext. */ \ + acltext[aclleng] = acl_hold_char; \ + acl_c_buf_p = acltext + n; \ + acl_hold_char = *acl_c_buf_p; \ + *acl_c_buf_p = '\0'; \ + aclleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef acltext_ptr +#ifdef ACL_USE_PROTOS +static void acl_flex_strncpy( char *s1, aclconst char *s2, int n ) +#else +static void acl_flex_strncpy( s1, s2, n ) +char *s1; +aclconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef ACL_NEED_STRLEN +#ifdef ACL_USE_PROTOS +static int acl_flex_strlen( aclconst char *s ) +#else +static int acl_flex_strlen( s ) +aclconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef ACL_USE_PROTOS +static void *ACL_FLEX_ALLOC( acl_size_t size ) +#else +static void *ACL_FLEX_ALLOC( size ) +acl_size_t size; +#endif + { + return (void *) PERM_MALLOC( size ); + } + +#ifdef ACL_USE_PROTOS +static void *ACL_FLEX_REALLOC( void *ptr, acl_size_t size ) +#else +static void *ACL_FLEX_REALLOC( ptr, size ) +void *ptr; +acl_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) PERM_REALLOC( (char *) ptr, size ); + } + +#ifdef ACL_USE_PROTOS +static void ACL_FLEX_FREE( void *ptr ) +#else +static void ACL_FLEX_FREE( ptr ) +void *ptr; +#endif + { + PERM_FREE( ptr ); + } + +#if ACL_MAIN +int main() + { + acllex(); + return 0; + } +#endif +#line 235 "aclscan.l" + + +void +acl_detab(char *t, char *s) +{ + int ii; + int pos; + int len; + + if (s == NULL || t == NULL) + return; + + len = strlen(s); + pos = 0; + for (ii = 0; ii < len; ii++) { + if (s[ii] == '\t') { + t[pos] = ' '; + } else { + t[pos] = s[ii]; + } + pos++; + } + t[pos] = '\0'; + return; +} + +void +aclerror(const char *s) +{ +char errorStr[256]; + +#if defined(UTEST) || defined(ACL_COMPILER) + printf("ACL file: %s\n", acl_filename); + printf("Syntax error at line: %d, token: %s\n", acl_lineno, acltext); + if ( last_string ) + PERM_FREE(last_string); +#else + sprintf(errorStr, "%d", acl_lineno); + if (acltext) { + nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program, + 3, acl_filename, errorStr, acltext); + } else { + nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program, + 2, acl_filename, errorStr); + } + if ( last_string ) + PERM_FREE(last_string); +#endif + +} + +int +acl_InitScanner(NSErr_t *errp, char *filename, char *buffer) +{ + acl_errp = errp; + acl_lineno = 1; + acl_use_buffer = (filename == NULL) ? 1 : 0 ; + if ( filename != NULL ) { + strcpy(acl_filename, filename); +#ifdef UTEST + aclin = fopen(filename, "r"); + if ( aclin == NULL ) { + return(-1); + } +#else + acl_prfd = system_fopenRO(filename); + if ( acl_prfd == NULL ) { + return(-1); + } + aclin = (FILE *) acl_prfd; +#endif + aclrestart(aclin); + } else if ( buffer != NULL ) { + strcpy(acl_filename, "internal-buffer"); + acl_buffer_offset = 0; + acl_buffer_length = strlen(buffer); + acl_buffer = PERM_STRDUP(buffer); + if (acl_buffer == NULL) + return(-1); + aclrestart(NULL); + } else { + return(-1); + } + return(0); +} + +int +acl_EndScanner() +{ + acl_lineno = 0; + if ( acl_use_buffer) { + if ( acl_buffer ) + PERM_FREE(acl_buffer); + } else if ( aclin ) { +#ifdef UTEST + fclose(aclin); +#else + if ( acl_prfd ) { + system_fclose(acl_prfd); + acl_prfd = NULL; + } +#endif + aclin = NULL ; + } + return(0); +} + +int +aclwrap() +{ + return(1); +} + +static int +acl_scanner_input(char *buffer, int max_size) +{ + int len = 0; + + if ( acl_use_buffer ) { + len = (acl_buffer_length > max_size) ? max_size : + acl_buffer_length; + memcpy(buffer, (const void *) &acl_buffer[acl_buffer_offset], + len); + acl_buffer_length -= len; + acl_buffer_offset += len; + } +#ifdef UTEST + else if ( ((len = fread( buffer, 1, max_size, aclin )) == 0) + && ferror( aclin ) ) { + aclerror( "scanner input failed" ); + } +#else + else if ( (len = system_fread( acl_prfd, buffer, max_size)) < 0 ) { + aclerror( "scanner input failed" ); + } +#endif + + return(len); +} diff --git a/lib/libaccess/aclbuild.cpp b/lib/libaccess/aclbuild.cpp new file mode 100644 index 00000000..8c4647dc --- /dev/null +++ b/lib/libaccess/aclbuild.cpp @@ -0,0 +1,1360 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (aclbuild.c) + * + * This module provides functions for building Access Control List + * (ACL) structures in memory. + * + */ + +#include <assert.h> +#include "base/systems.h" +#include "netsite.h" +#include "libaccess/nsauth.h" +#include "libaccess/nsuser.h" +#include "libaccess/nsgroup.h" +#include "libaccess/nsadb.h" +#include "libaccess/aclerror.h" +#include "libaccess/aclstruct.h" +#include "libaccess/aclbuild.h" +#include "libaccess/aclparse.h" +#include "libaccess/acleval.h" +#include "libaccess/usi.h" + +char * ACL_Program = "NSACL"; /* ACL facility name */ + +/* + * Description (accCreate) + * + * This function creates a new access control context, which + * provides context information for a set of ACL definitions. + * The caller also provides a handle for a symbol table to be + * used to store definitions of ACL and rights names. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * stp - symbol table handle (may be null) + * pacc - pointer to returned context handle + * + * Returns: + * + * If the context is created successfully, the return value is zero. + * 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 accCreate(NSErr_t * errp, void * stp, ACContext_t **pacc) +{ + ACContext_t * acc; /* pointer to new context */ + int rv; /* result value */ + int eid; /* error id */ + + *pacc = 0; + + /* Do we need to create a symbol table? */ + if (stp == 0) { + + /* Yes, create a symbol table for ACL, rights, etc. names */ + rv = symTableNew(&stp); + if (rv < 0) goto err_nomem1; + } + + /* Allocate the context structure */ + acc = (ACContext_t *)MALLOC(sizeof(ACContext_t)); + if (acc == 0) goto err_nomem2; + + /* Initialize it */ + acc->acc_stp = stp; + acc->acc_acls = 0; + acc->acc_rights = 0; + acc->acc_refcnt = 0; + + *pacc = acc; + return 0; + + err_nomem1: + rv = ACLERRNOMEM; + eid = ACLERR3000; + goto err_ret; + + err_nomem2: + rv = ACLERRNOMEM; + eid = ACLERR3020; + + err_ret: + nserrGenerate(errp, rv, eid, ACL_Program, 0); + return rv; +} + +/* + * Description (accDestroy) + * + * This function destroys a set of ACL data structures referenced + * by a specified ACContext_t structure, including the ACContext_t + * itself. + * + * Arguments: + * + * acc - pointer to ACContext_t structure + * flags - bit flags (unused - must be zero) + */ + +void accDestroy(ACContext_t * acc, int flags) +{ + ACL_t * acl; + + if (acc != 0) { + + /* + * First destroy all ACLs and any unnamed structures they reference. + * Note that aclDestroy() modifies the acc_acls list. + */ + while ((acl = acc->acc_acls) != 0) { + + aclDelete(acl); + } + + /* If there's a symbol table, destroy everything it references */ + if (acc->acc_stp != 0) { + symTableEnumerate(acc->acc_stp, 0, accDestroySym); + + /* Destroy the symbol table itself */ + symTableDestroy(acc->acc_stp, 0); + } + + /* Free the ACContext_t structure */ + FREE(acc); + } +} + +/* + * Description (accDestroySym) + * + * This function is called to destroy the data structure associated + * with a specified Symbol_t symbol table entry. It examines the + * type of the symbol and calls the appropriate destructor. + * + * Arguments: + * + * sym - pointer to symbol table entry + * argp - unused - must be zero + * + * Returns: + * + * The return value is SYMENUMREMOVE. + */ + +int accDestroySym(Symbol_t * sym, void * argp) +{ + switch (sym->sym_type) { + case ACLSYMACL: /* ACL */ + aclDestroy((ACL_t *)sym); + break; + + case ACLSYMRIGHT: /* access right */ + { + RightDef_t * rdp = (RightDef_t *)sym; + + if (rdp->rd_sym.sym_name != 0) { + FREE(rdp->rd_sym.sym_name); + } + FREE(rdp); + } + break; + + case ACLSYMRDEF: /* access rights list */ + aclRightSpecDestroy((RightSpec_t *)sym); + break; + + case ACLSYMREALM: /* realm name */ + aclRealmSpecDestroy((RealmSpec_t *)sym); + break; + + case ACLSYMHOST: /* host specifications */ + aclHostSpecDestroy((HostSpec_t *)sym); + break; + + case ACLSYMUSER: /* user/group list */ + aclUserSpecDestroy((UserSpec_t *)sym); + break; + } + + return SYMENUMREMOVE; +} + +/* + * Description (accReadFile) + * + * This function reads a specfied file containing ACL definitions + * and creates data structures in memory to represent the ACLs. + * The caller may provide a pointer to an existing ACContext_t + * structure which will serve as the root of the ACL structures, + * or else a new one will be created. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * aclfile - pointer to the ACL filename string + * pacc - value/result ACContext_t + * + * Returns: + * + * If the ACL file is read successfully, the return value is zero. + * 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 accReadFile(NSErr_t * errp, char * aclfile, ACContext_t **pacc) +{ + ACContext_t * acc = *pacc; /* pointer to ACL root structure */ + ACLFile_t * acf = 0; /* pointer to ACL file handle */ + void * stp = 0; /* ACL symbol table handle */ + int rv; /* result value */ + int eid; /* error id value */ + + /* Initialize the ACL parser */ + rv = aclParseInit(); + if (rv < 0) goto err_init; + + /* Do we need to create a new ACContext_t structure? */ + if (acc == 0) { + + /* Yes, create a symbol table for ACL, rights, etc. names */ + rv = symTableNew(&stp); + if (rv < 0) goto err_crsym; + + /* Create a root structure for the ACLs, including the symbol table */ + rv = accCreate(errp, stp, &acc); + if (rv < 0) goto err_ret2; + } + + /* Open the ACL definition file */ + rv = aclFileOpen(errp, aclfile, 0, &acf); + if (rv < 0) goto err_ret3; + + /* Parse the ACL definitions, building ACL structures in memory */ + rv = aclACLParse(errp, acf, acc, 0); + if (rv < 0) goto err_ret4; + + aclFileClose(acf, 0); + + if (pacc) *pacc = acc; + + return rv; + + err_init: + eid = ACLERR3100; + goto err_ret; + + err_crsym: + eid = ACLERR3120; + rv = ACLERRNOMEM; + goto err_ret; + + err_ret4: + aclFileClose(acf, 0); + err_ret3: + /* Destroy the ACContext_t if we just created it */ + if (acc != *pacc) { + accDestroy(acc, 0); + } + goto err_ret; + + err_ret2: + symTableDestroy(stp, 0); + + err_ret: + return rv; +} + +/* + * Description (aclAuthDNSAdd) + * + * This function adds a DNS name specification to the DNS filter + * associated with a given host list. The DNS name specification is + * either a fully-qualified domain name or a domain name suffix, + * indicated by a leading ".", e.g. (".mcom.com"). The name + * components included in a suffix must be complete. For example, + * ".scape.com" will not match names ending in ".netscape.com". + * + * Arguments: + * + * hspp - pointer to host list pointer + * dnsspec - DNS name or suffix string pointer + * fqdn - non-zero if dnsspec is fully qualified + * + * Returns: + * + * If successful, the return code is zero. + * An error is indicated by a negative return code (ACLERRxxxx + * - see aclerror.h). + */ + +int aclAuthDNSAdd(HostSpec_t **hspp, char * dnsspec, int fqdn) +{ + HostSpec_t * hsp; /* host list pointer */ + void * table; /* access control hash table pointer */ + Symbol_t * sym; /* hash table entry pointer */ + int rv; /* result value */ + + fqdn = (fqdn) ? 1 : 0; + + /* Create the HostSpec_t if it doesn't exist */ + hsp = *hspp; + if (hsp == 0) { + + hsp = (HostSpec_t *)MALLOC(sizeof(HostSpec_t)); + if (hsp == 0) goto err_nomem; + memset((void *)hsp, 0, sizeof(HostSpec_t)); + hsp->hs_sym.sym_type = ACLSYMHOST; + } + + /* Get pointer to hash table used for DNS filter */ + table = hsp->hs_host.inh_dnf.dnf_hash; + if (table == 0) { + + /* None there yet, so create one */ + rv = symTableNew(&table); + if (rv < 0) goto punt; + hsp->hs_host.inh_dnf.dnf_hash = table; + } + + /* See if the DNS spec is already in the table */ + rv = symTableFindSym(table, dnsspec, fqdn, (void **)&sym); + if (rv < 0) { + if (rv != SYMERRNOSYM) goto punt; + + /* It's not there, so add it */ + sym = (Symbol_t *)MALLOC(sizeof(Symbol_t)); + sym->sym_name = STRDUP(dnsspec); + sym->sym_type = fqdn; + + rv = symTableAddSym(table, sym, (void *)sym); + if (rv < 0) goto err_nomem; + } + + *hspp = hsp; + + punt: + return rv; + + err_nomem: + rv = ACLERRNOMEM; + goto punt; +} + +/* + * Description (aclAuthIPAdd) + * + * This function adds an IP address specification to the IP filter + * associated with a given host list. The IP address specification + * consists of an IP host or network address and an IP netmask. + * For host addresses the netmask value is 255.255.255.255. + * + * Arguments: + * + * hspp - pointer to host list pointer + * ipaddr - IP host or network address + * netmask - IP netmask value + * + * Returns: + * + * If successful, the return code is zero. + * An error is indicated by a negative return code (ACLERRxxxx + * - see aclerror.h). + */ + +int aclAuthIPAdd(HostSpec_t **hspp, IPAddr_t ipaddr, IPAddr_t netmask) +{ + HostSpec_t * hsp; /* host list pointer */ + IPFilter_t * ipf; /* IP filter pointer */ + IPNode_t * ipn; /* current node pointer */ + IPNode_t * lastipn; /* last (lower) node pointer */ + IPLeaf_t * leaf; /* leaf node pointer */ + IPAddr_t bitmask; /* bit mask for current node */ + int lastbit; /* number of last bit set in netmask */ + int i; /* loop index */ + + /* Create the HostSpec_t if it doesn't exist */ + hsp = *hspp; + if (hsp == 0) { + + hsp = (HostSpec_t *)MALLOC(sizeof(HostSpec_t)); + if (hsp == 0) goto err_nomem; + memset((void *)hsp, 0, sizeof(HostSpec_t)); + hsp->hs_sym.sym_type = ACLSYMHOST; + } + + ipf = &hsp->hs_host.inh_ipf; + + /* If the filter doesn't have a root node yet, create it */ + if (ipf->ipf_tree == 0) { + + /* Allocate node */ + ipn = (IPNode_t *)MALLOC(sizeof(IPNode_t)); + if (ipn == 0) goto err_nomem; + + /* Initialize it to test bit 31, but without any descendants */ + ipn->ipn_type = IPN_NODE; + ipn->ipn_bit = 31; + ipn->ipn_parent = NULL; + ipn->ipn_clear = NULL; + ipn->ipn_set = NULL; + ipn->ipn_masked = NULL; + + /* Set it as the root node in the radix tree */ + ipf->ipf_tree = ipn; + } + + /* First we search the tree to see where this IP specification fits */ + + lastipn = NULL; + + for (ipn = ipf->ipf_tree; (ipn != NULL) && (ipn->ipn_type == IPN_NODE); ) { + + /* Get a mask for the bit this node tests */ + bitmask = (IPAddr_t) 1<<ipn->ipn_bit; + + /* Save pointer to last internal node */ + lastipn = ipn; + + /* Is this a bit we care about? */ + if (bitmask & netmask) { + + /* Yes, get address of set or clear descendant pointer */ + ipn = (bitmask & ipaddr) ? ipn->ipn_set : ipn->ipn_clear; + } + else { + /* No, get the address of the masked descendant pointer */ + ipn = ipn->ipn_masked; + } + } + + /* Did we end up at a leaf node? */ + if (ipn == NULL) { + + /* + * No, well, we need to find a leaf node if possible. The + * reason is that we need an IP address and netmask to compare + * to the IP address and netmask we're inserting. We know that + * they're the same up to the bit tested by the lastipn node, + * but we need to know the *highest* order bit that's different. + * Any leaf node below lastipn will do. + */ + + leaf = NULL; + ipn = lastipn; + + while (ipn != NULL) { + + /* Look for any non-null child link of the current node */ + for (i = 0; i < IPN_NLINKS; ++i) { + if (ipn->ipn_links[i]) break; + } + + /* + * Fail search for leaf if no non-null child link found. + * This should only happen on the root node of the tree + * when the tree is empty. + */ + if (i >= IPN_NLINKS) { + assert(ipn == ipf->ipf_tree); + break; + } + + /* Step to the child node */ + ipn = ipn->ipn_links[i]; + + /* Is it a leaf? */ + if (ipn->ipn_type == IPN_LEAF) { + + /* Yes, search is over */ + leaf = (IPLeaf_t *)ipn; + ipn = NULL; + break; + } + } + } + else { + + /* Yes, loop terminated on a leaf node */ + assert(ipn->ipn_type == IPN_LEAF); + leaf = (IPLeaf_t *)ipn; + } + + /* Got a leaf yet? */ + if (leaf != NULL) { + + /* Combine the IP address and netmask differences */ + bitmask = (leaf->ipl_ipaddr ^ ipaddr) | (leaf->ipl_netmask ^ netmask); + + /* Are both the IP address and the netmask the same? */ + if (bitmask == 0) { + + /* Yes, duplicate entry */ + return 0; + } + + /* Find the bit number of the first different bit */ + for (lastbit = 31; + (bitmask & 0x80000000) == 0; --lastbit, bitmask <<= 1) ; + + /* Generate a bit mask with just that bit */ + bitmask = (IPAddr_t) (1 << lastbit); + + /* + * Go up the tree from lastipn, looking for an internal node + * that tests lastbit. Stop if we get to a node that tests + * a higher bit number first. + */ + for (ipn = lastipn, lastipn = (IPNode_t *)leaf; + ipn != NULL; ipn = ipn->ipn_parent) { + + if (ipn->ipn_bit >= lastbit) { + if (ipn->ipn_bit == lastbit) { + /* Need to add a leaf off ipn node */ + lastipn = NULL; + } + break; + } + lastipn = ipn; + } + + assert(ipn != NULL); + } + else { + + /* Just hang a leaf off the lastipn node if no leaf */ + ipn = lastipn; + lastipn = NULL; + lastbit = ipn->ipn_bit; + } + + /* + * If lastipn is not NULL at this point, the new leaf will hang + * off an internal node inserted between the upper node, referenced + * by ipn, and the lower node, referenced by lastipn. The lower + * node may be an internal node or a leaf. + */ + if (lastipn != NULL) { + IPNode_t * parent = ipn; /* parent of the new node */ + + assert((lastipn->ipn_type == IPN_LEAF) || + (ipn == lastipn->ipn_parent)); + + /* Allocate space for the internal node */ + ipn = (IPNode_t *)MALLOC(sizeof(IPNode_t)); + if (ipn == NULL) goto err_nomem; + + ipn->ipn_type = IPN_NODE; + ipn->ipn_bit = lastbit; + ipn->ipn_parent = parent; + ipn->ipn_clear = NULL; + ipn->ipn_set = NULL; + ipn->ipn_masked = NULL; + + bitmask = (IPAddr_t) (1 << lastbit); + + /* + * The values in the leaf we found above determine which + * descendant link of the new internal node will reference + * the subtree that we just ascended. + */ + if (leaf->ipl_netmask & bitmask) { + if (leaf->ipl_ipaddr & bitmask) { + ipn->ipn_set = lastipn; + } + else { + ipn->ipn_clear = lastipn; + } + } + else { + ipn->ipn_masked = lastipn; + } + + /* Allocate space for the new leaf */ + leaf = (IPLeaf_t *)MALLOC(sizeof(IPLeaf_t)); + if (leaf == NULL) { + FREE((void *)ipn); + goto err_nomem; + } + + /* Insert internal node in tree */ + + /* First the downward link from the parent to the new node */ + for (i = 0; i < IPN_NLINKS; ++i) { + if (parent->ipn_links[i] == lastipn) { + parent->ipn_links[i] = ipn; + break; + } + } + + /* Then the upward link from the child (if it's not a leaf) */ + if (lastipn->ipn_type == IPN_NODE) { + lastipn->ipn_parent = ipn; + } + } + else { + /* Allocate space for a leaf node only */ + leaf = (IPLeaf_t *)MALLOC(sizeof(IPLeaf_t)); + if (leaf == NULL) goto err_nomem; + } + + /* Initialize the new leaf */ + leaf->ipl_type = IPN_LEAF; + leaf->ipl_ipaddr = ipaddr; + leaf->ipl_netmask = netmask; + + /* + * Select the appropriate descendant link of the internal node + * and point it at the new leaf. + */ + bitmask = (IPAddr_t) (1 << ipn->ipn_bit); + if (bitmask & netmask) { + if (bitmask & ipaddr) { + assert(ipn->ipn_set == NULL); + ipn->ipn_set = (IPNode_t *)leaf; + } + else { + assert(ipn->ipn_clear == NULL); + ipn->ipn_clear = (IPNode_t *)leaf; + } + } + else { + assert(ipn->ipn_masked == NULL); + ipn->ipn_masked = (IPNode_t *)leaf; + } + + *hspp = hsp; + + /* Successful completion */ + return 0; + + err_nomem: + return ACLERRNOMEM; +} + +/* + * Description (aclAuthNameAdd) + * + * This function adds a user or group to a given user list, + * in the context of a specified ACL that is being created. The + * name of the user or group is provided by the caller, and is + * looked up in the authentication database associated with the + * specified user list. The return value indicates whether the name + * matched a user or group name, and whether the corresponding user + * or group id was already present in the given user list. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * usp - pointer to user list specification + * rlm - pointer to current authentication realm + * name - pointer to user or group name string + * + * Returns: + * + * The return value is zero if the name is not found in the + * authentication database. If the name is found, the return value + * is a positive value containing bit flags: + * + * AIF_GROUP - name matches a group name + * AIF_USER - name matches a user name + * AIF_DUP - name was already represented in the + * specified user list + * + * An error is indicated by a negative return code (ACLERRxxxx + * - see aclerror.h), and an error frame will be generated if + * an error list is provided. + */ + +int aclAuthNameAdd(NSErr_t * errp, UserSpec_t * usp, + Realm_t * rlm, char * name) +{ + void * guoptr; /* group or user object pointer */ + int irv; /* insert result value */ + int eid; /* error id */ + int rv; /* result value */ + + /* There must be a realm specified in order to handle users */ + if (rlm == 0) goto err_norealm; + + /* Open the authentication database if it's not already */ + if (rlm->rlm_authdb == 0) { + + if (rlm->rlm_aif == 0) { + rlm->rlm_aif = &NSADB_AuthIF; + } + + rv = (*rlm->rlm_aif->aif_open)(errp, + rlm->rlm_dbname, 0, &rlm->rlm_authdb); + if (rv < 0) goto err_open; + } + + /* Look up the name in the authentication DB */ + rv = (*rlm->rlm_aif->aif_findname)(errp, rlm->rlm_authdb, name, + (AIF_USER|AIF_GROUP), (void **)&guoptr); + if (rv <= 0) { + if (rv < 0) goto err_adb; + + /* The name was not found in the database */ + return 0; + } + + /* The name was found. Was it a user name? */ + if (rv == AIF_USER) { + + /* Yes, add the user id to the user list */ + irv = usiInsert(&usp->us_user.uu_user, ((UserObj_t *)guoptr)->uo_uid); + rv = ANA_USER; + } + else { + + /* No, must be a group name. Add group id to an_groups list. */ + irv = usiInsert(&usp->us_user.uu_group, + ((GroupObj_t *)guoptr)->go_gid); + rv = ANA_GROUP; + } + + /* Examine the result of the insert operation */ + if (irv <= 0) { + if (irv < 0) goto err_ins; + + /* Id was already in the list */ + rv |= ANA_DUP; + } + + punt: + return rv; + + err_norealm: + eid = ACLERR3400; + rv = ACLERRNORLM; + nserrGenerate(errp, rv, eid, ACL_Program, 1, name); + goto punt; + + err_open: + eid = ACLERR3420; + rv = ACLERROPEN; + nserrGenerate(errp, rv, eid, ACL_Program, + 2, rlm->rlm_dbname, system_errmsg()); + goto punt; + + err_adb: + /* Error accessing authentication database. */ + eid = ACLERR3440; + rv = ACLERRADB; + nserrGenerate(errp, rv, eid, ACL_Program, 2, rlm->rlm_dbname, name); + goto punt; + + err_ins: + /* Error on insert operation. Must be lack of memory. */ + eid = ACLERR3460; + rv = ACLERRNOMEM; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + goto punt; +} + +/* + * Description (aclClientsDirCreate) + * + * This function allocates and initializes a new ACClients_t + * ACL directive. + * + * Arguments: + * + * None. + * + * Returns: + * + * If successful, a pointer to the new ACClients_t is returned. + * A shortage of dynamic memory is indicated by a null return value. + */ + +ACClients_t * aclClientsDirCreate() +{ + ACClients_t * acd; /* pointer to new ACClients_t */ + + acd = (ACClients_t *)MALLOC(sizeof(ACClients_t)); + if (acd != 0) { + memset((void *)acd, 0, sizeof(ACClients_t)); + } + + return acd; +} + +/* + * Description (aclCreate) + * + * This function creates a new ACL root structure. The caller + * specifies the name to be associated with the ACL. The ACL handle + * returned by this function is passed to other functions in this + * module when adding information to the ACL. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acc - pointer to an access control context + * aclname - pointer to ACL name string + * pacl - pointer to returned ACL handle + * + * Returns: + * + * The return value is zero if the ACL is created 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 aclCreate(NSErr_t * errp, ACContext_t * acc, char * aclname, ACL_t **pacl) +{ + ACL_t * acl; /* pointer to created ACL */ + int rv; /* result value */ + int eid; /* error id */ + + *pacl = 0; + + /* Allocate the ACL_t structure */ + acl = (ACL_t *) MALLOC(sizeof(ACL_t)); + if (acl == 0) goto err_nomem; + + /* Initialize the structure */ + memset((void *)acl, 0, sizeof(ACL_t)); + acl->acl_sym.sym_name = STRDUP(aclname); + acl->acl_sym.sym_type = ACLSYMACL; + acl->acl_acc = acc; + acl->acl_refcnt = 1; + + /* Add it to the symbol table for the specified context */ + rv = symTableAddSym(acc->acc_stp, &acl->acl_sym, (void *)acl); + if (rv < 0) goto err_addsym; + + /* Add it to the list of ACLs for the specified context */ + acl->acl_next = acc->acc_acls; + acc->acc_acls = acl; + acc->acc_refcnt += 1; + + *pacl = acl; + return 0; + + err_nomem: + rv = ACLERRNOMEM; + eid = ACLERR3200; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + goto done; + + err_addsym: + FREE(acl); + rv = ACLERRDUPSYM; + eid = ACLERR3220; + nserrGenerate(errp, rv, eid, ACL_Program, 1, aclname); + + done: + return rv; +} + +/* + * Description (aclDestroy) + * + * This function destroys an ACL structure and its sub-structures. + * It does not free the ACContext_t referenced by the ACL. + * + * Arguments: + * + * acl - pointer to ACL_t structure + */ + +void aclDestroy(ACL_t * acl) +{ + ACL_t **pacl; /* ACL list link pointer */ + ACDirective_t * acd; /* ACL directive pointer */ + ACDirective_t * nacd; /* next ACL directive pointer */ + + /* Is there an ACContext_t structure? */ + if (acl->acl_acc != 0) { + + /* Remove this ACL from the list in the ACContext_t structure */ + for (pacl = &acl->acl_acc->acc_acls; + *pacl != 0; pacl = &(*pacl)->acl_next) { + + if (*pacl == acl) { + *pacl = acl->acl_next; + acl->acl_acc->acc_refcnt -= 1; + break; + } + } + } + + /* Destroy each ACL directive */ + for (acd = acl->acl_dirf; acd != 0; acd = nacd) { + nacd = acd->acd_next; + aclDirectiveDestroy(acd); + } + + /* Free the ACL rights list if it is unnamed */ + if ((acl->acl_rights != 0) && (acl->acl_rights->rs_sym.sym_name == 0)) { + aclRightSpecDestroy(acl->acl_rights); + } + + /* Free the ACL name string, if any */ + if (acl->acl_sym.sym_name != 0) { + FREE(acl->acl_sym.sym_name); + } + + /* Free the ACL itself */ + FREE(acl); +} + +/* + * Description (aclDelete) + * + * This function removes a specified ACL from the symbol table + * associated with its ACL context, and then destroys the ACL + * structure and any unnamed objects it references (other than + * the ACL context). + * + * Arguments: + * + * acl - pointer to the ACL + */ + +void aclDelete(ACL_t * acl) +{ + ACContext_t * acc = acl->acl_acc; + + if ((acc != 0) && (acl->acl_sym.sym_name != 0)) { + symTableRemoveSym(acc->acc_stp, &acl->acl_sym); + } + + aclDestroy(acl); +} + +/* + * Description (aclDirectiveAdd) + * + * This function adds a given directive to a specified ACL. + * + * Arguments: + * + * acl - pointer to the ACL + * acd - pointer to the directive to be added + * + * Returns: + * + * If successful, the return value is zero. An error is indicated + * by a negative return value. + */ + +int aclDirectiveAdd(ACL_t * acl, ACDirective_t * acd) +{ + /* Add the directive to the end of the ACL's directive list */ + acd->acd_next = 0; + + if (acl->acl_dirl == 0) { + /* First entry in empty list */ + acl->acl_dirf = acd; + } + else { + /* Append to end of list */ + acl->acl_dirl->acd_next = acd; + } + + acl->acl_dirl = acd; + + return 0; +} + +/* + * Description (aclDirectiveCreate) + * + * This function allocates and initializes a new ACDirective_t + * structure, representing an ACL directive. + * + * Arguments: + * + * None. + * + * Returns: + * + * If successful, the return value is a pointer to a new ACDirective_t. + * Otherwise the return value is null. + */ + +ACDirective_t * aclDirectiveCreate() +{ + ACDirective_t * acd; + + acd = (ACDirective_t *) MALLOC(sizeof(ACDirective_t)); + if (acd != 0) { + memset((void *)acd, 0, sizeof(ACDirective_t)); + } + + return acd; +} + +/* + * Description (aclDirectiveDestroy) + * + * This function destroys an ACL directive structure. + * + * Arguments: + * + * acd - pointer to ACL directive structure + */ + +void aclDirectiveDestroy(ACDirective_t * acd) +{ + switch (acd->acd_action) { + case ACD_ALLOW: + case ACD_DENY: + { + ACClients_t * acp; + ACClients_t * nacp; + + /* Free a list of ACClients_t structures */ + for (acp = acd->acd_cl; acp != 0; acp = nacp) { + nacp = acp->cl_next; + + /* Free the HostSpec_t if it's there and unnamed */ + if ((acp->cl_host != 0) && + (acp->cl_host->hs_sym.sym_name == 0)) { + aclHostSpecDestroy(acp->cl_host); + } + + /* Free the UserSpec_t if it's there and unnamed */ + if ((acp->cl_user != 0) && + (acp->cl_user->us_sym.sym_name == 0)) { + aclUserSpecDestroy(acp->cl_user); + } + } + } + break; + + case ACD_AUTH: + { + RealmSpec_t * rsp = acd->acd_auth.au_realm; + + /* Destroy the RealmSpec_t if it's unnamed */ + if ((rsp != 0) && (rsp->rs_sym.sym_name == 0)) { + aclRealmSpecDestroy(rsp); + } + } + break; + } + + FREE(acd); +} + +/* + * Description (aclDNSSpecDestroy) + * + * This function destroys an entry in a DNS filter. It is intended + * mainly to be used by aclHostSpecDestroy(). + * + * Arguments: + * + * sym - pointer to Symbol_t for DNS filter entry + * argp - unused (must be zero) + * + * Returns: + * + * The return value is SYMENUMREMOVE. + */ + +int aclDNSSpecDestroy(Symbol_t * sym, void * argp) +{ + if (sym != 0) { + + /* Free the DNS specification string if any */ + if (sym->sym_name != 0) { + FREE(sym->sym_name); + } + + /* Free the Symbol_t structure */ + FREE(sym); + } + + /* Indicate that the symbol table entry should be removed */ + return SYMENUMREMOVE; +} + +/* + * Description (aclHostSpecDestroy) + * + * This function destroys a HostSpec_t structure and its sub-structures. + * + * Arguments: + * + * hsp - pointer to HostSpec_t structure + */ + +void aclHostSpecDestroy(HostSpec_t * hsp) +{ + if (hsp == 0) return; + + /* Free the IP filter if any */ + if (hsp->hs_host.inh_ipf.ipf_tree != 0) { + IPNode_t * ipn; /* current node pointer */ + IPNode_t * parent; /* parent node pointer */ + int i; + + /* Traverse tree, freeing nodes */ + for (parent = hsp->hs_host.inh_ipf.ipf_tree; parent != NULL; ) { + + /* Look for a link to a child node */ + for (i = 0; i < IPN_NLINKS; ++i) { + ipn = parent->ipn_links[i]; + if (ipn != NULL) break; + } + + /* Any children for the parent node? */ + if (ipn == NULL) { + + /* Otherwise back up the tree */ + ipn = parent; + parent = ipn->ipn_parent; + + /* Free the lower node */ + FREE(ipn); + continue; + } + + /* + * Found a child node for the current parent. + * NULL out the downward link and check it out. + */ + parent->ipn_links[i] = NULL; + + /* Is it a leaf? */ + if (ipn->ipn_type == IPN_LEAF) { + /* Yes, free it */ + FREE(ipn); + continue; + } + + /* No, step down the tree */ + parent = ipn; + } + } + + /* Free the DNS filter if any */ + if (hsp->hs_host.inh_dnf.dnf_hash != 0) { + + /* Destroy each entry in the symbol table */ + symTableEnumerate(hsp->hs_host.inh_dnf.dnf_hash, 0, + aclDNSSpecDestroy); + + /* Destroy the symbol table itself */ + symTableDestroy(hsp->hs_host.inh_dnf.dnf_hash, 0); + } + + /* Free the symbol name if any */ + if (hsp->hs_sym.sym_name != 0) { + FREE(hsp->hs_sym.sym_name); + } + + /* Free the HostSpec_t structure */ + FREE(hsp); +} + +/* + * Description (aclRealmSpecDestroy) + * + * This function destroys a RealmSpec_t structure. + * + * Arguments: + * + * rsp - pointer to RealmSpec_t structure + */ + +void aclRealmSpecDestroy(RealmSpec_t * rsp) +{ + /* Close the realm authentication database if it appears open */ + if ((rsp->rs_realm.rlm_aif != 0) && + (rsp->rs_realm.rlm_authdb != 0)) { + (*rsp->rs_realm.rlm_aif->aif_close)(rsp->rs_realm.rlm_authdb, 0); + } + + /* Free the prompt string if any */ + if (rsp->rs_realm.rlm_prompt != 0) { + FREE(rsp->rs_realm.rlm_prompt); + } + + /* Free the database filename string if any */ + if (rsp->rs_realm.rlm_dbname != 0) { + FREE(rsp->rs_realm.rlm_dbname); + } + + /* Free the realm specification name if any */ + if (rsp->rs_sym.sym_name != 0) { + FREE(rsp->rs_sym.sym_name); + } + + /* Free the RealmSpec_t structure */ + FREE(rsp); +} + +/* + * Description (aclRightDef) + * + * This function find or creates an access right with a specified + * name in a given ACL context. If a new access right definition + * is created, it assigns a unique integer identifier to the the + * right, adds it to the ACL context symbol table and to the + * list of all access rights for the context. Note that access + * right names are case-insensitive. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * acc - pointer to an access control context + * rname - access right name (e.g. "GET") + * prd - pointer to returned RightDef_t pointer + * (may be null) + * + * Returns: + * + * The return value is zero if the access right definition already + * existed or one if it was created 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 aclRightDef(NSErr_t * errp, + ACContext_t * acc, char * rname, RightDef_t **prd) +{ + RightDef_t * rdp; /* pointer to right definition */ + int eid; /* error id code */ + int rv; /* result value */ + static int last_rid = 0; /* last assigned right id */ + + /* See if there's already a symbol table entry for it */ + rv = symTableFindSym(acc->acc_stp, rname, ACLSYMRIGHT, (void **)&rdp); + if (rv) { + + /* No, create an entry */ + + /* Allocate a right definition structure and initialize it */ + rdp = (RightDef_t *)MALLOC(sizeof(RightDef_t)); + if (rdp == 0) goto err_nomem; + + rdp->rd_sym.sym_name = STRDUP(rname); + rdp->rd_sym.sym_type = ACLSYMRIGHT; + rdp->rd_next = acc->acc_rights; + rdp->rd_id = ++last_rid; + + /* Add the right name to the symbol table for the ACL context */ + rv = symTableAddSym(acc->acc_stp, &rdp->rd_sym, (void *)rdp); + if (rv) goto err_stadd; + + /* Add the right definition to the list for the ACL context */ + acc->acc_rights = rdp; + + /* Indicate a new right definition was created */ + rv = 1; + } + + /* Return a pointer to the RightDef_t structure if indicated */ + if (prd != 0) *prd = rdp; + + return rv; + + err_nomem: + eid = ACLERR3600; + rv = ACLERRNOMEM; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + goto punt; + + err_stadd: + FREE(rdp->rd_sym.sym_name); + FREE(rdp); + eid = ACLERR3620; + rv = ACLERRDUPSYM; + nserrGenerate(errp, rv, eid, ACL_Program, 1, rname); + + punt: + return rv; +} + +/* + * Description (aclRightSpecDestroy) + * + * This function destroys a RightSpec_t structure. + * + * Arguments: + * + * rsp - pointer to RightSpec_t structure + */ + +void aclRightSpecDestroy(RightSpec_t * rsp) +{ + if (rsp != 0) { + + UILFREE(&rsp->rs_list); + + if (rsp->rs_sym.sym_name != 0) { + FREE(rsp->rs_sym.sym_name); + } + + FREE(rsp); + } +} + +/* + * Description (aclUserSpecCreate) + * + * This function allocates and initializes a new UserSpec_t + * structure, representing a list of users and groups. + * + * Arguments: + * + * None. + * + * Returns: + * + * If successful, the return value is a pointer to a new UserSpec_t. + * Otherwise the return value is null. + */ + +UserSpec_t * aclUserSpecCreate() +{ + UserSpec_t * usp; + + usp = (UserSpec_t *) MALLOC(sizeof(UserSpec_t)); + if (usp != 0) { + memset((void *)usp, 0, sizeof(UserSpec_t)); + usp->us_sym.sym_type = ACLSYMUSER; + } + + return usp; +} + +/* + * Description (aclUserSpecDestroy) + * + * This function destroys a UserSpec_t structure. + * + * Arguments: + * + * usp - pointer to UserSpec_t structure + */ + +void aclUserSpecDestroy(UserSpec_t * usp) +{ + if (usp != 0) { + + UILFREE(&usp->us_user.uu_user); + UILFREE(&usp->us_user.uu_group); + + if (usp->us_sym.sym_name != 0) { + FREE(usp->us_sym.sym_name); + } + + FREE(usp); + } +} diff --git a/lib/libaccess/aclcache.cpp b/lib/libaccess/aclcache.cpp new file mode 100644 index 00000000..34d2ecfb --- /dev/null +++ b/lib/libaccess/aclcache.cpp @@ -0,0 +1,579 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <base/nsassert.h> +#include <base/crit.h> +#include <base/ereport.h> +#include <plhash.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/aclglobal.h> +#include <libaccess/usrcache.h> +#include <libaccess/las.h> +#include <libaccess/ldapacl.h> +#include "aclutil.h" +#include "permhash.h" +#include "aclcache.h" + + +static CRITICAL acl_hash_crit = NULL; /* Controls Global Hash */ + +enum { + ACL_URI_HASH, + ACL_URI_GET_HASH +}; + +/* ACL_ListHashKeyHash + * Given an ACL List address, computes a randomized hash value of the + * ACL structure pointer addresses by simply adding them up. Returns + * the resultant hash value. + */ +static PLHashNumber +ACL_ListHashKeyHash(const void *Iacllist) +{ + PLHashNumber hash=0; + ACLWrapper_t *wrap; + ACLListHandle_t *acllist=(ACLListHandle_t *)Iacllist; + + for (wrap=acllist->acl_list_head; wrap; wrap=wrap->wrap_next) { + hash += (PLHashNumber)(PRSize)wrap->acl; + } + + return (hash); +} + +/* ACL_ListHashKeyCompare + * Given two acl lists, compares the addresses of the acl pointers within + * them to see if theyre identical. Returns 1 if equal, 0 otherwise. + */ +static int +ACL_ListHashKeyCompare(const void *Iacllist1, const void *Iacllist2) +{ + ACLWrapper_t *wrap1, *wrap2; + ACLListHandle_t *acllist1=(ACLListHandle_t *)Iacllist1; + ACLListHandle_t *acllist2=(ACLListHandle_t *)Iacllist2; + + if (acllist1->acl_count != acllist2->acl_count) + return 0; + + wrap1 = acllist1->acl_list_head; + wrap2 = acllist2->acl_list_head; + + while ((wrap1 != NULL) && (wrap2 != NULL)) { + if (wrap1->acl != wrap2->acl) + return 0; + wrap1 = wrap1->wrap_next; + wrap2 = wrap2->wrap_next; + } + + if ((wrap1 != NULL) || (wrap2 != NULL)) + return 0; + else + return 1; +} + +/* ACL_ListHashValueCompare + * Returns 1 if equal, 0 otherwise + */ +static int +ACL_ListHashValueCompare(const void *acllist1, const void *acllist2) +{ + + return (acllist1 == acllist2); +} + +void +ACL_ListHashInit() +{ + ACLListHash = PR_NewHashTable(200, + ACL_ListHashKeyHash, + ACL_ListHashKeyCompare, + ACL_ListHashValueCompare, + &ACLPermAllocOps, + NULL); + if (ACLListHash == NULL) { + ereport(LOG_SECURITY, "Unable to allocate ACL List Hash\n"); + return; + } + + return; +} + +static void +ACL_ListHashDestroy() +{ + if (ACLListHash) { + PR_HashTableDestroy(ACLListHash); + ACLListHash = NULL; + } + + return; +} + +/* ACL_ListHashUpdate + * Typically called with the &rq->acllist. Checks if the newly generated + * acllist matches one that's already been created. If so, toss the new + * list and set the pointer to the old list in its place. + */ +void +ACL_ListHashUpdate(ACLListHandle_t **acllistp) +{ + NSErr_t *errp = 0; + ACLListHandle_t *tmp_acllist; + + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + + tmp_acllist = (ACLListHandle_t *)PR_HashTableLookup(ACLListHash, *acllistp); + if (tmp_acllist && tmp_acllist != *acllistp) { + NS_ASSERT(*acllistp && *acllistp != ACL_LIST_NO_ACLS); + ACL_ListDestroy(errp, *acllistp); + *acllistp = tmp_acllist; + NS_ASSERT(ACL_CritHeld()); + tmp_acllist->ref_count++; /* we're gonna use it */ + } else { /* Wasn't in the list */ + PR_HashTableAdd(ACLListHash, *acllistp, *acllistp); + } + + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; +} + +/* ACL_ListCacheEnter + * In some cases, the URI cache is useless. E.g. when virtual servers are used. + * When that happens, the List Cache is still useful, because the cached ACL + * List has the Eval cache in it, plus any LAS caches. + */ +NSAPI_PUBLIC void +ACL_ListHashEnter(ACLListHandle_t **acllistp) +{ + NSErr_t *errp = 0; + + ACL_CritEnter(); + + /* Look for a matching ACL List and use it if we find one. */ + if (*acllistp) { + NS_ASSERT(*acllistp != ACL_LIST_NO_ACLS); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + ACL_ListHashUpdate(acllistp); + } else { + *acllistp = ACL_LIST_NO_ACLS; + } + + ACL_CritExit(); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; +} + +/* ACL_ListHashCheck + * When Virtual Servers are active, and the ACL URI cache is inactive, someone + * with an old ACL List pointer can check to see if it's still valid. This will + * also increment the reference count on it. + */ +NSAPI_PUBLIC int +ACL_ListHashCheck(ACLListHandle_t **acllistp) +{ + ACLListHandle_t *tmp_acllist; + + if (*acllistp == ACL_LIST_NO_ACLS) return 1; + + ACL_CritEnter(); + + tmp_acllist = (ACLListHandle_t *)PR_HashTableLookup(ACLListHash, *acllistp); + if (tmp_acllist) { + NS_ASSERT(*acllistp && *acllistp != ACL_LIST_NO_ACLS); + *acllistp = tmp_acllist; + NS_ASSERT(ACL_CritHeld()); + tmp_acllist->ref_count++; /* we're gonna use it */ + ACL_CritExit(); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return 1; /* Normal path */ + } else { /* Wasn't in the list */ + ACL_CritExit(); + return 0; + } + +} + + +void +ACL_UriHashDestroy(void) +{ + if (acl_uri_hash) { + PR_HashTableDestroy(acl_uri_hash); + acl_uri_hash = NULL; + } + if (acl_uri_get_hash) { + PR_HashTableDestroy(acl_uri_get_hash); + acl_uri_get_hash = NULL; + } + pool_destroy((void **)acl_uri_hash_pool); + acl_uri_hash_pool = NULL; + +} + +void +ACL_Destroy(void) +{ + ACL_ListHashDestroy(); + ACL_UriHashDestroy(); + ACL_LasHashDestroy(); +} + +/* Only used in ASSERT statements to verify that we have the lock */ +int +ACL_CritHeld(void) +{ + return (crit_owner_is_me(acl_hash_crit)); +} + +NSAPI_PUBLIC void +ACL_CritEnter(void) +{ + crit_enter(acl_hash_crit); +} + +NSAPI_PUBLIC void +ACL_CritExit(void) +{ + crit_exit(acl_hash_crit); +} + +void +ACL_CritInit(void) +{ + acl_hash_crit = crit_init(); +} + +void +ACL_UriHashInit(void) +{ + acl_uri_hash = PR_NewHashTable(200, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + acl_uri_get_hash = PR_NewHashTable(200, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + acl_uri_hash_pool = pool_create(); +} + +/* ACL_CacheCheck + * INPUT + * uri A URI string pointer + * acllistp A pointer to an acllist placeholder. E.g. &rq->acllist + * OUTPUT + * return 1 if cached. 0 if not. The reference count on the ACL List + * is INCREMENTED, and will be decremented when ACL_EvalDestroy or + * ACL_ListDecrement is + * called. + */ +int +ACL_INTCacheCheck(int which, char *uri, ACLListHandle_t **acllistp) +{ + PLHashTable *hash; + NS_ASSERT(uri && acl_uri_hash && acl_uri_get_hash); + + /* ACL cache: If the ACL List is already in the cache, there's no need + * to go through the pathcheck directives. + * NULL means that the URI hasn't been accessed before. + * ACL_LIST_NO_ACLS + * means that the URI has no ACLs. + * Anything else is a pointer to the acllist. + */ + ACL_CritEnter(); + + /* Get the pointer to the hash table after acquiring the lock */ + if (which == ACL_URI_HASH) + hash = acl_uri_hash; + else + hash = acl_uri_get_hash; + + *acllistp = (ACLListHandle_t *)PR_HashTableLookup(hash, uri); + if (*acllistp != NULL) { + if (*acllistp != ACL_LIST_NO_ACLS) { + NS_ASSERT((*acllistp)->ref_count >= 0); + NS_ASSERT(ACL_CritHeld()); + (*acllistp)->ref_count++; + } + ACL_CritExit(); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return 1; /* Normal path */ + } + + ACL_CritExit(); + return 0; +} + +int +ACL_CacheCheckGet(char *uri, ACLListHandle_t **acllistp) +{ + return (ACL_INTCacheCheck(ACL_URI_GET_HASH, uri, acllistp)); +} + +int +ACL_CacheCheck(char *uri, ACLListHandle_t **acllistp) +{ + return (ACL_INTCacheCheck(ACL_URI_HASH, uri, acllistp)); +} + + +/* ACL_CacheEnter + * INPUT + * acllist or 0 if there were no ACLs that applied. + * OUTPUT + * The acllist address may be changed if it matches an existing one. + */ +static void +ACL_INTCacheEnter(int which, char *uri, ACLListHandle_t **acllistp) +{ + ACLListHandle_t *tmpacllist; + NSErr_t *errp = 0; + PLHashTable *hash; + + NS_ASSERT(uri); + + ACL_CritEnter(); + + /* Get the pointer to the hash table after acquiring the lock */ + if (which == ACL_URI_HASH) + hash = acl_uri_hash; + else + hash = acl_uri_get_hash; + + /* Check again (now that we're in the critical section) to see if + * someone else created an ACL List for this URI. If so, discard the + * list that we made and replace it with the one just found. + */ + tmpacllist = (ACLListHandle_t *)PR_HashTableLookup(hash, uri); + if (tmpacllist != NULL) { + if (tmpacllist != ACL_LIST_NO_ACLS) { + NS_ASSERT(ACL_CritHeld()); + tmpacllist->ref_count++; /* we're going to use it */ + } + ACL_CritExit(); + if (*acllistp && *acllistp != ACL_LIST_NO_ACLS) { + ACL_ListDestroy(errp, *acllistp); + } + *acllistp = tmpacllist; + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; + } + + /* Didn't find another list, so put ours in. */ + /* Look for a matching ACL List and use it if we find one. */ + if (*acllistp) { + NS_ASSERT(*acllistp != ACL_LIST_NO_ACLS); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + ACL_ListHashUpdate(acllistp); + } else { + *acllistp = ACL_LIST_NO_ACLS; + } + PR_HashTableAdd(hash, pool_strdup((void **)acl_uri_hash_pool, uri), (void *)*acllistp); + + ACL_CritExit(); + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; +} + +void +ACL_CacheEnter(char *uri, ACLListHandle_t **acllistp) +{ + ACL_INTCacheEnter(ACL_URI_HASH, uri, acllistp); + return; +} + +void +ACL_CacheEnterGet(char *uri, ACLListHandle_t **acllistp) +{ + ACL_INTCacheEnter(ACL_URI_GET_HASH, uri, acllistp); + return; +} + +/* ACL_AddAclName + * Adds the ACLs for just the terminal object specified in a pathname. + * INPUT + * path The filesystem pathname of the terminal object. + * acllistp The address of the list of ACLs found thus far. + * Could be NULL. If so, a new acllist will be allocated (if any + * acls are found). Otherwise the existing list will be added to. + * masterlist Usually acl_root_30. + */ +void +ACL_AddAclName(char *path, ACLListHandle_t **acllistp, ACLListHandle_t +*masterlist) +{ + ACLHandle_t *acl; + NSErr_t *errp = 0; + +#ifdef XP_WIN32 + acl = ACL_ListFind(errp, masterlist, path, ACL_CASE_INSENSITIVE); +#else + acl = ACL_ListFind(errp, masterlist, path, ACL_CASE_SENSITIVE); +#endif + if (!acl) + return; + + NS_ASSERT(ACL_AssertAcl(acl)); + + if (!*acllistp) + *acllistp = ACL_ListNew(errp); + ACL_ListAppend(NULL, *acllistp, acl, 0); + + NS_ASSERT(ACL_AssertAcllist(*acllistp)); + return; +} + + +/* ACL_GetPathAcls + * Adds the ACLs for all directories plus the terminal object along a given + * filesystem pathname. For each pathname component, look for the name, the + * name + "/", and the name + "/*". The last one is because the resource + * picker likes to postpend "/*" for directories. + * INPUT + * path The filesystem pathname of the terminal object. + * acllistp The address of the list of ACLs found thus far. + * Could be NULL. If so, a new acllist will be allocated (if any + * acls are found). Otherwise the existing list will be added to. + * prefix A string to be prepended to the path component when looking + * for a matching ACL tag. + */ +void +ACL_GetPathAcls(char *path, ACLListHandle_t **acllistp, char *prefix, +ACLListHandle_t *masterlist) +{ + char *slashp=path; + int slashidx; + char ppath[ACL_PATH_MAX]; + int prefixlen; + char *dst; + + NS_ASSERT(path); + NS_ASSERT(prefix); + + dst = strncpy(ppath, prefix, ACL_PATH_MAX); + if (dst >= (ppath+ACL_PATH_MAX-1)) { + ereport(LOG_SECURITY, "Abort - the path is too long for ACL_GetPathAcls to handle\n"); + abort(); + } + prefixlen = strlen(ppath); + + /* Handle the first "/". i.e. the root directory */ + if (*path == '/') { + ppath[prefixlen]='/'; + ppath[prefixlen+1]='\0'; + ACL_AddAclName(ppath, acllistp, masterlist); + strcat(ppath, "*"); + ACL_AddAclName(ppath, acllistp, masterlist); + slashp = path; + } + + do { + slashp = strchr(++slashp, '/'); + if (slashp) { + slashidx = slashp - path; + strncpy(&ppath[prefixlen], path, slashidx); + ppath[slashidx+prefixlen] = '\0'; + ACL_AddAclName(ppath, acllistp, masterlist); + /* Must also handle "/a/b/" in addition to "/a/b" */ + strcat(ppath, "/"); + ACL_AddAclName(ppath, acllistp, masterlist); + strcat(ppath, "*"); + ACL_AddAclName(ppath, acllistp, masterlist); + continue; + } + strcpy(&ppath[prefixlen], path); + ACL_AddAclName(ppath, acllistp, masterlist); + strcat(ppath, "/"); + ACL_AddAclName(ppath, acllistp, masterlist); + strcat(ppath, "*"); + ACL_AddAclName(ppath, acllistp, masterlist); + break; + } while (slashp); + +} + + +static int get_is_owner_default (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + /* Make sure we don't generate error "all getters declined" message from + * ACL_GetAttribute. + */ + PListInitProp(subject, ACL_ATTR_IS_OWNER_INDEX, ACL_ATTR_IS_OWNER, + "true", 0); + + return LAS_EVAL_TRUE; +} + + +NSAPI_PUBLIC int +ACL_Init(void) +{ + ACL_InitAttr2Index(); + ACLGlobal = (ACLGlobal_p)PERM_CALLOC(sizeof(ACLGlobal_s)); + oldACLGlobal = (ACLGlobal_p)PERM_CALLOC(sizeof(ACLGlobal_s)); + NS_ASSERT(ACLGlobal && oldACLGlobal); + ACL_DATABASE_POOL = pool_create(); + ACL_METHOD_POOL = pool_create(); + ACL_CritInit(); + ACL_UriHashInit(); + ACL_ListHashInit(); + ACL_LasHashInit(); + ACL_Init2(); + init_ldb_rwlock(); + ACL_RegisterInit(); + + return 0; +} + +/* This one gets called at startup AND at cache flush time. */ +void +ACL_Init2(void) +{ + + /* Register the ACL functions */ + ACL_LasRegister(NULL, "timeofday", LASTimeOfDayEval, LASTimeOfDayFlush); + ACL_LasRegister(NULL, "dayofweek", LASDayOfWeekEval, LASDayOfWeekFlush); + ACL_LasRegister(NULL, "ip", LASIpEval, LASIpFlush); + ACL_LasRegister(NULL, "dns", LASDnsEval, LASDnsFlush); + ACL_LasRegister(NULL, "dnsalias", LASDnsEval, LASDnsFlush); + ACL_LasRegister(NULL, "group", LASGroupEval, (LASFlushFunc_t)NULL); + ACL_LasRegister(NULL, "user", LASUserEval, (LASFlushFunc_t)NULL); +#ifdef MCC_ADMSERV + ACL_LasRegister(NULL, "program", LASProgramEval, (LASFlushFunc_t)NULL); +#endif + + ACL_AttrGetterRegister(NULL, ACL_ATTR_USERDN, + get_userdn_ldap, + ACL_METHOD_ANY, ACL_DBTYPE_ANY, + ACL_AT_END, NULL); + return; +} + +NSAPI_PUBLIC int +ACL_InitPostMagnus(void) +{ + int rv; + + rv = ACL_AttrGetterRegister(NULL, ACL_ATTR_IS_OWNER, + get_is_owner_default, + ACL_METHOD_ANY, ACL_DBTYPE_ANY, + ACL_AT_END, NULL); + return rv; +} + +NSAPI_PUBLIC int +ACL_LateInitPostMagnus(void) +{ + return acl_usr_cache_init(); +} diff --git a/lib/libaccess/aclcache.h b/lib/libaccess/aclcache.h new file mode 100644 index 00000000..1f375bcf --- /dev/null +++ b/lib/libaccess/aclcache.h @@ -0,0 +1,27 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef CACHE_H +#define CACHE_H + +NSPR_BEGIN_EXTERN_C + +extern void ACL_ListHashInit(void); +extern void ACL_ListHashUpdate(ACLListHandle_t **acllistp); +extern void ACL_Destroy(void); +extern int ACL_CritHeld(void); +extern void ACL_CritInit(void); +extern void ACL_UriHashInit(void); +extern void ACL_UriHashDestroy(void); +extern int ACL_CacheCheck(char *uri, ACLListHandle_t **acllist_p); +extern void ACL_CacheEnter(char *uri, ACLListHandle_t **acllist_p); +extern void ACL_CacheAbort(ACLListHandle_t **acllist_p); +extern void ACL_Init2(void); +extern int ACL_RegisterInit (); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/aclerror.cpp b/lib/libaccess/aclerror.cpp new file mode 100644 index 00000000..2cbf2874 --- /dev/null +++ b/lib/libaccess/aclerror.cpp @@ -0,0 +1,246 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (aclerror.c) + * + * This module provides error-handling facilities for ACL-related + * errors. + */ + +#include "base/systems.h" +#ifdef NSPR20 +#include "prprf.h" +#else +#include "nspr/prprf.h" +#endif +#include <base/nsassert.h> +#include "libaccess/nserror.h" +#include "libaccess/nsautherr.h" +#include "libaccess/aclerror.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +#define aclerrnomem XP_GetAdminStr(DBT_AclerrfmtAclerrnomem) +#define aclerropen XP_GetAdminStr(DBT_AclerrfmtAclerropen) +#define aclerrdupsym1 XP_GetAdminStr(DBT_AclerrfmtAclerrdupsym1) +#define aclerrdupsym3 XP_GetAdminStr(DBT_AclerrfmtAclerrdupsym3) +#define aclerrsyntax XP_GetAdminStr(DBT_AclerrfmtAclerrsyntax) +#define aclerrundef XP_GetAdminStr(DBT_AclerrfmtAclerrundef) +#define aclaclundef XP_GetAdminStr(DBT_AclerrfmtAclaclundef) +#define aclerradb XP_GetAdminStr(DBT_AclerrfmtAclerradb) +#define aclerrparse1 XP_GetAdminStr(DBT_AclerrfmtAclerrparse1) +#define aclerrparse2 XP_GetAdminStr(DBT_AclerrfmtAclerrparse2) +#define aclerrparse3 XP_GetAdminStr(DBT_AclerrfmtAclerrparse3) +#define aclerrnorlm XP_GetAdminStr(DBT_AclerrfmtAclerrnorlm) +#define unknownerr XP_GetAdminStr(DBT_AclerrfmtUnknownerr) +#define aclerrinternal XP_GetAdminStr(DBT_AclerrfmtAclerrinternal) +#define aclerrinval XP_GetAdminStr(DBT_AclerrfmtAclerrinval) +#define aclerrfail XP_GetAdminStr(DBT_AclerrfmtAclerrfail) +#define aclerrio XP_GetAdminStr(DBT_AclerrfmtAclerrio) + +/* + * Description (aclErrorFmt) + * + * This function formats an ACL error message into a buffer provided + * by the caller. The ACL error information is passed in an error + * list structure. The caller can indicate how many error frames + * should be processed. A newline is inserted between messages for + * different error frames. The error frames on the error list are + * all freed, regardless of the maximum depth for traceback. + * + * Arguments: + * + * errp - error frame list pointer + * msgbuf - pointer to error message buffer + * maxlen - maximum length of generated message + * maxdepth - maximum depth for traceback + */ + +void aclErrorFmt(NSErr_t * errp, char * msgbuf, int maxlen, int maxdepth) +{ + NSEFrame_t * efp; /* error frame pointer */ + int len; /* length of error message text */ + int depth = 0; /* current depth */ + + msgbuf[0] = 0; + + while ((efp = errp->err_first) != 0) { + + /* Stop if the message buffer is full */ + if (maxlen <= 0) break; + + if (depth > 0) { + /* Put a newline & tab between error frame messages */ + *msgbuf++ = '\n'; + if (--maxlen <= 0) break; + *msgbuf++ = '\t'; + if (--maxlen <= 0) break; + } + + if (!strcmp(efp->ef_program, ACL_Program)) { + + /* Identify the facility generating the error and the id number */ + len = PR_snprintf(msgbuf, maxlen, + "[%s%d] ", efp->ef_program, efp->ef_errorid); + msgbuf += len; + maxlen -= len; + + if (maxlen <= 0) break; + + len = 0; + + switch (efp->ef_retcode) { + + case ACLERRFAIL: + case ACLERRNOMEM: + case ACLERRINTERNAL: + case ACLERRINVAL: + switch (efp->ef_errc) { + case 3: + PR_snprintf(msgbuf, maxlen, efp->ef_errv[0], efp->ef_errv[1], efp->ef_errv[2]); + break; + case 2: + PR_snprintf(msgbuf, maxlen, efp->ef_errv[0], efp->ef_errv[1]); + break; + case 1: + strncpy(msgbuf, efp->ef_errv[0], maxlen); + break; + default: + NS_ASSERT(0); /* don't break -- continue into case 0 */ + case 0: + switch (efp->ef_retcode) { + case ACLERRFAIL: + strncpy(msgbuf, XP_GetAdminStr(DBT_AclerrfmtAclerrfail), maxlen); + break; + case ACLERRNOMEM: + strncpy(msgbuf, aclerrnomem, maxlen); + break; + case ACLERRINTERNAL: + strncpy(msgbuf, aclerrinternal, maxlen); + break; + case ACLERRINVAL: + strncpy(msgbuf, aclerrinval, maxlen); + break; + } + break; + } + msgbuf[maxlen-1] = '\0'; + len = strlen(msgbuf); + break; + + case ACLERROPEN: + /* File open error: filename, system_errmsg */ + if (efp->ef_errc == 2) { + len = PR_snprintf(msgbuf, maxlen, aclerropen, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + case ACLERRDUPSYM: + /* Duplicate symbol */ + if (efp->ef_errc == 1) { + /* Duplicate symbol: filename, line#, symbol */ + len = PR_snprintf(msgbuf, maxlen, aclerrdupsym1, + efp->ef_errv[0]); + } + else if (efp->ef_errc == 3) { + /* Duplicate symbol: symbol */ + len = PR_snprintf(msgbuf, maxlen, aclerrdupsym3, + efp->ef_errv[0], efp->ef_errv[1], + efp->ef_errv[2]); + } + break; + + case ACLERRSYNTAX: + if (efp->ef_errc == 2) { + /* Syntax error: filename, line# */ + len = PR_snprintf(msgbuf, maxlen, aclerrsyntax, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + case ACLERRUNDEF: + if (efp->ef_errorid == ACLERR3800) { + /* Undefined symbol: acl, method/database name */ + len = PR_snprintf(msgbuf, maxlen, aclaclundef, + efp->ef_errv[0], efp->ef_errv[1], + efp->ef_errv[2]); + } + else if (efp->ef_errc == 3) { + /* Undefined symbol: filename, line#, symbol */ + len = PR_snprintf(msgbuf, maxlen, aclerrundef, + efp->ef_errv[0], efp->ef_errv[1], + efp->ef_errv[2]); + } + break; + + case ACLERRADB: + if (efp->ef_errc == 2) { + /* Authentication database error: DB name, symbol */ + len = PR_snprintf(msgbuf, maxlen, aclerradb, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + case ACLERRPARSE: + if (efp->ef_errc == 2) { + /* Parse error: filename, line# */ + len = PR_snprintf(msgbuf, maxlen, aclerrparse2, + efp->ef_errv[0], efp->ef_errv[1]); + } + else if (efp->ef_errc == 3) { + /* Parse error: filename, line#, token */ + len = PR_snprintf(msgbuf, maxlen, aclerrparse3, + efp->ef_errv[0], efp->ef_errv[1], + efp->ef_errv[2]); + } + else if (efp->ef_errc == 1) { + /* Parse error: line or pointer */ + len = PR_snprintf(msgbuf, maxlen, aclerrparse1, + efp->ef_errv[0]); + } + break; + + case ACLERRNORLM: + if (efp->ef_errc == 1) { + /* No realm: name */ + len = PR_snprintf(msgbuf, maxlen, aclerrnorlm, + efp->ef_errv[0]); + } + break; + + case ACLERRIO: + if (efp->ef_errc == 2) { + len = PR_snprintf(msgbuf, maxlen, aclerrio, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + default: + len = PR_snprintf(msgbuf, maxlen, unknownerr, efp->ef_retcode); + break; + } + } + else if (!strcmp(efp->ef_program, NSAuth_Program)) { + nsadbErrorFmt(errp, msgbuf, maxlen, maxdepth - depth); + } + else { + len = PR_snprintf(msgbuf, maxlen, unknownerr, efp->ef_retcode); + } + + msgbuf += len; + maxlen -= len; + + /* Free this frame */ + nserrFFree(errp, efp); + + if (++depth >= maxdepth) break; + } + + /* Free any remaining error frames */ + nserrDispose(errp); +} diff --git a/lib/libaccess/acleval.cpp b/lib/libaccess/acleval.cpp new file mode 100644 index 00000000..a2be1f7b --- /dev/null +++ b/lib/libaccess/acleval.cpp @@ -0,0 +1,556 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (acleval.c) + * + * This module provides functions for evaluating Access Control List + * (ACL) structures in memory. + * + */ + +#include "base/systems.h" +#include "netsite.h" +#include "libaccess/symbols.h" +#include "libaccess/aclerror.h" +#include "libaccess/acleval.h" +#include <assert.h> + +/* + * Description (RLMEQUIV) + * + * Macro for realm comparison. Both realm pointers must be non-null. + * The realms are equivalent if the pointers are equal, or if the + * authentication methods and database names are the same. The + * prompt string is not considered. + */ +#define RLMEQUIV(rlm1, rlm2) (((rlm1) != 0) && ((rlm2) != 0) && \ + (((rlm1) == (rlm2)) || \ + (((rlm1)->rlm_ameth == (rlm2)->rlm_ameth) && \ + ((rlm1)->rlm_dbname != 0) && \ + ((rlm2)->rlm_dbname != 0) && \ + !strcmp((rlm1)->rlm_dbname, \ + (rlm2)->rlm_dbname)))) + +int aclDNSLookup(DNSFilter_t * dnf, char * dnsspec, int fqdn, char **match) +{ + char * subdns; /* suffix of client DNS name */ + void * table; /* hash table pointer */ + Symbol_t * sym; /* DNS spec symbol pointer */ + int rv; /* result value */ + + fqdn = (fqdn) ? 1 : 0; + + if (match) *match = 0; + + /* Handle null or empty filter */ + if ((dnf == 0) || (dnf->dnf_hash == 0)) { + + return ACL_NOMATCH; + } + + /* Got the client's DNS name? */ + if (!dnsspec || !*dnsspec) { + /* No, use special one */ + dnsspec = "unknown"; + } + + /* Get hash table pointer */ + table = dnf->dnf_hash; + + /* + * Look up each possible suffix for the client domain name, + * starting with the entire string, and working toward the + * last component. + */ + + subdns = dnsspec; + + while (subdns != 0) { + + /* Look up the domain name suffix in the hash table */ + rv = symTableFindSym(table, subdns, fqdn, (void **)&sym); + if (rv == 0) break; + + /* Step to the next level */ + if (subdns[0] == '.') subdns += 1; + subdns = strchr(subdns, '.'); + + /* If it was fully qualified, now it's not */ + fqdn = 0; + } + + /* One more possibility if nothing found yet... */ + if (rv) { + rv = symTableFindSym(table, "*", 0, (void **)&sym); + } + + if (rv == 0) { + if (match) *match = sym->sym_name; + rv = ACL_DNMATCH; + } + else rv = ACL_NOMATCH; + + return rv; +} + +int aclIPLookup(IPFilter_t * ipf, IPAddr_t ipaddr, void **match) +{ + IPLeaf_t * leaf; /* radix tree leaf pointer */ + IPAddr_t bitmask; /* bit mask for current node */ + IPNode_t * ipn; /* current internal node */ + IPNode_t * lastipn; /* last internal node seen in search */ + IPNode_t * mipn; /* ipn_masked subtree root pointer */ + + if (match) *match = 0; + + /* Handle null or empty IP filter */ + if ((ipf == 0) || (ipf->ipf_tree == 0)) goto fail; + + lastipn = NULL; + ipn = ipf->ipf_tree; + + /* + * The tree traversal first works down the tree, under the assumption + * that all of the bits in the given IP address may be significant. + * The internal nodes of the tree will cause particular bits of the + * IP address to be tested, and the ipn_clear or ipn_set link to + * a descendant followed accordingly. The internal nodes are arranged + * in such a way that high-order bits are tested before low-order bits. + * Usually some bits are skipped, as they are not needed to distinguish + * the entries in the tree. + * + * At the bottom of the tree, a leaf node may be found, or the last + * descendant link may be NULL. If a leaf node is found, it is + * tested for a match against the given IP address. If it doesn't + * match, or the link was NULL, backtracking begins, as described + * below. + * + * Backtracking follows the ipn_parent links back up the tree from + * the last internal node, looking for internal nodes with ipn_masked + * descendants. The subtrees attached to these links are traversed + * downward, as before, with the same processing at the bottom as + * the first downward traversal. Following the ipn_masked links is + * essentially examining the possibility that the IP address bit + * associated with the internal node may be masked out by the + * ipl_netmask in a leaf at the bottom of such a subtree. Since + * the ipn_masked links are examined from the bottom of the tree + * to the top, this looks at the low-order bits first. + */ + + while (ipn != NULL) { + + /* + * Work down the tree testing bits in the IP address indicated + * by the internal nodes. Exit the loop when there are no more + * internal nodes. + */ + while ((ipn != NULL) && (ipn->ipn_type == IPN_NODE)) { + + /* Save pointer to internal node */ + lastipn = ipn; + + /* Get a mask for the bit this node tests */ + bitmask = (IPAddr_t) 1<<ipn->ipn_bit; + + /* Select link to follow for this IP address */ + ipn = (bitmask & ipaddr) ? ipn->ipn_set : ipn->ipn_clear; + } + + /* Did we end up with a non-NULL node pointer? */ + if (ipn != NULL) { + + /* It must be a leaf node */ + assert(ipn->ipn_type == IPN_LEAF); + leaf = (IPLeaf_t *)ipn; + + /* Is it a matching leaf? */ + if (leaf->ipl_ipaddr == (ipaddr & leaf->ipl_netmask)) goto win; + } + + /* + * Backtrack, starting at lastipn. Search each subtree + * emanating from an ipn_masked link. Step up the tree + * until the ipn_masked link of the node referenced by + * "ipf->ipf_tree" has been considered. + */ + + for (ipn = lastipn; ipn != NULL; ipn = ipn->ipn_parent) { + + /* + * Look for a node with a non-NULL masked link, but don't + * go back to the node we just came from. + */ + + if ((ipn->ipn_masked != NULL) && (ipn->ipn_masked != lastipn)) { + + /* Get the root of this subtree */ + mipn = ipn->ipn_masked; + + /* If this is an internal node, start downward traversal */ + if (mipn->ipn_type == IPN_NODE) { + ipn = mipn; + break; + } + + /* Otherwise it's a leaf */ + assert(mipn->ipn_type == IPN_LEAF); + leaf = (IPLeaf_t *)mipn; + + /* Is it a matching leaf? */ + if (leaf->ipl_ipaddr == (ipaddr & leaf->ipl_netmask)) goto win; + } + + /* Don't consider nodes above the given root */ + if (ipn == ipf->ipf_tree) goto fail; + + lastipn = ipn; + } + } + + fail: + /* No matching entry found */ + return ACL_NOMATCH; + + win: + /* Found a match in leaf */ + if (match) *match = (void *)leaf; + + return ACL_IPMATCH; +} + +int aclUserLookup(UidUser_t * uup, UserObj_t * uoptr) +{ + int gl1cnt; /* elements left in uup->uu_group list */ + int gl2cnt; /* elements left in uoptr->uo_groups list */ + USI_t * gl1ptr; /* pointer to next group in uup->uu_group */ + USI_t * gl2ptr; /* pointer to next group in uoptr->uo_groups */ + + /* Try for a direct match on the user id */ + if (usiPresent(&uup->uu_user, uoptr->uo_uid)) { + return ACL_USMATCH; + } + + /* + * Now we want to see if there are any matches between the + * uup->uu_group group id list and the list of groups in the + * user object. + */ + + gl1cnt = UILCOUNT(&uup->uu_group); + gl1ptr = UILLIST(&uup->uu_group); + gl2cnt = UILCOUNT(&uoptr->uo_groups); + gl2ptr = UILLIST(&uoptr->uo_groups); + + while ((gl1cnt > 0) && (gl2cnt > 0)) { + + if (*gl1ptr == *gl2ptr) { + return ACL_GRMATCH; + } + + if (*gl1ptr < *gl2ptr) { + ++gl1ptr; + --gl1cnt; + } + else { + ++gl2ptr; + --gl2cnt; + } + } + + return ACL_NOMATCH; +} + +/* + * Description (aclEvaluate) + * + * This function evaluates a given ACL against specified client + * information and a particular access right that is needed to + * service the client. It can optionally return the ACL directive + * number which allows or denies the client's access. + * + * Arguments: + * + * acl - pointer to ACL to evaluate + * arid - desired access right id value + * clauth - pointer to client authentication information + * padn - pointer to returned ACL directive number + * (may be null) + * + * Returns: + * + * A return value of zero indicates that the given ACL does not + * control the desired access right, or contains no directives which + * match the specified client. A positive return value contains a + * value of ACD_ALLOW, ACD_DENY, or ACD_AUTH, and may also have the + * ACD_ALWAYS bit flag set. The value indicates whether the client + * should be allowed or denied access, or whether authentication is + * needed. The ACD_ALWAYS flag indicates if the action should occur + * immediately, terminating any further ACL evaluation. An error + * is indicated by a negative error code (ACLERRxxxx - see aclerror.h). + */ + +int aclEvaluate(ACL_t * acl, USI_t arid, ClAuth_t * clauth, int * padn) +{ + ACDirective_t * acd; /* current ACL directive pointer */ + RightSpec_t * rsp; /* pointer to rights controlled by ACL */ + ACClients_t * csp; /* pointer to clients specification */ + HostSpec_t * hsp; /* pointer to host specification */ + UserSpec_t * usp; /* pointer to user specification */ + Realm_t * rlm = 0; /* current authentication realm pointer */ + Realm_t * authrlm = 0; /* realm to be used for authentication */ + int ndir; /* ACL directive number */ + int rv; /* result value */ + int decision = 0; /* current access control decision */ + int result = 0; /* function return value */ + int mdn = 0; /* matching directive number */ + + if (padn) *padn = 0; + + /* Does this ACL control the desired access right? */ + + rsp = acl->acl_rights; + if ((rsp == 0) || !usiPresent(&rsp->rs_list, arid)) { + + /* No, nothing to do */ + return 0; + } + + ndir = 0; + + /* Loop on each ACL directive */ + for (acd = acl->acl_dirf; acd != 0; acd = acd->acd_next) { + + /* Bump directive number */ + ++ndir; + + /* Dispatch on directive action code */ + switch (acd->acd_action) { + + case ACD_ALLOW: + case ACD_DENY: + + /* Loop to process list of client specifications */ + for (csp = acd->acd_cl; csp != 0; csp = csp->cl_next) { + + /* Is there a host list? */ + hsp = csp->cl_host; + if (hsp != 0) { + + /* An empty host list will not match */ + rv = 0; + + /* Yes, is there an IP address filter? */ + if (hsp->hs_host.inh_ipf.ipf_tree != 0) { + + /* + * Yes, see if the the client's IP address + * matches anything in the IP filter. + */ + rv = aclIPLookup(&hsp->hs_host.inh_ipf, + clauth->cla_ipaddr, 0); + } + + /* If no IP match, is there a DNS filter? */ + if (!rv && (hsp->hs_host.inh_dnf.dnf_hash != 0)) { + + /* Yes, try for a DNS match */ + rv = aclDNSLookup(&hsp->hs_host.inh_dnf, + clauth->cla_dns, 1, 0); + } + + /* + * Does the client match the host list? If not, skip + * to the next client specification. + */ + if (!rv) continue; + } + + /* Is there a user list? */ + usp = csp->cl_user; + if (usp != 0) { + + /* Yes, has the client user been authenticated yet? */ + if ((clauth->cla_realm != 0) && (clauth->cla_uoptr != 0)) { + + /* + * Yes, has the client user been authenticated in the + * realm associated with this user list? + */ + if (RLMEQUIV(rlm, clauth->cla_realm)) { + + /* + * Yes, does the user spec allow all + * authenticated users? + */ + rv = (usp->us_flags & ACL_USALL) ? ACL_GRMATCH : 0; + if (!rv) { + + /* + * No, need to check client user against list. + */ + rv = aclUserLookup(&usp->us_user, + clauth->cla_uoptr); + } + + /* Got a match yet? */ + if (rv) { + + /* + * Yes, update the the access control decision, + * clearing any pending authentication request + * flag. + */ + authrlm = 0; + decision = acd->acd_action; + + /* Copy the "always" flag to the result */ + result = (acd->acd_flags & ACD_ALWAYS); + mdn = ndir; + } + } + else { + + /* + * The client has been authenticated already, + * but not in the realm used by this directive. + * Since directives in a given ACL are not + * independent policy statements, it seems that + * the proper thing to do here is to reject + * this ACL in its entirity. This case is not + * an authentication failure per se, but rather + * an inability to evaluate this particular + * ACL directive which requires authentication. + */ + return 0; + } + } + else { + + /* + * The client user has not been authenticated in this + * realm yet, but could potentially be one of the + * users on this user list. This directive is + * therefore "potentially matching". The question + * is: would it change the current decision to allow + * or deny the client if the client user actually did + * match the user list? + */ + if ((authrlm == 0) && (decision != acd->acd_action)) { + + /* + * Yes, set the "request authentication" flag, + * along with ACD_ALWAYS if it is set in the + * directive. + */ + authrlm = rlm; + decision = ACD_AUTH; + result = (acd->acd_flags & ACD_ALWAYS); + mdn = ndir; + } + } + } + else { + + /* + * There is no user list. Therefore any user, + * authenticated or not, is considered a match. + * Update the decision, and clear the + * "authentication requested" flag. + */ + authrlm = 0; + decision = acd->acd_action; + result = (acd->acd_flags & ACD_ALWAYS); + mdn = ndir; + } + + /* + * If we hit a client specification that requires + * immediate action, exit the loop. + */ + if (result & ACD_ALWAYS) break; + } + break; + + case ACD_AUTH: + + /* Got a pointer to a realm specification? */ + if (acd->acd_auth.au_realm != 0) { + + /* Yes, update the current realm pointer */ + rlm = &acd->acd_auth.au_realm->rs_realm; + + /* Has the client already successfully authenticated? */ + if ((clauth->cla_realm == 0) || (clauth->cla_uoptr == 0)) { + + /* + * No, if this is an "always" directive, override any + * previously selected realm and request authentication. + */ + if ((acd->acd_flags & ACD_ALWAYS) != 0) { + + /* Set decision to request authentication */ + authrlm = rlm; + decision = ACD_AUTH; + result = ACD_ALWAYS; + mdn = ndir; + } + } + } + break; + + case ACD_EXEC: + + /* Conditionally terminate ACL evaluation */ + switch (decision) { + case ACD_ALLOW: + if (acd->acd_flags & ACD_EXALLOW) { + result = (acd->acd_flags & ACD_ALWAYS); + goto out; + } + break; + case ACD_DENY: + if (acd->acd_flags & ACD_EXDENY) { + result = (acd->acd_flags & ACD_ALWAYS); + goto out; + } + break; + case ACD_AUTH: + if (acd->acd_flags & ACD_EXAUTH) { + result = (acd->acd_flags & ACD_ALWAYS); + goto out; + } + break; + default: + break; + } + break; + + default: + break; + } + + /* + * If we hit a directive that requires immediate action, exit + * the loop. + */ + if (result & ACD_ALWAYS) break; + } + + out: + /* If the decision is to request authentication, set the desired realm */ + if (decision == ACD_AUTH) { + clauth->cla_realm = authrlm; + } + + /* Combine decision with flags already in result */ + result |= decision; + + /* Return matching directive number if desired */ + if (padn) *padn = mdn; + + return result; +} diff --git a/lib/libaccess/aclflush.cpp b/lib/libaccess/aclflush.cpp new file mode 100644 index 00000000..dfee47d6 --- /dev/null +++ b/lib/libaccess/aclflush.cpp @@ -0,0 +1,178 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * Source file for the ACL_CacheFlush-related routines. + */ + +#include <base/nsassert.h> +#include <base/util.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/aclglobal.h> +#include <libaccess/las.h> +#include "aclcache.h" +#include <libaccess/dbtlibaccess.h> + +extern void ACL_DatabaseDestroy(void); + +PRIntn +deletelists(PRHashEntry *he, PRIntn i, void *arg) +{ + ACLListHandle_t *acllist=(ACLListHandle_t *)he->value; + NSErr_t *errp = 0; + + NS_ASSERT(he); + NS_ASSERT(he->value); + + if (acllist->ref_count) { + // If the list is in use, increment the counter. Then set the + // stale flag. The other user can't delete the list since we're + // counted as well. Finally, decrement the counter and whoever + // sets it to zero will delete the ACL List. + NS_ASSERT(ACL_CritHeld()); + acllist->ref_count++; + acllist->flags |= ACL_LIST_STALE; + if (--acllist->ref_count == 0) + ACL_ListDestroy(errp, acllist); + } else { + ACL_ListDestroy(errp, acllist); + } + + return 0; +} + +PRIntn +restartdeletelists(PRHashEntry *he, PRIntn i, void *arg) +{ + NSErr_t *errp = 0; + + // Cannot be anyone left using the lists, so just free them no matter + // what. + ACLListHandle_t *acllist=(ACLListHandle_t *)he->value; + + NS_ASSERT(he); + NS_ASSERT(he->value); + + ACL_ListDestroy(errp, acllist); + + return 0; +} + +static AclCacheFlushFunc_t AclCacheFlushRoutine = NULL; + +NSAPI_PUBLIC int +ACL_CacheFlushRegister(AclCacheFlushFunc_t flush_func) +{ + NS_ASSERT(flush_func); + AclCacheFlushRoutine = flush_func; + + return 0; +} + +NSAPI_PUBLIC int +ACL_CacheFlush(void) +{ + ACLGlobal_p newACLGlobal; + NSErr_t *errp = 0; + + NS_ASSERT(ACLGlobal); + NS_ASSERT(ACLGlobal->masterlist); + NS_ASSERT(ACLGlobal->listhash); + NS_ASSERT(ACLGlobal->urihash); + NS_ASSERT(ACLGlobal->urigethash); + NS_ASSERT(ACLGlobal->pool); + + ACL_CritEnter(); + + // Swap the pointers. Keep using the current database/method tables + // until the new ones are built. This is a kludge. An in-progress + // evaluation could conceivably get messed up, but the window seems + // small. + newACLGlobal = oldACLGlobal; + + oldACLGlobal = ACLGlobal; + ACLGlobal = newACLGlobal; + + // Prepare the new ACLGlobal structure + ACL_UriHashInit(); /* Also initializes ACLGlobal->pool */ + ACL_ListHashInit(); + ACLGlobal->evalhash = oldACLGlobal->evalhash; + ACLGlobal->flushhash = oldACLGlobal->flushhash; + ACLGlobal->methodhash = oldACLGlobal->methodhash; + ACLGlobal->dbtypehash = oldACLGlobal->dbtypehash; + ACLGlobal->dbnamehash = oldACLGlobal->dbnamehash; + ACLGlobal->attrgetterhash = oldACLGlobal->attrgetterhash; + ACLGlobal->databasepool = oldACLGlobal->databasepool; + ACLGlobal->methodpool = oldACLGlobal->methodpool; + + // Mark all existing ACL Lists as stale. Delete any unreferenced ones. + PR_HashTableEnumerateEntries(oldACLGlobal->listhash, deletelists, NULL); + + // Delete the old master list. + ACL_ListDestroy(errp, oldACLGlobal->masterlist); + oldACLGlobal->masterlist = NULL; + PR_HashTableDestroy(oldACLGlobal->listhash); + oldACLGlobal->listhash = NULL; + PR_HashTableDestroy(oldACLGlobal->urihash); + oldACLGlobal->urihash = NULL; + PR_HashTableDestroy(oldACLGlobal->urigethash); + oldACLGlobal->urigethash = NULL; + pool_destroy(oldACLGlobal->pool); + oldACLGlobal->pool = NULL; + memset(oldACLGlobal, 0, sizeof(ACLGlobal_s)); + + + // Read in the ACLs again in lib/frame + if (AclCacheFlushRoutine) { + (*AclCacheFlushRoutine)(); + } + + ACL_CritExit(); + + return 0; +} + + +NSAPI_PUBLIC void +ACL_Restart(void *clntData) +{ + NSErr_t *errp = 0; + + NS_ASSERT(ACLGlobal); + NS_ASSERT(ACLGlobal->masterlist); + NS_ASSERT(ACLGlobal->listhash); + NS_ASSERT(ACLGlobal->urihash); + NS_ASSERT(ACLGlobal->urigethash); + NS_ASSERT(ACLGlobal->pool); + + // Unlike ACL_CacheFlush, this routine can be much more cavalier about + // freeing up memory, since there's guaranteed to be no users about at + // this time. + + ACL_DatabaseDestroy(); + ACL_MethodSetDefault(errp, ACL_METHOD_INVALID); + + // Mark all existing ACL Lists as stale. Delete any unreferenced ones + // (i.e. all of them) + PR_HashTableEnumerateEntries(ACLGlobal->listhash, restartdeletelists, NULL); + + // Delete the master list. + ACL_ListDestroy(errp, ACLGlobal->masterlist); + + ACL_LasHashDestroy(); + PR_HashTableDestroy(ACLGlobal->listhash); + PR_HashTableDestroy(ACLGlobal->urihash); + PR_HashTableDestroy(ACLGlobal->urigethash); + pool_destroy(ACLGlobal->pool); + + PERM_FREE(ACLGlobal); + ACLGlobal = NULL; + PERM_FREE(oldACLGlobal); + oldACLGlobal = NULL; + + return; +} diff --git a/lib/libaccess/aclparse.cpp b/lib/libaccess/aclparse.cpp new file mode 100644 index 00000000..d8c429fe --- /dev/null +++ b/lib/libaccess/aclparse.cpp @@ -0,0 +1,2241 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * 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 <base/systems.h> +#include <base/file.h> +#include <base/util.h> +#include <netsite.h> +#include <libaccess/nsadb.h> +#include <libaccess/aclerror.h> +#include <libaccess/aclparse.h> +#include <libaccess/symbols.h> + +#ifdef XP_UNIX +#include <sys/types.h> +#include <netinet/in.h> /* ntohl */ +#include <arpa/inet.h> +#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; +} diff --git a/lib/libaccess/aclpriv.h b/lib/libaccess/aclpriv.h new file mode 100644 index 00000000..a93cc055 --- /dev/null +++ b/lib/libaccess/aclpriv.h @@ -0,0 +1,171 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * ACL private data structure definitions + */ + +#ifndef ACL_PARSER_HEADER +#define ACL_PARSER_HEADER + +#include <netsite.h> +#include <plhash.h> +#include <base/pool.h> +#include <base/plist.h> +#include <libaccess/las.h> + + +#define ACL_TERM_BSIZE 4 +#define ACL_FALSE_IDX -2 +#define ACL_TRUE_IDX -1 +#define ACL_MIN_IDX 0 +#define ACL_EXPR_STACK 1024 +#define ACL_TABLE_THRESHOLD 10 + +typedef enum { + ACL_EXPR_OP_AND, + ACL_EXPR_OP_OR, + ACL_EXPR_OP_NOT + } ACLExprOp_t; + + +typedef struct ACLExprEntry { + char *attr_name; /* LAS name input */ + CmpOp_t comparator; /* LAS comparator input */ + char *attr_pattern; /* LAS attribute input */ + int false_idx; /* index, -1 true, -2 false */ + int true_idx; /* index, -1 true, -2 false */ + int start_flag; /* marks start of an expr */ + void *las_cookie; /* private data store for LAS */ + LASEvalFunc_t las_eval_func; /* LAS function */ +} ACLExprEntry_t; + +typedef struct ACLExprRaw { + char *attr_name; /* expr lval */ + CmpOp_t comparator; /* comparator */ + char *attr_pattern; /* expr rval */ + ACLExprOp_t logical; /* logical operator */ +} ACLExprRaw_t; + +typedef struct ACLExprStack { + char *expr_text[ACL_EXPR_STACK]; + ACLExprRaw_t *expr[ACL_EXPR_STACK]; + int stack_index; + int found_subexpression; + int last_subexpression; +} ACLExprStack_t; + +typedef struct ACLExprHandle { + char *expr_tag; + char *acl_tag; + int expr_number; + ACLExprType_t expr_type; + int expr_flags; + int expr_argc; + char **expr_argv; + PList_t expr_auth; + ACLExprEntry_t *expr_arry; + int expr_arry_size; + int expr_term_index; + ACLExprRaw_t *expr_raw; + int expr_raw_index; + int expr_raw_size; + struct ACLExprHandle *expr_next; /* Null-terminated */ +} ACLExprHandle_t; + +typedef struct ACLHandle { + int ref_count; + char *tag; + PFlags_t flags; + char *las_name; + pblock *pb; + char **attr_name; + int expr_count; + ACLExprHandle_t *expr_list_head; /* Null-terminated */ + ACLExprHandle_t *expr_list_tail; +} ACLHandle_t; + + +typedef struct ACLWrapper { + ACLHandle_t *acl; + struct ACLWrapper *wrap_next; +} ACLWrapper_t; + +#define ACL_LIST_STALE 0x1 +#define ACL_LIST_IS_STALE(x) ((x)->flags & ACL_LIST_STALE) + +typedef struct ACLListHandle { + ACLWrapper_t *acl_list_head; /* Null-terminated */ + ACLWrapper_t *acl_list_tail; /* Null-terminated */ + int acl_count; + void *acl_sym_table; + void *cache; + uint32 flags; + int ref_count; +} ACLListHandle_t; + +typedef struct ACLAceNumEntry { + int acenum; + struct ACLAceNumEntry *next; + struct ACLAceNumEntry *chain; /* only used for freeing memory */ +} ACLAceNumEntry_t; + +typedef struct ACLAceEntry { + ACLExprHandle_t *acep; + /* Array of auth block ptrs for all the expr + clauses in this ACE */ + PList_t *autharray; + /* PList with auth blocks for ALL attributes */ + PList_t global_auth; + struct ACLAceEntry *next; /* Null-terminated list */ +} ACLAceEntry_t; + +typedef struct PropList PropList_t; + +typedef struct ACLEvalHandle { + pool_handle_t *pool; + ACLListHandle_t *acllist; + PList_t subject; + PList_t resource; + int default_result; +} ACLEvalHandle_t; + + +typedef struct ACLListCache { +/* Hash table for all access rights used in all acls in this list. Each + * hash entry has a list of ACE numbers that relate to this referenced + * access right. + */ + PLHashTable *Table; + char *deny_response; + char *deny_type; + ACLAceEntry_t *acelist; /* Evaluation order + * list of all ACEs + */ + ACLAceNumEntry_t *chain_head; /* Chain of all Ace num + * entries for this + * ACL list so we can free them + */ + ACLAceNumEntry_t *chain_tail; +} ACLListCache_t; + +/* this is to speed up acl_to_str_append */ +typedef struct acl_string_s { + char * str; + long str_size; + long str_len; +} acl_string_t; + + + +NSPR_BEGIN_EXTERN_C +extern int ACL_ExprDisplay( ACLExprHandle_t *acl_expr ); +extern int ACL_AssertAcl( ACLHandle_t *acl ); +extern int ACL_EvalDestroyContext ( ACLListCache_t *cache ); +extern time_t *acl_get_req_time(PList_t resource); +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/aclscan.h b/lib/libaccess/aclscan.h new file mode 100644 index 00000000..df54f374 --- /dev/null +++ b/lib/libaccess/aclscan.h @@ -0,0 +1,25 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Internal functions for scanner. + */ + + +#ifndef ACLSCAN_H +#define ACLSCAN_H + +NSPR_BEGIN_EXTERN_C + +extern int acl_InitScanner(NSErr_t *errp, char *filename, char *buffer); +extern int acl_EndScanner(void); +extern void aclerror(const char *s); +extern int acllex(void); + +NSPR_END_EXTERN_C + +#endif + diff --git a/lib/libaccess/aclscan.l b/lib/libaccess/aclscan.l new file mode 100644 index 00000000..009e02db --- /dev/null +++ b/lib/libaccess/aclscan.l @@ -0,0 +1,379 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Lexical analyzer for ACL + */ + +%{ +#include "acl.tab.h" /* token codes */ +#include <base/file.h> +#include <libaccess/acl.h> +#include <libaccess/nserror.h> +#include "aclpriv.h" +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <libaccess/aclerror.h> +#ifdef XP_WIN32 +#include <io.h> +#endif + +#include "parse.h" +#include "aclscan.h" + +static int acl_scanner_input(char *buffer, int max_size); + +#define YY_NEVER_INTERACTIVE 1 +#undef YY_INPUT +#define YY_INPUT(buf, result, max) (result = acl_scanner_input(buf, max)) + +static int acl_lineno; +static int acl_tokenpos; +static char acl_filename[500]; +static NSErr_t *acl_errp; +static int acl_use_buffer; +static char *acl_buffer; +static int acl_buffer_length; +static int acl_buffer_offset; +static char *last_string; +static SYS_FILE acl_prfd; + + +%} + +ws [ \t]+ +comment #.* +qstring \"[^\"\n]*[\"\n] +variable [\*a-zA-Z0-9\.\-\_][\*a-zA-Z0-9\.\-\_]* + +%% + +\n.* { + acl_lineno++; + acl_tokenpos = 0; + yyless(1); + } + +{ws} ; + +{comment} ; + +<<EOF>> { + yylval.string = NULL; + last_string = yylval.string; + return(0); + } + +{qstring} { + yylval.string = PERM_STRDUP( yytext+1 ); + last_string = yylval.string; + if ( yylval.string[yyleng-2] != '"' ) + fprintf(stderr, "Unterminated string\n") ; + else + yylval.string[yyleng-2] = '\0'; + acl_tokenpos += yyleng; + return ACL_QSTRING_TOK; + } + + +absolute { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_ABSOLUTE_TOK; + } + +acl { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_ACL_TOK; + } + +allow { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_ALLOW_TOK; + } + +always { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_ALWAYS_TOK; + } + +at { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_AT_TOK; + } + +authenticate { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_AUTHENTICATE_TOK; + } + +content { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_CONTENT_TOK; + } + +default { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_DEFAULT_TOK; + } + +deny { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_DENY_TOK; + } + +in { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_IN_TOK; + } + +inherit { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_INHERIT_TOK; + } + +terminal { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_TERMINAL_TOK; + } + +version { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_VERSION_TOK; + } + +with { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_WITH_TOK; + } + +not { + last_string = NULL; + acl_tokenpos += yyleng; + return ACL_NOT_TOK; + } + +and { + last_string = NULL; + yylval.ival = ACL_EXPR_OP_AND; + acl_tokenpos += yyleng; + return ACL_AND_TOK; + } + +or { + last_string = NULL; + yylval.ival = ACL_EXPR_OP_OR; + acl_tokenpos += yyleng; + return ACL_OR_TOK; + } + +"=" { + last_string = NULL; + yylval.ival = CMP_OP_EQ; + acl_tokenpos += yyleng; + return ACL_EQ_TOK; + } + +">=" { + last_string = NULL; + yylval.ival = CMP_OP_GE; + acl_tokenpos += yyleng; + return ACL_GE_TOK; + } + +">" { + last_string = NULL; + yylval.ival = CMP_OP_GT; + acl_tokenpos += yyleng; + return ACL_GT_TOK; + } + +"<" { + last_string = NULL; + yylval.ival = CMP_OP_LT; + acl_tokenpos += yyleng; + return ACL_LT_TOK; + } + +"<=" { + last_string = NULL; + yylval.ival = CMP_OP_LE; + acl_tokenpos += yyleng; + return ACL_LE_TOK; + } + +"!=" { + last_string = NULL; + yylval.ival = CMP_OP_NE; + acl_tokenpos += yyleng; + return ACL_NE_TOK; + } + +[(){},;] { + last_string = NULL; + acl_tokenpos += yyleng; + return yytext[0]; + } + +{variable} { + acl_tokenpos += yyleng; + yylval.string = PERM_STRDUP( yytext ); + last_string = yylval.string; + return ACL_VARIABLE_TOK; + } +%% + +void +acl_detab(char *t, char *s) +{ + int ii; + int pos; + int len; + + if (s == NULL || t == NULL) + return; + + len = strlen(s); + pos = 0; + for (ii = 0; ii < len; ii++) { + if (s[ii] == '\t') { + t[pos] = ' '; + } else { + t[pos] = s[ii]; + } + pos++; + } + t[pos] = '\0'; + return; +} + +void +yyerror(const char *s) +{ +char errorStr[256]; + +#if defined(UTEST) || defined(ACL_COMPILER) + printf("ACL file: %s\n", acl_filename); + printf("Syntax error at line: %d, token: %s\n", acl_lineno, yytext); + if ( last_string ) + free(last_string); +#else + sprintf(errorStr, "%d", acl_lineno); + if (yytext) { + nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program, + 3, acl_filename, errorStr, yytext); + } else { + nserrGenerate(acl_errp, ACLERRPARSE, ACLERR1780, ACL_Program, + 2, acl_filename, errorStr); + } + if ( last_string ) + free(last_string); +#endif + +} + +int +acl_InitScanner(NSErr_t *errp, char *filename, char *buffer) +{ + acl_errp = errp; + acl_lineno = 1; + acl_use_buffer = (filename == NULL) ? 1 : 0 ; + if ( filename != NULL ) { + strcpy(acl_filename, filename); +#ifdef UTEST + yyin = fopen(filename, "r"); + if ( yyin == NULL ) { + return(-1); + } +#else + acl_prfd = system_fopenRO(filename); + if ( acl_prfd == NULL ) { + return(-1); + } + yyin = (FILE *) acl_prfd; +#endif + yyrestart(yyin); + } else if ( buffer != NULL ) { + strcpy(acl_filename, "internal-buffer"); + acl_buffer_offset = 0; + acl_buffer_length = strlen(buffer); + acl_buffer = PERM_STRDUP(buffer); + if (acl_buffer == NULL) + return(-1); + yyrestart(NULL); + } else { + return(-1); + } + return(0); +} + +int +acl_EndScanner() +{ + acl_lineno = 0; + if ( acl_use_buffer) { + if ( acl_buffer ) + PERM_FREE(acl_buffer); + } else if ( yyin ) { +#ifdef UTEST + fclose(yyin); +#else + if ( acl_prfd ) { + system_fclose(acl_prfd); + acl_prfd = NULL; + } +#endif + yyin = NULL ; + } + return(0); +} + +int +yywrap() +{ + return(1); +} + +static int +acl_scanner_input(char *buffer, int max_size) +{ + int len = 0; + + if ( acl_use_buffer ) { + len = (acl_buffer_length > max_size) ? max_size : + acl_buffer_length; + memcpy(buffer, (const void *) &acl_buffer[acl_buffer_offset], + len); + acl_buffer_length -= len; + acl_buffer_offset += len; + } +#ifdef UTEST + else if ( ((len = fread( buffer, 1, max_size, aclin )) == 0) + && ferror( aclin ) ) { + yyerror( "scanner input failed" ); + } +#else + else if ( (len = system_fread( acl_prfd, buffer, max_size)) < 0 ) { + yyerror( "scanner input failed" ); + } +#endif + + return(len); +} diff --git a/lib/libaccess/aclspace.cpp b/lib/libaccess/aclspace.cpp new file mode 100644 index 00000000..fb211075 --- /dev/null +++ b/lib/libaccess/aclspace.cpp @@ -0,0 +1,37 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <string.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclglobal.h> + +/* Ordered list of generic rights */ +char *generic_rights[7] = { + "read", + "write", + "execute", + "delete", + "info", + "list", + NULL + } ; + +char *http_generic[7] = { + "http_get, http_head, http_trace, http_revlog, http_options, http_copy, http_getattribute, http_index, http_getproperties, http_getattributenames ", + "http_put, http_mkdir, http_startrev, http_stoprev, http_edit, http_post, http_save, http_setattribute, http_revadd, http_revlabel, http_lock, http_unlock, http_unedit, http_stoprev, http_startrev", + "http_post", + "http_delete, http_destroy, http_move", + "http_head, http_trace, http_options", + "http_index", + NULL + } ; + +/* Pointer to all global ACL data. This pointer is moved (atomically) + when a cache flush call is made. +*/ +ACLGlobal_p ACLGlobal; +ACLGlobal_p oldACLGlobal; diff --git a/lib/libaccess/acltext.y b/lib/libaccess/acltext.y new file mode 100644 index 00000000..01d6ae1f --- /dev/null +++ b/lib/libaccess/acltext.y @@ -0,0 +1,957 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * This grammar is intended to parse the version 3.0 + * and version 2.0 ACL text files and output an ACLListHandle_t + * structure. + */ + +%{ +#include <string.h> +#include <netsite.h> +#include <base/util.h> +#include <base/plist.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/nserror.h> +#include "parse.h" +#include "aclscan.h" + +#define MAX_LIST_SIZE 255 +static ACLListHandle_t *curr_acl_list; /* current acl list */ +static ACLHandle_t *curr_acl; /* current acl */ +static ACLExprHandle_t *curr_expr; /* current expression */ +static PFlags_t pflags; /* current authorization flags */ +static char *curr_args_list[MAX_LIST_SIZE]; /* current args */ +static char *curr_user_list[MAX_LIST_SIZE]; /* current users v2 */ +static char *curr_ip_dns_list[MAX_LIST_SIZE]; /* current ip/dns v2 */ +static PList_t curr_auth_info; /* current authorization method */ +static int use_generic_rights; /* use generic rights for conversion */ + +int acl_PushListHandle(ACLListHandle_t *handle) +{ + curr_acl_list = handle; + return(0); +} + +static void +acl_string_lower(char *s) +{ +int ii; +int len; + + len = strlen(s); + for (ii = 0; ii < len; ii++) + s[ii] = tolower(s[ii]); + + return; +} + +static void +acl_clear_args(char **args_list) +{ + args_list[0] = NULL; +} + +static void +acl_add_arg(char **args_list, char *arg) +{ + static int args_index; + + if ( args_list[0] == NULL ) { + args_index = 0; + } + args_list[args_index] = arg; + args_index++; + args_list[args_index] = NULL; +} + +static void +acl_free_args(char **args_list) +{ + int ii; + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( args_list[ii] ) + free(args_list[ii]); + else + break; + } +} + +static int +acl_set_args(ACLExprHandle_t *expr, char **args_list) +{ + int ii; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( args_list[ii] ) { + if ( ACL_ExprAddArg(NULL, expr, args_list[ii]) < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + } else + break; + } + return(0); +} + +static int +acl_set_users(ACLExprHandle_t *expr, char **user_list) +{ + int ii; + int jj; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( user_list[ii] ) { + if ( ACL_ExprTerm(NULL, expr, "user", CMP_OP_EQ, + user_list[ii]) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + } else + break; + } + + acl_free_args(user_list); + + for (jj = 0; jj < ii - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + yyerror("ACL_ExprOr() failed"); + return(-1); + } + } + return(0); +} + +static int +acl_set_users_or_groups(ACLExprHandle_t *expr, char **user_list) +{ + int ii; + int jj; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( user_list[ii] ) { + if ( ACL_ExprTerm(NULL, expr, "user", CMP_OP_EQ, + user_list[ii]) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + if ( ACL_ExprTerm(NULL, expr, "group", CMP_OP_EQ, + user_list[ii]) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + acl_free_args(user_list); + return(-1); + } + } else + break; + } + + acl_free_args(user_list); + + for (jj = 0; jj < (ii * 2) - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + yyerror("ACL_ExprOr() failed"); + return(-1); + } + } + return(0); +} + +static int +acl_set_ip_dns(ACLExprHandle_t *expr, char **ip_dns) +{ + int ii; + int jj; + int len; + char *attr; + char *val; + + if (expr == NULL) + return(-1); + + for (ii = 0; ii < MAX_LIST_SIZE; ii++) { + if ( ip_dns[ii] ) { + + attr = "ip"; + val = ip_dns[ii]; + len = strlen(val); + + for (jj = 0; jj < len; jj++) { + if ( strchr("0123456789.*", val[jj]) == 0 ) { + attr = "dns"; + break; + } + } + + if ( ACL_ExprTerm(NULL, expr, attr, CMP_OP_EQ, + val) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + acl_free_args(ip_dns); + return(-1); + } + + } else + break; + } + + acl_free_args(ip_dns); + + for (jj = 0; jj < ii - 1; jj++) { + if ( ACL_ExprOr(NULL, expr) < 0 ) { + yyerror("ACL_ExprOr() failed"); + return(-1); + } + } + + return(0); +} + + +%} + +%union { + char *string; + int ival; +} + +%token ACL_ABSOLUTE_TOK +%token ACL_ACL_TOK +%token ACL_ALLOW_TOK +%token ACL_ALWAYS_TOK +%token ACL_AND_TOK +%token ACL_AT_TOK +%token ACL_AUTHENTICATE_TOK +%token ACL_CONTENT_TOK +%token ACL_DEFAULT_TOK +%token ACL_DENY_TOK +%token ACL_GROUP_TOK +%token ACL_IN_TOK +%token ACL_INHERIT_TOK +%token ACL_NOT_TOK +%token ACL_NULL_TOK +%token ACL_OR_TOK +%token <string> ACL_QSTRING_TOK +%token ACL_READ_TOK +%token ACL_TERMINAL_TOK +%token <string> ACL_VARIABLE_TOK +%token ACL_VERSION_TOK +%token ACL_WRITE_TOK +%token ACL_WITH_TOK + +%token <ival> ACL_EQ_TOK +%token <ival> ACL_GE_TOK +%token <ival> ACL_GT_TOK +%token <ival> ACL_LE_TOK +%token <ival> ACL_LT_TOK +%token <ival> ACL_NE_TOK + +%% + +/* + * If no version is specified then we have a version 2.0 ACL. + */ +start: | start_acl_v2 + | ACL_VERSION_TOK ACL_VARIABLE_TOK + { + free($<string>2); + } + ';' start_acl_v3 + ; + +/* + ************************************************************ + * Parse version 2.0 ACL + ************************************************************ + */ +start_acl_v2: acl_list_v2 + ; + +acl_list_v2: acl_v2 + | acl_list_v2 acl_v2 + ; + +acl_v2: ACL_ACL_TOK acl_name_v2 + '(' arg_list_v2 ')' '{' directive_list_v2 '}' + { + acl_free_args(curr_args_list); + } + ; + +acl_name_v2: ACL_VARIABLE_TOK + { + curr_acl = ACL_AclNew(NULL, $<string>1); + free($<string>1); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + yyerror("Couldn't add ACL to list."); + return(-1); + } + acl_clear_args(curr_args_list); + use_generic_rights = 0; + if (strstr(curr_acl->tag, "READ")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("read")); + acl_add_arg(curr_args_list, PERM_STRDUP("execute")); + acl_add_arg(curr_args_list, PERM_STRDUP("list")); + acl_add_arg(curr_args_list, PERM_STRDUP("info")); + } if (strstr(curr_acl->tag, "WRITE")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("write")); + acl_add_arg(curr_args_list, PERM_STRDUP("delete")); + } + } + | ACL_QSTRING_TOK + { + curr_acl = ACL_AclNew(NULL, $<string>1); + free($<string>1); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + yyerror("Couldn't add ACL to list."); + return(-1); + } + acl_clear_args(curr_args_list); + use_generic_rights = 0; + if (strstr(curr_acl->tag, "READ")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("read")); + acl_add_arg(curr_args_list, PERM_STRDUP("execute")); + acl_add_arg(curr_args_list, PERM_STRDUP("list")); + acl_add_arg(curr_args_list, PERM_STRDUP("info")); + } if (strstr(curr_acl->tag, "WRITE")) { + use_generic_rights++; + acl_add_arg(curr_args_list, PERM_STRDUP("write")); + acl_add_arg(curr_args_list, PERM_STRDUP("delete")); + } + } + ; + +arg_list_v2: arg_v2 + | arg_v2 ',' arg_list_v2 + ; + +arg_v2: ACL_VARIABLE_TOK + { + char acl_tmp_arg[255]; + char *acl_new_arg; + + if (!use_generic_rights) { + acl_string_lower($<string>1); + strcpy(acl_tmp_arg, "http_"); + strcat(acl_tmp_arg, $<string>1); + PERM_FREE($<string>1); + acl_new_arg = PERM_STRDUP(acl_tmp_arg); + acl_add_arg(curr_args_list, acl_new_arg); + } else { + PERM_FREE($<string>1); + } + } + | ACL_QSTRING_TOK + { + if (!use_generic_rights) { + acl_add_arg(curr_args_list, $<string>1); + } else { + PERM_FREE($<string>1); + } + } + ; + +directive_list_v2: directive_v2 ';' + | directive_v2 ';' directive_list_v2 + ; + +directive_v2: auth_method_v2 + | auth_statement_v2 + ; + +auth_statement_v2: ACL_ALWAYS_TOK auth_type_v2 + { + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + yyerror("Could not set authorization processing flags"); + return(-1); + } + } + host_spec_list_action_v2 + | ACL_DEFAULT_TOK auth_type_v2 host_spec_list_action_v2 + ; + +auth_type_v2: ACL_ALLOW_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_ALLOW) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(allow) failed"); + return(-1); + } + acl_clear_args(curr_user_list); + acl_clear_args(curr_ip_dns_list); + } + | ACL_DENY_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_DENY) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(allow) failed"); + return(-1); + } + acl_clear_args(curr_user_list); + acl_clear_args(curr_ip_dns_list); + } + ; + +auth_method_v2: + ACL_ALWAYS_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(auth) failed"); + return(-1); + } + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + yyerror("Could not set authorization processing flags"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + yyerror("Could not set authorization info"); + return(-1); + } + } + realm_definition_v2 + | ACL_DEFAULT_TOK ACL_AUTHENTICATE_TOK ACL_IN_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(auth) failed"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + yyerror("Could not set authorization info"); + return(-1); + } + } + realm_definition_v2 + ; + +host_spec_list_action_v2: user_expr_v2 ACL_AT_TOK host_spec_list_v2 + { + if ( acl_set_users_or_groups(curr_expr, curr_user_list) < 0 ) { + yyerror("acl_set_users_or_groups() failed"); + return(-1); + } + + if ( acl_set_ip_dns(curr_expr, curr_ip_dns_list) < 0 ) { + yyerror("acl_set_ip_dns() failed"); + return(-1); + } + + if ( ACL_ExprAnd(NULL, curr_expr) < 0 ) { + yyerror("ACL_ExprAnd() failed"); + return(-1); + } + + if ( acl_set_args(curr_expr, curr_args_list) < 0 ) { + yyerror("acl_set_args() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + | user_expr_v2 + { + if ( acl_set_users_or_groups(curr_expr, curr_user_list) < 0 ) { + yyerror("acl_set_users_or_groups() failed"); + return(-1); + } + + if ( acl_set_args(curr_expr, curr_args_list) < 0 ) { + yyerror("acl_set_args() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + ; + +user_expr_v2: user_v2 + | '(' user_list_v2 ')' + ; + +user_list_v2: user_v2 + | user_v2 ',' user_list_v2 + ; + +user_v2: ACL_VARIABLE_TOK + { + acl_add_arg(curr_user_list, $<string>1); + } + | ACL_QSTRING_TOK + { + acl_add_arg(curr_user_list, $<string>1); + } + ; + + +host_spec_list_v2: dns_spec_v2 + | ip_spec_v2 + | '(' dns_ip_spec_list_v2 ')' + ; + +dns_spec_v2: ACL_VARIABLE_TOK + { + acl_add_arg(curr_ip_dns_list, $<string>1); + } + | ACL_QSTRING_TOK + { + acl_add_arg(curr_ip_dns_list, $<string>1); + } + ; + +ip_spec_v2: ACL_VARIABLE_TOK ACL_VARIABLE_TOK + { + char tmp_str[255]; + + util_sprintf(tmp_str, "%s+%s", $<string>1, $<string>2); + free($<string>1); + free($<string>2); + acl_add_arg(curr_ip_dns_list, PERM_STRDUP(tmp_str)); + } + ; + +dns_ip_spec_list_v2: dns_spec_v2 + | ip_spec_v2 + | dns_spec_v2 ',' dns_ip_spec_list_v2 + | ip_spec_v2 ',' dns_ip_spec_list_v2 + ; + +realm_definition_v2: '{' methods_list_v2 '}' + { + if ( ACL_ExprAddArg(NULL, curr_expr, "user") < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + + if ( ACL_ExprAddArg(NULL, curr_expr, "group") < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + ; + +method_v2: ACL_VARIABLE_TOK ACL_VARIABLE_TOK ';' + { + acl_string_lower($<string>1); + if (strcmp($<string>1, "database") == 0) { + free($<string>1); + free($<string>2); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($<string>1), $<string>1, $<string>2, NULL) < 0 ) { + } + free($<string>1); + } + } + | ACL_VARIABLE_TOK ACL_QSTRING_TOK ';' + { + acl_string_lower($<string>1); + if (strcmp($<string>1, "database") == 0) { + free($<string>1); + free($<string>2); + } else { + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($<string>1), $<string>1, $<string>2, NULL) < 0 ) { + } + free($<string>1); + } + } + ; + +methods_list_v2: method_v2 + | method_v2 methods_list_v2 + ; + +/* + ************************************************************ + * Parse version 3.0 ACL + ************************************************************ + */ + +start_acl_v3: acl_list + ; + +acl_list: acl + | acl_list acl + ; + +acl: named_acl ';' body_list + | named_acl ';' + ; + +named_acl: ACL_ACL_TOK ACL_VARIABLE_TOK + { + curr_acl = ACL_AclNew(NULL, $<string>2); + free($<string>2); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + yyerror("Couldn't add ACL to list."); + return(-1); + } + } + | ACL_ACL_TOK ACL_QSTRING_TOK + { + curr_acl = ACL_AclNew(NULL, $<string>2); + free($<string>2); + if ( ACL_ListAppend(NULL, curr_acl_list, curr_acl, 0) < 0 ) { + yyerror("Couldn't add ACL to list."); + return(-1); + } + } + ; + +body_list: body + | body body_list + ; + +body: authenticate_statement ';' + | authorization_statement ';' + | deny_statement ';' + ; + +deny_statement: + ACL_ABSOLUTE_TOK ACL_DENY_TOK ACL_WITH_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_RESPONSE) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(deny) failed"); + return(-1); + } + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + if ( ACL_ExprSetPFlags(NULL, curr_expr, + ACL_PFLAG_ABSOLUTE) < 0 ) { + yyerror("Could not set deny processing flags"); + return(-1); + } + } + deny_common + | ACL_DENY_TOK ACL_WITH_TOK + { + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_RESPONSE) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(deny) failed"); + return(-1); + } + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + deny_common + ; + +deny_common: ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprSetDenyWith(NULL, curr_expr, + $<string>1, $<string>3) < 0 ) { + yyerror("ACL_ExprSetDenyWith() failed"); + return(-1); + } + free($<string>1); + free($<string>3); + } + ; + +authenticate_statement: ACL_AUTHENTICATE_TOK + { + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_AUTH) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(allow) failed"); + return(-1); + } + curr_auth_info = PListCreate(NULL, ACL_ATTR_INDEX_MAX, NULL, NULL); + if ( ACL_ExprAddAuthInfo(curr_expr, curr_auth_info) < 0 ) { + yyerror("Could not set authorization info"); + return(-1); + } + } + '(' attribute_list ')' '{' parameter_list '}' + { + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + ; + +attribute_list: attribute + | attribute_list ',' attribute + +attribute: ACL_VARIABLE_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprAddArg(NULL, curr_expr, $<string>1) < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + free($<string>1); + } + ; + +parameter_list: parameter ';' + | parameter ';' parameter_list + ; + +parameter: ACL_VARIABLE_TOK ACL_EQ_TOK ACL_QSTRING_TOK + { + acl_string_lower($<string>1); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($<string>1), $<string>1, $<string>3, NULL) < 0 ) { + } + free($<string>1); + } + | ACL_VARIABLE_TOK ACL_EQ_TOK ACL_VARIABLE_TOK + { + acl_string_lower($<string>1); + if ( PListInitProp(curr_auth_info, + ACL_Attr2Index($<string>1), $<string>1, $<string>3, NULL) < 0 ) { + } + free($<string>1); + } + ; + +authorization_statement: ACL_ALLOW_TOK + { + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_ALLOW) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(allow) failed"); + return(-1); + } + } + auth_common_action + | ACL_DENY_TOK + { + pflags = 0; + curr_expr = ACL_ExprNew(ACL_EXPR_TYPE_DENY) ; + if ( curr_expr == NULL ) { + yyerror("ACL_ExprNew(deny) failed"); + return(-1); + } + } + auth_common_action + ; + +auth_common_action: + { + if ( ACL_ExprAppend(NULL, curr_acl, curr_expr) < 0 ) { + yyerror("Could not add authorization"); + return(-1); + } + } + auth_common + { + if ( ACL_ExprSetPFlags (NULL, curr_expr, pflags) < 0 ) { + yyerror("Could not set authorization processing flags"); + return(-1); + } +#ifdef DEBUG + if ( ACL_ExprDisplay(curr_expr) < 0 ) { + yyerror("ACL_ExprDisplay() failed"); + return(-1); + } + printf("Parsed authorization.\n"); +#endif + } + ; + +auth_common: flag_list '(' args_list ')' expression + ; + +flag_list: + | ACL_ABSOLUTE_TOK + { + pflags = ACL_PFLAG_ABSOLUTE; + } + | ACL_ABSOLUTE_TOK content_static + { + pflags = ACL_PFLAG_ABSOLUTE; + } + | ACL_CONTENT_TOK + { + pflags = ACL_PFLAG_CONTENT; + } + | ACL_CONTENT_TOK absolute_static + { + pflags = ACL_PFLAG_CONTENT; + } + | ACL_TERMINAL_TOK + { + pflags = ACL_PFLAG_TERMINAL; + } + | ACL_TERMINAL_TOK content_absolute + { + pflags = ACL_PFLAG_TERMINAL; + } + ; + +content_absolute: ACL_CONTENT_TOK + { + pflags |= ACL_PFLAG_CONTENT; + } + | ACL_ABSOLUTE_TOK + { + pflags |= ACL_PFLAG_ABSOLUTE; + } + | ACL_CONTENT_TOK ACL_ABSOLUTE_TOK + { + pflags |= ACL_PFLAG_ABSOLUTE | ACL_PFLAG_CONTENT; + } + | ACL_ABSOLUTE_TOK ACL_CONTENT_TOK + { + pflags |= ACL_PFLAG_ABSOLUTE | ACL_PFLAG_CONTENT; + } + ; + +content_static: ACL_CONTENT_TOK + { + pflags |= ACL_PFLAG_CONTENT; + } + | ACL_TERMINAL_TOK + { + pflags |= ACL_PFLAG_TERMINAL; + } + | ACL_CONTENT_TOK ACL_TERMINAL_TOK + { + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_CONTENT; + } + | ACL_TERMINAL_TOK ACL_CONTENT_TOK + { + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_CONTENT; + } + ; + +absolute_static: ACL_ABSOLUTE_TOK + { + pflags |= ACL_PFLAG_ABSOLUTE; + } + | ACL_TERMINAL_TOK + { + pflags |= ACL_PFLAG_TERMINAL; + } + | ACL_ABSOLUTE_TOK ACL_TERMINAL_TOK + { + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_ABSOLUTE; + } + | ACL_TERMINAL_TOK ACL_ABSOLUTE_TOK + { + pflags |= ACL_PFLAG_TERMINAL | ACL_PFLAG_ABSOLUTE; + } + ; + +args_list: arg + | args_list ',' arg + ; + +arg: ACL_VARIABLE_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprAddArg(NULL, curr_expr, $<string>1) < 0 ) { + yyerror("ACL_ExprAddArg() failed"); + return(-1); + } + free( $<string>1 ); + } + ; + +expression: factor + | factor ACL_AND_TOK expression + { + if ( ACL_ExprAnd(NULL, curr_expr) < 0 ) { + yyerror("ACL_ExprAnd() failed"); + return(-1); + } + } + | factor ACL_OR_TOK expression + { + if ( ACL_ExprOr(NULL, curr_expr) < 0 ) { + yyerror("ACL_ExprOr() failed"); + return(-1); + } + } + ; + +factor: base_expr + | '(' expression ')' + | ACL_NOT_TOK factor + { + if ( ACL_ExprNot(NULL, curr_expr) < 0 ) { + yyerror("ACL_ExprNot() failed"); + return(-1); + } + } + ; + +base_expr: ACL_VARIABLE_TOK relop ACL_QSTRING_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprTerm(NULL, curr_expr, + $<string>1, (CmpOp_t) $<ival>2, $<string>3) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + free($<string>1); + free($<string>3); + return(-1); + } + free($<string>1); + free($<string>3); + } + | ACL_VARIABLE_TOK relop ACL_VARIABLE_TOK + { + acl_string_lower($<string>1); + if ( ACL_ExprTerm(NULL, curr_expr, + $<string>1, (CmpOp_t) $<ival>2, $<string>3) < 0 ) { + yyerror("ACL_ExprTerm() failed"); + free($<string>1); + free($<string>3); + return(-1); + } + free($<string>1); + free($<string>3); + } + ; + +relop: ACL_EQ_TOK + | ACL_GE_TOK + | ACL_GT_TOK + | ACL_LT_TOK + | ACL_LE_TOK + | ACL_NE_TOK + ; +%% diff --git a/lib/libaccess/acltools.cpp b/lib/libaccess/acltools.cpp new file mode 100644 index 00000000..1283147e --- /dev/null +++ b/lib/libaccess/acltools.cpp @@ -0,0 +1,3457 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * Tools to build and maintain access control lists. + */ + +#include <stdio.h> +#include <string.h> + +#define ALLOCATE_ATTR_TABLE 1 /* Include the table of PList names */ + +#include <netsite.h> +#include <base/plist.h> +#include <base/util.h> +#include <base/crit.h> +#include <base/file.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/aclerror.h> +#include <libaccess/symbols.h> +#include <libaccess/aclstruct.h> +#include <libaccess/las.h> + +#include "aclscan.h" +#include "parse.h" +#include "oneeval.h" + +#include <libaccess/authdb.h> + +static CRITICAL acl_parse_crit = NULL; + +/* + * Allocate a new ACL handle + * + * This function creates a new ACL structure that will be used for + * access control information. + * + * Input: + * tag Specifies an identifier name for the new ACL, or + * it may be NULL when no name is required. + * Returns: + * A new ACL structure. + */ + +NSAPI_PUBLIC ACLHandle_t * +ACL_AclNew(NSErr_t *errp, char *tag ) +{ +ACLHandle_t *handle; + + handle = ( ACLHandle_t * ) PERM_CALLOC ( 1 * sizeof (ACLHandle_t) ); + if ( handle && tag ) { + handle->tag = PERM_STRDUP( tag ); + if ( handle->tag == NULL ) { + PERM_FREE(handle); + return(NULL); + } + } + return(handle); +} + +/* + * Appends to a specified ACL + * + * This function appends a specified ACL to the end of a given ACL list. + * + * Input: + * errp The error stack + * flags should always be zero now + * acl_list target ACL list + * acl new acl + * Returns: + * < 0 failure + * > 0 The number of acl's in the current list + */ + +NSAPI_PUBLIC int +ACL_ExprAppend( NSErr_t *errp, ACLHandle_t *acl, + ACLExprHandle_t *expr ) +{ + + if ( acl == NULL || expr == NULL ) + return(ACLERRUNDEF); + + expr->acl_tag = acl->tag; + + if ( expr->expr_type == ACL_EXPR_TYPE_AUTH || + expr->expr_type == ACL_EXPR_TYPE_RESPONSE ) { + expr->expr_number = -1; // expr number isn't valid + } else { + acl->expr_count++; + expr->expr_number = acl->expr_count; + } + + if ( acl->expr_list_head == NULL ) { + acl->expr_list_head = expr; + acl->expr_list_tail = expr; + } else { + acl->expr_list_tail->expr_next = expr; + acl->expr_list_tail = expr; + } + + return(acl->expr_count); +} + +/* + * Add authentication information to an ACL + * + * This function adds authentication data to an expr, based on + * the information provided by the parameters. + * + * Input: + * expr an authenticate expression to add database + * and method information to. ie, auth_info + * auth_info authentication information, eg database, + * method, etc. + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ExprAddAuthInfo( ACLExprHandle_t *expr, PList_t auth_info ) +{ + if ( expr == NULL || auth_info == NULL ) + return(ACLERRUNDEF); + + expr->expr_auth = auth_info; + + return(0); +} + +/* + * Add authorization information to an ACL + * + * This function adds an authorization to a given ACL, based on the information + * provided by the parameters. + * + * Input: + * errp The error stack + * access_rights strings which identify the access rights to be + * controlled by the generated expr. + * flags processing flags + * allow non-zero to allow the indicated rights, or zero to + * deny them. + * attr_expr handle for an attribute expression, which may be + * obtained by calling ACL_ExprNew() + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_AddPermInfo( NSErr_t *errp, ACLHandle_t *acl, + char **access_rights, + PFlags_t flags, + int allow, + ACLExprHandle_t *expr, + char *tag ) +{ + if ( acl == NULL || expr == NULL ) + return(ACLERRUNDEF); + + expr->expr_flags = flags; + expr->expr_argv = (char **) access_rights; + expr->expr_tag = PERM_STRDUP( tag ); + if ( expr->expr_tag == NULL ) + return(ACLERRNOMEM); + return(ACL_ExprAppend( errp, acl, expr )); +} + +/* + * Add rights information to an expression + * + * This function adds a right to an authorization, based on the information + * provided by the parameters. + * + * Input: + * errp The error stack + * access_right strings which identify the access rights to be + * controlled by the generated expr. + * expr handle for an attribute expression, which may be + * obtained by calling ACL_ExprNew() + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ExprAddArg( NSErr_t *errp, + ACLExprHandle_t *expr, + char *arg ) +{ + + if ( expr == NULL ) + return(ACLERRUNDEF); + + if (expr->expr_argv == NULL) + expr->expr_argv = (char **) PERM_MALLOC( 2 * sizeof(char *) ); + else + expr->expr_argv = (char **) PERM_REALLOC( expr->expr_argv, + (expr->expr_argc+2) + * sizeof(char *) ); + + if (expr->expr_argv == NULL) + return(ACLERRNOMEM); + + expr->expr_argv[expr->expr_argc] = PERM_STRDUP( arg ); + if (expr->expr_argv[expr->expr_argc] == NULL) + return(ACLERRNOMEM); + expr->expr_argc++; + expr->expr_argv[expr->expr_argc] = NULL; + + return(0); + +} + + +NSAPI_PUBLIC int +ACL_ExprSetDenyWith( NSErr_t *errp, ACLExprHandle_t *expr, char *deny_type, char *deny_response) +{ +int rv; + + if ( expr->expr_argc == 0 ) { + if ( (rv = ACL_ExprAddArg(errp, expr, deny_type)) < 0 ) + return(rv); + if ( (rv = ACL_ExprAddArg(errp, expr, deny_response)) < 0 ) + return(rv); + } else if ( expr->expr_argc == 2 ) { + if ( deny_type ) { + if ( expr->expr_argv[0] ) + PERM_FREE(expr->expr_argv[0]); + expr->expr_argv[0] = PERM_STRDUP(deny_type); + if ( expr->expr_argv[0] == NULL ) + return(ACLERRNOMEM); + } + if ( deny_response ) { + if ( expr->expr_argv[1] ) + PERM_FREE(expr->expr_argv[1]); + expr->expr_argv[1] = PERM_STRDUP(deny_response); + if ( expr->expr_argv[0] == NULL ) + return(ACLERRNOMEM); + } + } else { + return(ACLERRINTERNAL); + } + return(0); +} + +NSAPI_PUBLIC int +ACL_ExprGetDenyWith( NSErr_t *errp, ACLExprHandle_t *expr, char **deny_type, +char **deny_response) +{ + if ( expr->expr_argc == 2 ) { + *deny_type = expr->expr_argv[0]; + *deny_response = expr->expr_argv[1]; + return(0); + } else { + return(ACLERRUNDEF); + } +} + +/* + * Function to set the authorization statement processing flags. + * + * Input: + * errp The error reporting stack + * expr The authoization statement + * flags The flags to set + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ExprSetPFlags( NSErr_t *errp, + ACLExprHandle_t *expr, + PFlags_t flags ) +{ + if ( expr == NULL ) + return(ACLERRUNDEF); + + expr->expr_flags |= flags; + return(0); +} + +/* + * Function to clear the authorization statement processing flags. + * + * Input: + * errp The error reporting stack + * expr The authoization statement + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ExprClearPFlags( NSErr_t *errp, + ACLExprHandle_t *expr ) +{ + if ( expr == NULL ) + return(ACLERRUNDEF); + + expr->expr_flags = 0; + return(0); +} + +/* + * Allocate a new expression handle. + * + * Returns: + * NULL If handle could not be allocated. + * pointer New handle. + */ + +NSAPI_PUBLIC ACLExprHandle_t * +ACL_ExprNew( const ACLExprType_t expr_type ) +{ +ACLExprHandle_t *expr_handle; + + expr_handle = ( ACLExprHandle_t * ) PERM_CALLOC ( sizeof(ACLExprHandle_t) ); + if ( expr_handle ) { + expr_handle->expr_arry = ( ACLExprEntry_t * ) + PERM_CALLOC( ACL_TERM_BSIZE * sizeof(ACLExprEntry_t) ) ; + expr_handle->expr_arry_size = ACL_TERM_BSIZE; + expr_handle->expr_type = expr_type; + + expr_handle->expr_raw = ( ACLExprRaw_t * ) + PERM_CALLOC( ACL_TERM_BSIZE * sizeof(ACLExprRaw_t) ) ; + expr_handle->expr_raw_size = ACL_TERM_BSIZE; + + } + return(expr_handle); +} + + +/* + * LOCAL FUNCTION + * + * displays the ASCII equivalent index value. + */ + +static char * +acl_index_string ( int value, char *buffer ) +{ + + if ( value == ACL_TRUE_IDX ) { + strcpy( buffer, "TRUE" ); + return( buffer ); + } + + if ( value == ACL_FALSE_IDX ) { + strcpy( buffer, "FALSE" ); + return( buffer ); + } + + sprintf( buffer, "goto %d", value ); + return( buffer ); +} + + +/* + * LOCAL FUNCTION + * + * displays ASCII equivalent of CmpOp_t + */ + +static char * +acl_comp_string( CmpOp_t cmp ) +{ + switch (cmp) { + case CMP_OP_EQ: + return("="); + case CMP_OP_NE: + return("!="); + case CMP_OP_GT: + return(">"); + case CMP_OP_LT: + return("<"); + case CMP_OP_GE: + return(">="); + case CMP_OP_LE: + return("<="); + default: + return("unknown op"); + } +} + +/* + * Add a term to the specified attribute expression. + * + * Input: + * errp Error stack + * acl_expr Target expression handle + * attr_name Term Attribute name + * cmp Comparison operator + * attr_pattern Pattern for comparison + * Ouput: + * acl_expr New term added + * Returns: + * 0 Success + * < 0 Error + */ + +NSAPI_PUBLIC int +ACL_ExprTerm( NSErr_t *errp, ACLExprHandle_t *acl_expr, + char *attr_name, + CmpOp_t cmp, + char *attr_pattern ) +{ +ACLExprEntry_t *expr; +ACLExprRaw_t *raw_expr; + + if ( acl_expr == NULL || acl_expr->expr_arry == NULL ) + return(ACLERRUNDEF); + + if ( acl_expr->expr_term_index >= acl_expr->expr_arry_size ) { + acl_expr->expr_arry = ( ACLExprEntry_t *) + PERM_REALLOC ( acl_expr->expr_arry, + (acl_expr->expr_arry_size + ACL_TERM_BSIZE) + * sizeof(ACLExprEntry_t)); + if ( acl_expr->expr_arry == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_arry_size += ACL_TERM_BSIZE; + } + + expr = &acl_expr->expr_arry[acl_expr->expr_term_index]; + acl_expr->expr_term_index++; + + expr->attr_name = PERM_STRDUP(attr_name); + if ( expr->attr_name == NULL ) + return(ACLERRNOMEM); + expr->comparator = cmp; + expr->attr_pattern = PERM_STRDUP(attr_pattern); + if ( expr->attr_pattern == NULL ) + return(ACLERRNOMEM); + expr->true_idx = ACL_TRUE_IDX; + expr->false_idx = ACL_FALSE_IDX; + expr->start_flag = 1; + expr->las_cookie = 0; + expr->las_eval_func = 0; + + if ( acl_expr->expr_raw_index >= acl_expr->expr_raw_size ) { + acl_expr->expr_raw = ( ACLExprRaw_t *) + PERM_REALLOC ( acl_expr->expr_raw, + (acl_expr->expr_raw_size + ACL_TERM_BSIZE) + * sizeof(ACLExprRaw_t)); + if ( acl_expr->expr_raw == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_raw_size += ACL_TERM_BSIZE; + } + + raw_expr = &acl_expr->expr_raw[acl_expr->expr_raw_index]; + acl_expr->expr_raw_index++; + + raw_expr->attr_name = expr->attr_name; + raw_expr->comparator = cmp; + raw_expr->attr_pattern = expr->attr_pattern; + raw_expr->logical = (ACLExprOp_t)0; + +#ifdef DEBUG_LEVEL_2 + printf ( "%d: %s %s %s, t=%d, f=%d\n", + acl_expr->expr_term_index - 1, + expr->attr_name, + acl_comp_string( expr->comparator ), + expr->attr_pattern, + expr->true_idx, + expr->false_idx ); +#endif + + return(0); +} + +/* + * Negate the previous term or subexpression. + * + * Input: + * errp The error stack + * acl_expr The expression to negate + * Ouput + * acl_expr The negated expression + * Returns: + * 0 Success + * < 0 Failure + */ + +NSAPI_PUBLIC int +ACL_ExprNot( NSErr_t *errp, ACLExprHandle_t *acl_expr ) +{ +int idx; +int ii; +int expr_one = 0; +ACLExprRaw_t *raw_expr; + + if ( acl_expr == NULL ) + return(ACLERRUNDEF); + + + if ( acl_expr->expr_raw_index >= acl_expr->expr_raw_size ) { + acl_expr->expr_raw = ( ACLExprRaw_t *) + PERM_REALLOC ( acl_expr->expr_raw, + (acl_expr->expr_raw_size + ACL_TERM_BSIZE) + * sizeof(ACLExprRaw_t)); + if ( acl_expr->expr_raw == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_raw_size += ACL_TERM_BSIZE; + } + + raw_expr = &acl_expr->expr_raw[acl_expr->expr_raw_index]; + acl_expr->expr_raw_index++; + + raw_expr->logical = ACL_EXPR_OP_NOT; + raw_expr->attr_name = NULL; + + /* Find the last expression */ + idx = acl_expr->expr_term_index - 1; + for ( ii = idx; ii >= 0; ii-- ) { + if ( acl_expr->expr_arry[ii].start_flag ) { + expr_one = ii; + break; + } + } + +#ifdef DEBUG_LEVEL_2 + printf("not, start index=%d\n", expr_one); +#endif + + + /* + * The intent here is negate the last expression by + * modifying the true and false links. + */ + + for ( ii = expr_one; ii < acl_expr->expr_term_index; ii++ ) { + if ( acl_expr->expr_arry[ii].true_idx == ACL_TRUE_IDX ) + acl_expr->expr_arry[ii].true_idx = ACL_FALSE_IDX; + else if ( acl_expr->expr_arry[ii].true_idx == ACL_FALSE_IDX ) + acl_expr->expr_arry[ii].true_idx = ACL_TRUE_IDX; + + if ( acl_expr->expr_arry[ii].false_idx == ACL_TRUE_IDX ) + acl_expr->expr_arry[ii].false_idx = ACL_FALSE_IDX; + else if ( acl_expr->expr_arry[ii].false_idx == ACL_FALSE_IDX ) + acl_expr->expr_arry[ii].false_idx = ACL_TRUE_IDX; + + } + + return(0) ; +} + +/* + * Logical 'and' the previous two terms or subexpressions. + * + * Input: + * errp The error stack + * acl_expr The terms or subexpressions + * Output: + * acl_expr The expression after logical 'and' + */ + +NSAPI_PUBLIC int +ACL_ExprAnd( NSErr_t *errp, ACLExprHandle_t *acl_expr ) +{ +int idx; +int ii; +int expr_one = ACL_FALSE_IDX; +int expr_two = ACL_FALSE_IDX; +ACLExprRaw_t *raw_expr; + + if ( acl_expr == NULL ) + return(ACLERRUNDEF); + + if ( acl_expr->expr_raw_index >= acl_expr->expr_raw_size ) { + acl_expr->expr_raw = ( ACLExprRaw_t *) + PERM_REALLOC ( acl_expr->expr_raw, + (acl_expr->expr_raw_size + ACL_TERM_BSIZE) + * sizeof(ACLExprRaw_t) ); + if ( acl_expr->expr_raw == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_raw_size += ACL_TERM_BSIZE; + } + + raw_expr = &acl_expr->expr_raw[acl_expr->expr_raw_index]; + acl_expr->expr_raw_index++; + + raw_expr->logical = ACL_EXPR_OP_AND; + raw_expr->attr_name = NULL; + + /* Find the last two expressions */ + idx = acl_expr->expr_term_index - 1; + for ( ii = idx; ii >= 0; ii-- ) { + if ( acl_expr->expr_arry[ii].start_flag ) { + if ( expr_two == ACL_FALSE_IDX ) + expr_two = ii; + else if ( expr_one == ACL_FALSE_IDX ) { + expr_one = ii; + break; + } + } + } + +#ifdef DEBUG_LEVEL_2 + printf("and, index=%d, first expr=%d, second expr=%d\n", idx, expr_one, expr_two); +#endif + + for ( ii = expr_one; ii < expr_two; ii++) { + if ( acl_expr->expr_arry[ii].true_idx == ACL_TRUE_IDX ) + acl_expr->expr_arry[ii].true_idx = expr_two; + if ( acl_expr->expr_arry[ii].false_idx == ACL_TRUE_IDX ) + acl_expr->expr_arry[ii].false_idx = expr_two; + } + + acl_expr->expr_arry[expr_two].start_flag = 0; + return(0); +} + +/* + * Logical 'or' the previous two terms or subexpressions. + * + * Input: + * errp The error stack + * acl_expr The terms or subexpressions + * Output: + * acl_expr The expression after logical 'or' + */ + +NSAPI_PUBLIC int +ACL_ExprOr( NSErr_t *errp, ACLExprHandle_t *acl_expr ) +{ +int idx; +int ii; +int expr_one = ACL_FALSE_IDX; +int expr_two = ACL_FALSE_IDX; +ACLExprRaw_t *raw_expr; + + if ( acl_expr == NULL ) + return(ACLERRUNDEF); + + if ( acl_expr->expr_raw_index >= acl_expr->expr_raw_size ) { + acl_expr->expr_raw = ( ACLExprRaw_t *) + PERM_REALLOC ( acl_expr->expr_raw, + (acl_expr->expr_raw_size + ACL_TERM_BSIZE) + * sizeof(ACLExprRaw_t) ); + if ( acl_expr->expr_raw == NULL ) + return(ACLERRNOMEM); + acl_expr->expr_raw_size += ACL_TERM_BSIZE; + } + + raw_expr = &acl_expr->expr_raw[acl_expr->expr_raw_index]; + acl_expr->expr_raw_index++; + + raw_expr->logical = ACL_EXPR_OP_OR; + raw_expr->attr_name = NULL; + + /* Find the last two expressions */ + idx = acl_expr->expr_term_index - 1; + for ( ii = idx; ii >= 0; ii-- ) { + if ( acl_expr->expr_arry[ii].start_flag ) { + if ( expr_two == ACL_FALSE_IDX ) + expr_two = ii; + else if ( expr_one == ACL_FALSE_IDX ) { + expr_one = ii; + break; + } + } + } + +#ifdef DEBUG_LEVEL_2 + printf("or, index=%d, first expr=%d, second expr=%d\n", idx, expr_one, expr_two); +#endif + + for ( ii = expr_one; ii < expr_two; ii++) { + if ( acl_expr->expr_arry[ii].true_idx == ACL_FALSE_IDX ) + acl_expr->expr_arry[ii].true_idx = expr_two; + if ( acl_expr->expr_arry[ii].false_idx == ACL_FALSE_IDX ) + acl_expr->expr_arry[ii].false_idx = expr_two; + } + acl_expr->expr_arry[expr_two].start_flag = 0; + + return(0); +} + +/* + * INTERNAL FUNCTION (GLOBAL) + * + * Write an expression array to standard output. This + * is only useful debugging. + */ + +int +ACL_ExprDisplay( ACLExprHandle_t *acl_expr ) +{ +int ii; +char buffer[256]; + + if ( acl_expr == NULL ) + return(0); + + for ( ii = 0; ii < acl_expr->expr_term_index; ii++ ) { + printf ("%d: if ( %s %s %s ) ", + ii, + acl_expr->expr_arry[ii].attr_name, + acl_comp_string( acl_expr->expr_arry[ii].comparator ), + acl_expr->expr_arry[ii].attr_pattern ); + + printf("%s ", acl_index_string(acl_expr->expr_arry[ii].true_idx, buffer)); + printf("else %s\n", + acl_index_string(acl_expr->expr_arry[ii].false_idx, buffer) ); + } + + return(0); +} + +/* + * Creates a handle for a new list of ACLs + * + * This function creates a new list of ACLs. The list is initially empty + * and can be added to by ACL_ListAppend(). A resource manager would use + * these functions to build up a list of all the ACLs applicable to a + * particular resource access. + * + * Input: + * Returns: + * NULL failure, otherwise returns a new + * ACLListHandle + */ + +NSAPI_PUBLIC ACLListHandle_t * +ACL_ListNew(NSErr_t *errp) +{ +ACLListHandle_t *handle; + + handle = ( ACLListHandle_t * ) PERM_CALLOC ( sizeof(ACLListHandle_t) ); + handle->ref_count = 1; + return(handle); +} + +/* + * Allocates a handle for an ACL wrapper + * + * This wrapper is just used for ACL list creation. It's a way of + * linking ACLs into a list. This is an internal function. + */ + +static ACLWrapper_t * +acl_wrapper_new(void) +{ +ACLWrapper_t *handle; + + handle = ( ACLWrapper_t * ) PERM_CALLOC ( sizeof(ACLWrapper_t) ); + return(handle); +} + +/* + * Description + * + * This function destroys an entry a symbol table entry for an + * ACL. + * + * Arguments: + * + * sym - pointer to Symbol_t for an ACL entry + * argp - unused (must be zero) + * + * Returns: + * + * The return value is SYMENUMREMOVE. + */ + +static +int acl_hash_entry_destroy(Symbol_t * sym, void * argp) +{ + if (sym != 0) { + + /* Free the acl name string if any */ + if (sym->sym_name != 0) { + PERM_FREE(sym->sym_name); + } + + /* Free the Symbol_t structure */ + PERM_FREE(sym); + } + + /* Indicate that the symbol table entry should be removed */ + return SYMENUMREMOVE; +} + + +/* + * LOCAL FUNCTION + * + * Create a new symbol with the sym_name equal to the + * acl->tag value. Attaches the acl to the sym_data + * pointer. + */ + +static Symbol_t * +acl_sym_new(ACLHandle_t *acl) +{ + Symbol_t *sym; + /* It's not there, so add it */ + sym = (Symbol_t *) PERM_MALLOC(sizeof(Symbol_t)); + if ( sym == NULL ) + return(NULL); + + sym->sym_name = PERM_STRDUP(acl->tag); + if ( sym->sym_name == NULL ) { + PERM_FREE(sym); + return(NULL); + } + + sym->sym_type = ACLSYMACL; + sym->sym_data = (void *) acl; + return(sym); + +} + +/* + * LOCAL FUNCTION + * + * Add a acl symbol to an acl_list's symbol table. + * + * Each acl list has a symbol table. the symbol table + * is a quick qay to reference named acl's + */ + +static int +acl_sym_add(ACLListHandle_t *acl_list, ACLHandle_t *acl) +{ +Symbol_t *sym; +int rv; + + if ( acl->tag == NULL ) + return(ACLERRUNDEF); + + rv = symTableFindSym(acl_list->acl_sym_table, + acl->tag, + ACLSYMACL, + (void **)&sym); + if ( rv == SYMERRNOSYM ) { + sym = acl_sym_new(acl); + if ( sym ) + rv = symTableAddSym(acl_list->acl_sym_table, sym, (void *)sym); + } + + if ( sym == NULL || rv < 0 ) + return(ACLERRUNDEF); + + return(0); +} + +/* + * LOCAL FUNCTION + * + * Destroy an acl_list's symbol table and all memory referenced + * by the symbol table. This does not destroy an acl_list. + */ + +static void +acl_symtab_destroy(ACLListHandle_t *acl_list) +{ + /* Destroy each entry in the symbol table */ + symTableEnumerate(acl_list->acl_sym_table, 0, acl_hash_entry_destroy); + /* Destory the hash table itself */ + symTableDestroy(acl_list->acl_sym_table, 0); + acl_list->acl_sym_table = NULL; + return; +} + + +/* + * Appends to a specified ACL + * + * This function appends a specified ACL to the end of a given ACL list. + * + * Input: + * errp The error stack + * flags should always be zero now + * acl_list target ACL list + * acl new acl + * Returns: + * > 0 The number of acl's in the current list + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ListAppend( NSErr_t *errp, ACLListHandle_t *acl_list, ACLHandle_t *acl, + int flags ) +{ + ACLWrapper_t *wrapper; + ACLHandle_t *tmp_acl; + + if ( acl_list == NULL || acl == NULL ) + return(ACLERRUNDEF); + + if ( acl_list->acl_sym_table == NULL && + acl_list->acl_count == ACL_TABLE_THRESHOLD ) { + + /* + * The symbol table isn't really critical so we don't log + * an error if its creation fails. + */ + + symTableNew(&acl_list->acl_sym_table); + if ( acl_list->acl_sym_table ) { + for (wrapper = acl_list->acl_list_head; wrapper; + wrapper = wrapper->wrap_next ) { + tmp_acl = wrapper->acl; + if ( acl_sym_add(acl_list, tmp_acl) ) { + acl_symtab_destroy(acl_list); + break; + } + } + } + } + + wrapper = acl_wrapper_new(); + if ( wrapper == NULL ) + return(ACLERRNOMEM); + + wrapper->acl = acl; + + if ( acl_list->acl_list_head == NULL ) { + acl_list->acl_list_head = wrapper; + acl_list->acl_list_tail = wrapper; + } else { + acl_list->acl_list_tail->wrap_next = wrapper; + acl_list->acl_list_tail = wrapper; + } + + acl->ref_count++; + + acl_list->acl_count++; + + + if ( acl_list->acl_sym_table ) { + /* + * If we fail to insert the ACL then we + * might as well destroy this hash table since it is + * useless. + */ + if ( acl_sym_add(acl_list, acl) ) { + acl_symtab_destroy(acl_list); + } + } + + + return(acl_list->acl_count); +} + +/* + * Concatenates two ACL lists + * + * Attaches all ACLs in acl_list2 to the end of acl_list1. acl_list2 + * is left unchanged. + * + * Input: + * errp pointer to the error stack + * acl_list1 target ACL list + * acl_list2 source ACL list + * Output: + * acl_list1 list contains the concatenation of acl_list1 + * and acl_list2. + * Returns: + * > 0 Number of ACLs in acl_list1 after concat + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ListConcat( NSErr_t *errp, ACLListHandle_t *acl_list1, + ACLListHandle_t *acl_list2, int flags ) +{ +ACLWrapper_t *wrapper; +int rv; + + if ( acl_list1 == NULL || acl_list2 == NULL ) + return(ACLERRUNDEF); + + for ( wrapper = acl_list2->acl_list_head; + wrapper != NULL; wrapper = wrapper->wrap_next ) + if ( (rv = ACL_ListAppend ( errp, acl_list1, wrapper->acl, 0 )) < 0 ) + return(rv); + + return(acl_list1->acl_count); +} + +/* + * LOCAL FUNCTION + * + * Free up memory associated with and ACLExprEntry. Probably + * only useful internally since we aren't exporting + * this structure. + */ + +static void +ACL_ExprEntryDestroy( ACLExprEntry_t *entry ) +{ + LASFlushFunc_t flushp; + + if ( entry == NULL ) + return; + + if ( entry->las_cookie ) +/* freeLAS(NULL, entry->attr_name, &entry->las_cookie); */ + { + ACL_LasFindFlush( NULL, entry->attr_name, &flushp ); + if ( flushp ) + ( *flushp )( &entry->las_cookie ); + } + + if ( entry->attr_name ) + PERM_FREE( entry->attr_name ); + + if ( entry->attr_pattern ) + PERM_FREE( entry->attr_pattern ); + + return; +} + +/* + * LOCAL FUNCTION + * + * This function is used to free all the pvalue memory + * in a plist. + */ + +static void +acl_expr_auth_destroy(char *pname, const void *pvalue, void *user_data) +{ + PERM_FREE((char *) pvalue); + return; +} + +/* + * Free up memory associated with and ACLExprHandle. + * + * Input: + * expr expression handle to free up + */ + +NSAPI_PUBLIC void +ACL_ExprDestroy( ACLExprHandle_t *expr ) +{ +int ii; + + if ( expr == NULL ) + return; + + if ( expr->expr_tag ) + PERM_FREE( expr->expr_tag ); + + if ( expr->expr_argv ) { + for ( ii = 0; ii < expr->expr_argc; ii++ ) + if ( expr->expr_argv[ii] ) + PERM_FREE( expr->expr_argv[ii] ); + PERM_FREE( expr->expr_argv ); + } + + for ( ii = 0; ii < expr->expr_term_index; ii++ ) + ACL_ExprEntryDestroy( &expr->expr_arry[ii] ); + + if ( expr->expr_auth ) { + PListEnumerate(expr->expr_auth, acl_expr_auth_destroy, NULL); + PListDestroy(expr->expr_auth); + } + + PERM_FREE( expr->expr_arry ); + PERM_FREE( expr->expr_raw ); + + PERM_FREE( expr ); + + return; +} + +/* + * Free up memory associated with and ACLHandle. + * + * Input: + * acl target acl + */ + +NSAPI_PUBLIC void +ACL_AclDestroy(NSErr_t *errp, ACLHandle_t *acl ) +{ +ACLExprHandle_t *handle; +ACLExprHandle_t *tmp; + + if ( acl == NULL ) + return; + + acl->ref_count--; + + if ( acl->ref_count ) + return; + + if ( acl->tag ) + PERM_FREE( acl->tag ); + + if ( acl->las_name ) + PERM_FREE( acl->las_name ); + + if ( acl->attr_name ) + PERM_FREE( acl->attr_name ); + + handle = acl->expr_list_head; + while ( handle ) { + tmp = handle; + handle = handle->expr_next; + ACL_ExprDestroy( tmp ); + } + + PERM_FREE(acl); + + return; +} + +/* + * Destorys a input ACL List + * + * Input: + * acl_list target list + * Output: + * none target list is freed + */ + +NSAPI_PUBLIC void +ACL_ListDestroy(NSErr_t *errp, ACLListHandle_t *acl_list ) +{ + ACLWrapper_t *wrapper; + ACLWrapper_t *tmp_wrapper; + ACLHandle_t *tmp_acl; + + + if ( acl_list == NULL ) + return; + + if ( acl_list->acl_sym_table ) { + /* Destroy each entry in the symbol table */ + symTableEnumerate(acl_list->acl_sym_table, 0, acl_hash_entry_destroy); + /* Destory the hash table itself */ + symTableDestroy(acl_list->acl_sym_table, 0); + } + + ACL_EvalDestroyContext( (ACLListCache_t *)acl_list->cache ); + + wrapper = acl_list->acl_list_head; + + while ( wrapper ) { + tmp_acl = wrapper->acl; + tmp_wrapper = wrapper; + wrapper = wrapper->wrap_next; + PERM_FREE( tmp_wrapper ); + ACL_AclDestroy(errp, tmp_acl ); + } + + PERM_FREE( acl_list ); + + return; +} + +/* + * FUNCTION: ACL_ListGetFirst + * + * DESCRIPTION: + * + * This function is used to start an enumeration of an + * ACLListHandle_t. It returns an ACLHandle_t* for the first + * ACL on the list, and initializes a handle supplied by the + * caller, which is used to track the current position in the + * enumeration. This function is normally used in a loop + * such as: + * + * ACLListHandle_t *acl_list = <some ACL list>; + * ACLHandle_t *cur_acl; + * ACLListEnum_t acl_enum; + * + * for (cur_acl = ACL_ListGetFirst(acl_list, &acl_enum); + * cur_acl != 0; + * cur_acl = ACL_ListGetNext(acl_list, &acl_enum)) { + * ... + * } + * + * The caller should guarantee that no ACLs are added or removed + * from the ACL list during the enumeration. + * + * ARGUMENTS: + * + * acl_list - handle for the ACL list + * acl_enum - pointer to uninitialized enumeration handle + * + * RETURNS: + * + * As described above. If the acl_list argument is null, or the + * referenced ACL list is empty, the return value is null. + */ + +NSAPI_PUBLIC ACLHandle_t * +ACL_ListGetFirst(ACLListHandle_t *acl_list, ACLListEnum_t *acl_enum) +{ + ACLWrapper_t *wrapper; + ACLHandle_t *acl = 0; + + *acl_enum = 0; + + if (acl_list) { + + wrapper = acl_list->acl_list_head; + *acl_enum = (ACLListEnum_t)wrapper; + + if (wrapper) { + acl = wrapper->acl; + } + } + + return acl; +} + +NSAPI_PUBLIC ACLHandle_t * +ACL_ListGetNext(ACLListHandle_t *acl_list, ACLListEnum_t *acl_enum) +{ + ACLWrapper_t *wrapper = (ACLWrapper_t *)(*acl_enum); + ACLHandle_t *acl = 0; + + if (wrapper) { + + wrapper = wrapper->wrap_next; + *acl_enum = (ACLListEnum_t)wrapper; + + if (wrapper) acl = wrapper->acl; + } + + return acl; +} + +/* + * FUNCTION: ACL_AclGetTag + * + * DESCRIPTION: + * + * Returns the tag string associated with an ACL. + * + * ARGUMENTS: + * + * acl - handle for an ACL + * + * RETURNS: + * + * The return value is a pointer to the ACL tag string. + */ + +NSAPI_PUBLIC const char * +ACL_AclGetTag(ACLHandle_t *acl) +{ + return (acl) ? (const char *)(acl->tag) : 0; +} + +/* + * Finds a named ACL in an input list. + * + * Input: + * acl_list a list of ACLs to search + * acl_name the name of the ACL to find + * flags e.g. ACL_CASE_INSENSITIVE + * Returns: + * NULL No ACL found + * acl A pointer to an ACL with named acl_name + */ + +NSAPI_PUBLIC ACLHandle_t * +ACL_ListFind (NSErr_t *errp, ACLListHandle_t *acl_list, char *acl_name, int flags ) +{ +ACLHandle_t *result = NULL; +ACLWrapper_t *wrapper; +Symbol_t *sym; + + if ( acl_list == NULL || acl_name == NULL ) + return( result ); + + /* + * right now the symbol table exists if there hasn't been + * any collisions based on using case insensitive names. + * if there are any collisions then the table will be + * deleted and we will look up using list search. + * + * we should probably create two hash tables, one for case + * sensitive lookups and the other for insensitive. + */ + if ( acl_list->acl_sym_table ) { + if ( symTableFindSym(acl_list->acl_sym_table, + acl_name, ACLSYMACL, (void **) &sym) >= 0 ) { + result = (ACLHandle_t *) sym->sym_data; + if ( result && (flags & ACL_CASE_SENSITIVE) && + strcmp(result->tag, acl_name) ) { + result = NULL; /* case doesn't match */ + } + } + return( result ); + } + + if ( flags & ACL_CASE_INSENSITIVE ) { + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag && + strcasecmp( wrapper->acl->tag, acl_name ) == 0 ) { + result = wrapper->acl; + break; + } + } + } else { + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag && + strcmp( wrapper->acl->tag, acl_name ) == 0 ) { + result = wrapper->acl; + break; + } + } + } + + return( result ); +} + +/* + * Function parses an input ACL file and resturns an + * ACLListHandle_t pointer that represents the entire + * file without the comments. + * + * Input: + * filename the name of the target ACL text file + * errp a pointer to an error stack + * + * Returns: + * NULL parse failed + * + */ + +NSAPI_PUBLIC ACLListHandle_t * +ACL_ParseFile( NSErr_t *errp, char *filename ) +{ +ACLListHandle_t *handle = NULL; +int eid = 0; +int rv = 0; +char *errmsg; + + ACL_InitAttr2Index(); + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + if ( acl_InitScanner( errp, filename, NULL ) < 0 ) { + rv = ACLERROPEN; + eid = ACLERR1900; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + } else { + + handle = ACL_ListNew(errp); + if ( handle == NULL ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else if ( acl_PushListHandle( handle ) < 0 ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else if ( acl_Parse() ) { + rv = ACLERRPARSE; + eid = ACLERR1780; + } + + if ( acl_EndScanner() < 0 ) { + rv = ACLERROPEN; + eid = ACLERR1500; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + } + + } + + if ( rv || eid ) { + ACL_ListDestroy(errp, handle); + handle = NULL; + } + + crit_exit( acl_parse_crit ); + return(handle); + +} + +/* + * Function parses an input ACL string and returns an + * ACLListHandle_t pointer that represents the entire + * file without the comments. + * + * Input: + * buffer the target ACL buffer + * errp a pointer to an error stack + * + * Returns: + * NULL parse failed + * + */ + +NSAPI_PUBLIC ACLListHandle_t * +ACL_ParseString( NSErr_t *errp, char *buffer ) +{ +ACLListHandle_t *handle = NULL; +int eid = 0; +int rv = 0; +char *errmsg; + + ACL_InitAttr2Index(); + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + if ( acl_InitScanner( errp, NULL, buffer ) < 0 ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else { + + handle = ACL_ListNew(errp); + if ( handle == NULL ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else if ( acl_PushListHandle( handle ) < 0 ) { + rv = ACLERRNOMEM; + eid = ACLERR1920; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else if ( acl_Parse() ) { + rv = ACLERRPARSE; + eid = ACLERR1780; + } + + if ( acl_EndScanner() < 0 ) { + rv = ACLERROPEN; + eid = ACLERR1500; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, "buffer", errmsg); + } + + } + + if ( rv || eid ) { + ACL_ListDestroy(errp, handle); + handle = NULL; + } + + crit_exit( acl_parse_crit ); + return(handle); + +} + +/* + * LOCAL FUNCTION + * + * Convert sub-expression to string. + */ + +static int +acl_expr_string( ACLExprOp_t logical, ACLExprStack_t *expr_stack ) +{ +char **expr_text; +char **prev_expr_text; +char *tmp; + + switch (logical) { + case ACL_EXPR_OP_NOT: + if ( expr_stack->stack_index < 1 ) { + printf("expression stack underflow.\n"); + return(ACLERRINTERNAL); + } + + expr_text = &expr_stack->expr_text[expr_stack->stack_index - 1]; + tmp = (char *) PERM_MALLOC(strlen(*expr_text) + 7); + if ( tmp == NULL ) + return(ACLERRNOMEM); + + if ( expr_stack->found_subexpression ) { + sprintf(tmp, "not (%s)", *expr_text); + expr_stack->found_subexpression = 0; + expr_stack->last_subexpression = expr_stack->stack_index - 1; + } else { + sprintf(tmp, "not %s", *expr_text); + } + + PERM_FREE(*expr_text); + *expr_text = tmp; + return(0); + + case ACL_EXPR_OP_AND: + case ACL_EXPR_OP_OR: + if ( expr_stack->stack_index < 2 ) { + printf("expression stack underflow.\n"); + return(ACLERRINTERNAL); + } + + expr_stack->stack_index--; + prev_expr_text = &expr_stack->expr_text[expr_stack->stack_index]; + expr_stack->stack_index--; + expr_text = &expr_stack->expr_text[expr_stack->stack_index]; + + tmp = (char *) PERM_MALLOC (strlen(*expr_text) + + strlen(*prev_expr_text) + 15); + if ( tmp == NULL ) + return(ACLERRNOMEM); + + if ( expr_stack->found_subexpression && + expr_stack->stack_index == expr_stack->last_subexpression && + logical == ACL_EXPR_OP_AND ) { + sprintf(tmp, "%s and\n (%s)", *expr_text, *prev_expr_text); + } else if ( expr_stack->found_subexpression && + expr_stack->stack_index == expr_stack->last_subexpression ) { + sprintf(tmp, "%s or\n (%s)", *expr_text, *prev_expr_text); + } else if ( logical == ACL_EXPR_OP_AND ) { + sprintf(tmp, "%s and\n %s", *expr_text, *prev_expr_text); + } else { + sprintf(tmp, "%s or\n %s", *expr_text, *prev_expr_text); + } + + expr_stack->found_subexpression++; + expr_stack->stack_index++; + PERM_FREE(*expr_text); + PERM_FREE(*prev_expr_text); + *expr_text = tmp; + *prev_expr_text = NULL; + return(0); + + default: + printf("Bad boolean logic value.\n"); + return(ACLERRINTERNAL); + } + +} + +/* + * LOCAL FUNCTION + * + * Reduce all sub-expressions to a single string. + */ + +static int +acl_reduce_expr_logic( ACLExprStack_t *expr_stack, ACLExprRaw_t *expr_raw ) +{ +char **expr_text; +char **prev_expr_text; +char *tmp; + + if (expr_raw->attr_name) { + if (expr_stack->stack_index >= ACL_EXPR_STACK ) { + printf("expression stack overflow."); + return(ACLERRINTERNAL); + } + + if ( expr_stack->found_subexpression && expr_stack->stack_index > 0 ) { + prev_expr_text = &expr_stack->expr_text[expr_stack->stack_index-1]; + tmp = (char *) PERM_MALLOC(strlen(*prev_expr_text) + 3); + sprintf(tmp, "(%s)", *prev_expr_text); + PERM_FREE(*prev_expr_text); + *prev_expr_text = tmp; + expr_stack->found_subexpression = 0; + expr_stack->last_subexpression = expr_stack->stack_index - 1; + } + + expr_stack->expr[expr_stack->stack_index] = expr_raw; + expr_text = &expr_stack->expr_text[expr_stack->stack_index]; + *expr_text = (char *) PERM_MALLOC(strlen(expr_raw->attr_name) + + strlen(expr_raw->attr_pattern) + + 7); + if ( *expr_text == NULL ) + return(ACLERRNOMEM); + + sprintf(*expr_text, "%s %s \"%s\"", expr_raw->attr_name, + acl_comp_string(expr_raw->comparator), + expr_raw->attr_pattern); + + expr_stack->stack_index++; + expr_stack->expr_text[expr_stack->stack_index] = NULL; + } else { + return(acl_expr_string(expr_raw->logical, expr_stack)); + } + return(0); +} + +/* + * LOCAL FUNCTION + * + * Appends str2 to str1. + * + * Input: + * str1 an existing dynamically allocated string + * str2 a text string + * Returns: + * 0 success + * < 0 failure + */ + +static int +acl_to_str_append(acl_string_t * p_aclstr, const char *str2) +{ + int str2len, newlen; + + if (p_aclstr == NULL || str2 == NULL) + return (ACLERRINTERNAL); + if (p_aclstr->str == NULL) { + p_aclstr->str = (char *) PERM_MALLOC(4096); + if (p_aclstr->str == NULL) + return (ACLERRNOMEM); + p_aclstr->str_size = 4096; + p_aclstr->str_len = 0; + } + + str2len = strlen(str2); + newlen = p_aclstr->str_len + str2len; + if (newlen >= p_aclstr->str_size) { + p_aclstr->str_size = str2len > 4095 ? str2len+p_aclstr->str_size+1 : 4096+p_aclstr->str_size ; + p_aclstr->str = (char *) PERM_REALLOC(p_aclstr->str, p_aclstr->str_size); + if (p_aclstr->str == NULL) + return (ACLERRNOMEM); + } + memcpy((void *)&(p_aclstr->str[p_aclstr->str_len]), (void *) str2, str2len+1); + p_aclstr->str_len += str2len; + return 0; +} + +/* + * LOCAL FUNCTION + * + * Output Authorization Expression type either "Allow" or "Deny" + */ + +static int +acl_to_str_expr_type( acl_string_t *str_t, ACLExprHandle_t *expr ) +{ + switch (expr->expr_type) { + case ACL_EXPR_TYPE_ALLOW: + acl_to_str_append(str_t, "allow "); + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute "); + return(0); + case ACL_EXPR_TYPE_DENY: + acl_to_str_append(str_t, "deny "); + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute "); + return(0); + case ACL_EXPR_TYPE_AUTH: + acl_to_str_append(str_t, "authenticate "); + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute "); + return(0); + case ACL_EXPR_TYPE_RESPONSE: + acl_to_str_append(str_t, "deny with "); + return(0); + default: + return(ACLERRINTERNAL); + } +} + +/* + * LOCAL FUNCTION + * + * Output Authorization Expression Rights "(right, right)" + */ + +static int +acl_to_str_expr_arg( acl_string_t *str_t, ACLExprHandle_t *expr ) +{ +int ii; + + if ( expr->expr_argc <= 0 ) { + return(ACLERRINTERNAL); + } + + if ( expr->expr_type == ACL_EXPR_TYPE_RESPONSE ) { + acl_to_str_append(str_t, expr->expr_argv[0]); + acl_to_str_append(str_t, "=\""); + acl_to_str_append(str_t, expr->expr_argv[1]); + acl_to_str_append(str_t, "\";\n"); + return(0); + } + + acl_to_str_append(str_t, "("); + for (ii = 0; ii < expr->expr_argc; ii++) { + acl_to_str_append(str_t, expr->expr_argv[ii]); + if ( ii < expr->expr_argc - 1 ) { + acl_to_str_append(str_t, ","); + } + } + acl_to_str_append(str_t, ") "); + + return(0); +} + +/* + * LOCAL FUNCTION + * + * Walks through the authentication statement PList_t and + * prints the structure to a string. + */ + +static void +acl_to_str_auth_expr(char *lval, const void *rval, void *user_data) +{ + // ###### char **str = (char **) user_data; + acl_string_t * p_aclstr = (acl_string_t *) user_data; + + acl_to_str_append(p_aclstr, "\t"); + acl_to_str_append(p_aclstr, lval); + acl_to_str_append(p_aclstr, " = \""); + acl_to_str_append(p_aclstr, (char *) rval); + acl_to_str_append(p_aclstr, "\";\n"); + + return; +} + +/* + * LOCAL FUNCTION + * + * Output the logic part of the authencation statement to a string. + */ + +static int +acl_to_str_auth_logic( acl_string_t *str_t, ACLExprHandle_t *expr) +{ + + if ( expr->expr_auth == NULL ) { + acl_to_str_append(str_t, "{\n"); + acl_to_str_append(str_t, "# Authenticate statement with no body?\n"); + acl_to_str_append(str_t, "\tnull=null;\n"); + acl_to_str_append(str_t, "};\n"); + return(0); + } + + acl_to_str_append(str_t, "{\n"); + PListEnumerate(expr->expr_auth, acl_to_str_auth_expr, (void *) str_t); + acl_to_str_append(str_t, "};\n"); + + return(0); +} + + +/* + * LOCAL FUNCTION + * + * Output the logic part of the authorization statement to a string. + */ + +static int +acl_to_str_expr_logic( acl_string_t *str_t, ACLExprHandle_t *expr, ACLExprStack_t *expr_stack) +{ +int rv = 0; +int ii; + + expr_stack->stack_index = 0; + expr_stack->found_subexpression = 0; + expr_stack->last_subexpression = -1; + + for (ii = 0; ii < expr->expr_raw_index; ii++) { + rv = acl_reduce_expr_logic(expr_stack, &expr->expr_raw[ii]); + if (rv) break; + } + + if (!rv && expr_stack->expr_text[0]) { + acl_to_str_append(str_t, "\n "); + acl_to_str_append(str_t, expr_stack->expr_text[0]); + acl_to_str_append(str_t, ";\n"); + PERM_FREE(expr_stack->expr_text[0]); + } + + return(rv); +} + +/* + * LOCAL FUNCTION + * + * Output an ACL list to a string. + */ + +static int +acl_to_str_create( acl_string_t *str_t, ACLListHandle_t *acl_list ) +{ +ACLWrapper_t *wrap; +ACLHandle_t *acl; +ACLExprHandle_t *expr; +int rv = 0; +ACLExprStack_t *expr_stack; + + expr_stack = (ACLExprStack_t *) PERM_MALLOC(sizeof(ACLExprStack_t)); + if ( expr_stack == NULL ) + return(ACLERRNOMEM); + + acl_to_str_append(str_t, "# File automatically written\n"); + acl_to_str_append(str_t, "#\n"); + acl_to_str_append(str_t, "# You may edit this file by hand\n"); + acl_to_str_append(str_t, "#\n\n"); + if ( acl_list->acl_list_head == NULL ) { + PERM_FREE(expr_stack); + return(0); + } + + acl_to_str_append(str_t, "version 3.0;\n"); + for (wrap = acl_list->acl_list_head; wrap && !rv; + wrap = wrap->wrap_next ) { + acl = wrap->acl; + if ( acl->tag ) { + acl_to_str_append(str_t, "\nacl \""); + acl_to_str_append(str_t, acl->tag); + acl_to_str_append(str_t, "\";\n"); + } else { + acl_to_str_append(str_t, "\nacl;\n"); + } + + for (expr = acl->expr_list_head; expr && rv == 0; + expr = expr->expr_next ) { + + if ( (rv = acl_to_str_expr_type(str_t, expr)) < 0 ) + break; + + if ( (rv = acl_to_str_expr_arg(str_t, expr)) < 0) + break; + + switch (expr->expr_type) { + case ACL_EXPR_TYPE_DENY: + case ACL_EXPR_TYPE_ALLOW: + rv = acl_to_str_expr_logic(str_t, expr, expr_stack); + break; + case ACL_EXPR_TYPE_AUTH: + rv = acl_to_str_auth_logic(str_t, expr); + break; + case ACL_EXPR_TYPE_RESPONSE: + break; + } + + } + } + + PERM_FREE(expr_stack); + return(rv); +} + + +/* + * Creates an ACL text string from an ACL handle + * + * Input: + * errp error stack + * acl target text string pointer + * acl_list Source ACL list handle + * Ouput: + * acl a chunk of dynamic memory pointing to ACL text + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_WriteString(NSErr_t *errp, char **acl, ACLListHandle_t *acl_list) +{ + int rv; + acl_string_t str_t = {NULL,0,0}; + + if ( acl_list == NULL || acl == NULL ) + return(ACLERRUNDEF); + + rv = acl_to_str_create(&str_t, acl_list); + *acl = str_t.str; + + return ( rv ); +} + +/* + * Write an ACL text file from an input ACL list structure. + * + * Input: + * filename name for the output text file + * acl_list a list of ACLs to convert to text + * Output: + * errp an error stack, set if there are errors + * to report + * Returns: + * 0 success + * ACLERROPEN, + * ACLERRNOMEM on failure + */ + +NSAPI_PUBLIC int +ACL_WriteFile( NSErr_t *errp, char *filename, ACLListHandle_t *acl_list ) +{ +int rv; +int eid; +char *errmsg; +#ifdef UTEST +FILE *ofp; +#else +SYS_FILE ofp; +#endif +acl_string_t aclstr = {NULL,0,0}; +char *acl_text = NULL; + + if ( filename == NULL || acl_list == NULL ) { + rv = ACLERROPEN; + eid = ACLERR1900; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + return(ACLERROPEN); + } + +#ifdef UTEST + ofp = fopen(filename, "w"); + if ( ofp == NULL ) { +#else + ofp = system_fopenWT(filename); + if ( ofp == SYS_ERROR_FD ) { +#endif + rv = ACLERROPEN; + eid = ACLERR1900; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + return(ACLERROPEN); + } + + rv = acl_to_str_create(&aclstr, acl_list); + acl_text = aclstr.str; + + if ( rv ) { + eid = ACLERR3000; + rv = ACLERRNOMEM; + nserrGenerate(errp, rv, eid, ACL_Program, 0); + } else { +#ifdef UTEST + if (fputs(acl_text, ofp) == 0) { +#else + if (system_fwrite_atomic(ofp, acl_text, strlen(acl_text))==IO_ERROR) { +#endif + eid = ACLERR3200; + rv = ACLERRIO; + errmsg = system_errmsg(); + nserrGenerate(errp, rv, eid, ACL_Program, 2, filename, errmsg); + } + } + + if ( acl_text ) + PERM_FREE(acl_text); + +#ifdef UTEST + fclose(ofp); +#else + system_fclose(ofp); +#endif + + return(rv); +} + +/* + * Delete a named ACL from an ACL list + * + * Input: + * acl_list Target ACL list handle + * acl_name Name of the target ACL + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ListAclDelete(NSErr_t *errp, ACLListHandle_t *acl_list, char *acl_name, int flags ) +{ +ACLHandle_t *acl = NULL; +ACLWrapper_t *wrapper; +ACLWrapper_t *wrapper_prev = NULL; +Symbol_t *sym; + + if ( acl_list == NULL || acl_name == NULL ) + return(ACLERRUNDEF); + + if ( flags & ACL_CASE_INSENSITIVE ) { + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag && + strcasecmp( wrapper->acl->tag, acl_name ) == 0 ) { + acl = wrapper->acl; + break; + } + wrapper_prev = wrapper; + } + } else { + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag && + strcmp( wrapper->acl->tag, acl_name ) == 0 ) { + acl = wrapper->acl; + break; + } + wrapper_prev = wrapper; + } + } + + if ( acl ) { + + if ( wrapper_prev ) { + wrapper_prev->wrap_next = wrapper->wrap_next; + } else { + acl_list->acl_list_head = wrapper->wrap_next; + } + + if ( acl_list->acl_list_tail == wrapper ) { + acl_list->acl_list_tail = wrapper_prev; + } + + acl = wrapper->acl; + acl_list->acl_count--; + PERM_FREE(wrapper); + + if ( acl_list->acl_sym_table ) { + if ( symTableFindSym(acl_list->acl_sym_table, + acl->tag, ACLSYMACL, (void **) &sym) < 0 ) { + + /* not found, this is an error of some sort */ + + } else { + symTableRemoveSym(acl_list->acl_sym_table, sym); + acl_hash_entry_destroy(sym, 0); + } + } + + ACL_AclDestroy(errp, acl); + return(0); + } + + return(ACLERRUNDEF); +} + +/* + * local function: translate string to lower case + * return <0: fail + * 0: succeed + */ +int +open_file_buf(FILE ** file, char * filename, char *mode, char ** buf, long * size) +{ + int rv = 0; + long cur = 0; + long in = 0; + struct stat fi; + + if (filename==NULL || mode==NULL) { + rv = ACLERROPEN; + goto open_cleanup; + } + + if ((*file=fopen(filename,mode))==NULL) { + rv = ACLERROPEN; + goto open_cleanup; + } + + if (system_stat(filename, &fi)==-1) { + rv = ACLERROPEN; + goto open_cleanup; + } + + *size = fi.st_size; + + if ((*buf=(char *)PERM_MALLOC(*size+1))==NULL) { + rv = ACLERRNOMEM; + goto open_cleanup; + } + + + rv = 0; + while (cur<*size) { + in=fread(&(*buf)[cur], 1, *size, *file); + cur = cur+in; + if (feof(*file)) { + break; + } + if (ferror(*file)) { + rv = ACLERRIO; + break; + } + } + if (rv==0) + (*buf)[cur] = 0; + +open_cleanup: + if (rv<0) { + if (*file) + fclose(*file); + if (*buf) + PERM_FREE(*buf); + } + return rv; +} + + +/* + * local function: writes buf to disk and close the file + */ +void +close_file_buf(FILE * file, char * filename, char * mode, char * buf) +{ + if (file==NULL) + return; + fclose(file); + if (strchr(mode, 'w')!=NULL || strchr(mode, 'a')!=NULL) { + file = fopen(filename, "wb"); + fwrite(buf,1,strlen(buf),file); + fclose(file); + } + PERM_FREE(buf); +} + + +/* + * local function: translate string to lower case + */ +char * +str_tolower(char * string) +{ + register char * p = string; + for (; *p; p++) + *p = tolower(*p); + return string; +} + +/* + * local function: get the first name appear in block + * return: 0 : not found, + * 1 : found + */ +int +acl_get_first_name(char * block, char ** name, char ** next) +{ + char bounds[] = "\t \"\';"; + char boundchar; + char *p=NULL, *q=NULL, *start=NULL, *end=NULL; + + if (block==NULL) + return 0; +try_next: + if ((p=strstr(block, "acl"))!=NULL) { + + // check if this "acl" is the first occurance in this line. + for (q=p-1; ((q>=block) && *q!='\n'); q--) { + if (strchr(" \t",*q)==NULL) { + // if not, try next; + block = p+3; + goto try_next; + } + } + + p+=3; + while (strchr(bounds,*p)&&(*p!=0)) + p++; + if (*p==0) + return 0; + boundchar = *(p-1); + start = p; + while ((boundchar!=*p)&&(*p!=0)&&(*p!=';')) + p++; + if (*p==0) + return 0; + end = p; + *name = (char *)PERM_MALLOC(end-start+1); + strncpy(*name, start, (end-start)); + (*name)[end-start]=0; + *next = end; + return 1; + } + return 0; +} + +/* + * local function: find the pointer to acl string from the given block + */ +char * +acl_strstr(char * block, char * aclname) +{ + const char set[] = "\t \"\';"; + char * name, * rstr = NULL; + char * lowerb = block; + int found = 0; + + if (block==NULL||aclname==NULL) + return NULL; + + while ((name = strstr(block, aclname))!=NULL && !found) { + if (name>lowerb) { // This should be true, just in case + if ((strchr(set,name[-1])!=0) && (strchr(set,name[strlen(aclname)])!=0)) { + // the other 2 sides are in boundary set, that means, this is an exact match. + while (&name[-1]>=lowerb) { + name --; + if (strchr(set, *name)==0) + break; // should point to 'l' + } + + if (name==lowerb) + return NULL; + + if ((name-2)>=lowerb) + if ((name[-2]=='a') && (name[-1]=='c') && (*name=='l')) { + name -= 2; // name point to 'a' + rstr = name; + while (TRUE) { + if (name==lowerb) { + found = 1; + break; + } + else if (name[-1]==' '||name[-1]=='\t') + name --; + else if (name[-1]=='\n') { + found = 1; + break; + } + else + break; // acl is not at the head, there are other chars. + } + } + } + block = name + strlen(aclname); + } + } + return rstr; +} + + + +/* + * local function: find the acl string from mapfile and return its acl structure + */ +int +get_acl_from_file(char * filename, char * aclname, ACLListHandle_t ** acllist_pp) +{ + int rv = 0; + char * pattern=NULL; + char header[] = "version 3.0;\n"; + int headerlen = strlen(header); + long filesize; + FILE * file; + char * mirror=NULL, * text=NULL, *nextname=NULL; + char * block=NULL, * aclhead=NULL, * aclend=NULL; + + *acllist_pp = NULL; + + // build the acl name pattern, which should be acl "..." + // the ".." is built by acl_to_str_create + + if (aclname==NULL) { + rv = ACLERRUNDEF; + goto get_cleanup; + } + + if ((pattern=(char *)PERM_MALLOC(strlen(aclname) + 1))==NULL) { + rv = ACLERRNOMEM; + goto get_cleanup; + } + else { + sprintf(pattern,"%s", aclname); + str_tolower(pattern); + } + + /* get the acl text from the mapfile */ + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto get_cleanup; + + if ((mirror = (char *) PERM_MALLOC(filesize+1))==NULL) { + rv = ACLERRNOMEM; + goto get_cleanup; + } + + memcpy(mirror, block, filesize); + mirror[filesize]=0; + str_tolower(mirror); + + if ((aclhead = acl_strstr(mirror, pattern))!=NULL) { + // use mirror to search, then transfer to work on block; + aclhead = block + (aclhead - mirror); + acl_get_first_name(aclhead+3, &nextname, &aclend); + aclend = acl_strstr(aclhead+3, nextname); + if (aclend == NULL) { + // this is the last acl in the file + aclend = &aclhead[strlen(aclhead)]; + } + + int len = aclend - aclhead; + text = (char *) PERM_MALLOC(len + headerlen + 1); + sprintf(text, "%s", header); + memcpy(&text[headerlen], aclhead, len); + text[headerlen + len] = 0; + + if ((*acllist_pp=ACL_ParseString(NULL, text))==NULL) { + rv = ACLERRPARSE; + } + } + +get_cleanup: + if (pattern) + PERM_FREE(pattern); + if (file) + close_file_buf(file, filename, "rb", block); + if (mirror) + PERM_FREE(mirror); + if (text) + PERM_FREE(text); + if (nextname) + PERM_FREE(nextname); + return rv; +} + + +/* + * local function: delete the acl string from mapfile + */ +int +delete_acl_from_file(char * filename, char * aclname) +{ + char * pattern=NULL; + char header[] = "version 3.0;\n"; + int headerlen = strlen(header); + int rv = ACLERRUNDEF; + long filesize; + FILE * file; + char * mirror=NULL, * text=NULL, * nextname=NULL; + char * block=NULL, * aclhead=NULL, * aclend=NULL; + int remain; + + // build the acl name pattern, which should be acl "..." + // the ".." is built by acl_to_str_create + + if (aclname==NULL) { + rv = ACLERRUNDEF; + goto delete_cleanup; + } + + if ((pattern=(char *)PERM_MALLOC(strlen(aclname) + 10))==NULL) { + rv = ACLERRNOMEM; + goto delete_cleanup; + } + else { + sprintf(pattern,"%s", aclname); + str_tolower(pattern); + } + + /* file the acl text from the mapfile */ + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto delete_cleanup; + + if ((mirror = (char *) PERM_MALLOC(filesize+1))==NULL) { + rv = ACLERRNOMEM; + goto delete_cleanup; + } + + memcpy(mirror, block, filesize); + mirror[filesize]=0; + str_tolower(mirror); + + if ((aclhead = acl_strstr(mirror, pattern))!=NULL) { + // use mirror to search, then transfer to work on block; + aclhead = block + (aclhead - mirror); + acl_get_first_name(aclhead+3, &nextname, &aclend); + aclend = acl_strstr(aclhead+3, nextname); + if (aclend == NULL) { + // this is the last acl in the file + aclend = &aclhead[strlen(aclhead)]; + } + + int len = aclend - aclhead; + text = (char *) PERM_MALLOC(len + headerlen + 1); + sprintf(text, "%s", header); + memcpy(&text[headerlen], aclhead, len); + text[headerlen + len] = 0; + + if (ACL_ParseString(NULL, text)==NULL) { + rv = ACLERRPARSE; + goto delete_cleanup; + } + } + + if (aclhead!=NULL) { // found the acl in the map file + + // int filesize = mpfile->Size(); + + remain = strlen(aclend); + if (memcpy(aclhead, aclend, remain)!=NULL) + rv = 0; + else + rv = ACLERRIO; + + aclhead[remain]=0; + + block = (char *) PERM_REALLOC(block, strlen(block)+1); + } + else + rv = ACLERRUNDEF; + +delete_cleanup: + if (pattern) + PERM_FREE(pattern); + if (text) + PERM_FREE(text); + if (mirror) + PERM_FREE(mirror); + if (nextname) + PERM_FREE(nextname); + if (file) + close_file_buf(file, filename, "wb", block); + return rv; +} + +/* + * local function: append the acl string to file + */ +int +append_acl_to_file(char * filename, char * aclname, char * acltext) +{ + int rv; + /* acltext has been parsed to verify syntax up to this point */ + char * pattern=NULL; + char * start=NULL; + char * block; + long filesize; + FILE * file; + long len; + + if ((pattern=(char *)PERM_MALLOC(strlen(aclname) + 10))==NULL) { + rv = ACLERRNOMEM; + goto append_cleanup; + } + else { + sprintf(pattern,"%s", aclname); + } + + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto append_cleanup; + + // find the begining of acl, skip the version part + + len = strlen(block); + start = acl_strstr(acltext, pattern); + if ((block=(char *)PERM_REALLOC(block, len+strlen(start)+1))==NULL) { + rv = ACLERRNOMEM; + goto append_cleanup; + } + strcat(block, start); + +append_cleanup: + if (pattern) + PERM_FREE(pattern); + if (file) + close_file_buf(file, filename, "wb", block); + + return rv; +} + + + +/* + * local function: rename the acl name in the file + */ +int +rename_acl_in_file(char * filename, char * aclname, char * newname) +{ + ACLListHandle_t * racllist=NULL; + char * pattern=NULL; + char header[] = "version 3.0;\n"; + int headerlen = strlen(header); + int rv = 0; + long filesize; + FILE * file; + int remain; + long len; + char * text=NULL, * mirror=NULL, * nextname=NULL; + char * block=NULL, * aclhead=NULL, * aclend=NULL; + char * cut=NULL; + acl_string_t str_t = {NULL,0,0}; + + // build the acl name pattern, which should be acl "..." + // the ".." is built by acl_to_str_create + + if (aclname==NULL || newname==NULL) { + rv = ACLERRUNDEF; + goto rename_cleanup; + } + + if ((pattern=(char *)PERM_MALLOC(strlen(aclname) + 10))==NULL) { + rv = ACLERRNOMEM; + goto rename_cleanup; + } + else { + sprintf(pattern,"%s", aclname); + str_tolower(pattern); + } + + // file the acl text from the mapfile + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto rename_cleanup; + + if ((mirror = (char *) PERM_MALLOC(filesize+1))==NULL) { + rv = ACLERRNOMEM; + goto rename_cleanup; + } + + memcpy(mirror, block, filesize); + mirror[filesize]=0; + str_tolower(mirror); + + if ((aclhead = acl_strstr(mirror, pattern))!=NULL) { + // use mirror to search, then transfer to work on block; + aclhead = block + (aclhead - mirror); + acl_get_first_name(aclhead+3, &nextname, &aclend); + aclend = acl_strstr(aclhead+3, nextname); + if (aclend == NULL) { + // this is the last acl in the file + aclend = &aclhead[strlen(aclhead)]; + } + + len = aclend - aclhead; + text = (char *) PERM_MALLOC(len + headerlen + 1); + sprintf(text, "%s", header); + memcpy(&text[headerlen], aclhead, len); + text[headerlen + len] = 0; + + if ((racllist=ACL_ParseString(NULL, text))==NULL) { + rv = ACLERRPARSE; + goto rename_cleanup; + } + } + + if (aclhead!=NULL) { // found the acl in the map file + + remain = strlen(aclend); + // delete the acltext from where it is + if (memcpy(aclhead, aclend, remain)!=NULL) + rv = 0; + else + rv = ACLERRUNDEF; + + aclhead[remain] = 0; + len = strlen(block); + + /* establish the renamed the acl */ + acl_to_str_append(&str_t, "acl \""); + acl_to_str_append(&str_t, newname); + acl_to_str_append(&str_t, "\";"); + /* skip acl "..."; the semicollon in the last counts for the +1 + add the rest acl text to str_t */ + cut = strchr(text, ';'); // skip version ...; + cut = strchr(cut+1, ';') + 1; // skip acl ...; + if (cut==NULL) { + rv = ACLERRUNDEF; + goto rename_cleanup; + } + acl_to_str_append(&str_t, cut); + // acl_to_str_append(&str_t, "\n"); + + if ((block=(char *) PERM_REALLOC(block, len + strlen(str_t.str) + 1))==NULL) { + rv = ACLERRNOMEM; + goto rename_cleanup; + } + // strcat(block, "\n"); + strcat(block, str_t.str); + } + else + rv = ACLERRUNDEF; + +rename_cleanup: + if (pattern) + PERM_FREE(pattern); + if (text) + PERM_FREE(text); + if (mirror) + PERM_FREE(mirror); + if (nextname) + PERM_FREE(nextname); + if (str_t.str) + PERM_FREE(str_t.str); + if (file) + close_file_buf(file, filename, "wb", block); + return rv; +} + + +/* + * Retrieves the definition of a named ACL + * + * Input: + * errp a error stack + * filename Target ACL file + * acl_name Name of the target ACL + * acl_text a dynmaically allocated text (result) + * Output: + * errp error stack is set on error + * Returns: + * 0 success + * <0 failure + */ +NSAPI_PUBLIC int +ACL_FileGetAcl(NSErr_t *errp, + char *filename, + char *acl_name, + // ACLListHandle_t **acllist_p, + char ** acltext, + int flags) +{ + int rv; + ACLListHandle_t * acllist_p; + + if (acl_parse_crit == NULL) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + rv = get_acl_from_file(filename, acl_name, &acllist_p); + + if (acllist_p == NULL) { + *acltext = NULL; + goto get_cleanup; + } + + /* + if ((rv=ACL_Decompose(errp, acltext, acllist_p))<0) { + *acltext = NULL; + goto get_cleanup; + } + */ + if ((rv=ACL_WriteString(errp, acltext, acllist_p))<0) { + *acltext = NULL; + goto get_cleanup; + } + + +get_cleanup: + + crit_exit( acl_parse_crit ); + + return rv; +} + + + +/* + * Delete a named ACL from an ACL file + * + * Input: + * errp a error stack + * filename Target ACL file + * acl_name Name of the target ACL + * Output: + * errp error stack is set on error + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_FileDeleteAcl(NSErr_t *errp, + char *filename, + char *acl_name, + int flags) +{ + int rv = 0; + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + rv = delete_acl_from_file(filename, acl_name); + + crit_exit( acl_parse_crit ); + return(rv); +} + + +/* + * Sets the definition of an ACL in an ACL file + * + * Input: + * errp a error stack + * filename Target ACL file + * acl_name Name of the target ACL + * acl_text a string that defines the new ACL + * Output: + * errp error stack is set on error + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_FileSetAcl(NSErr_t *errp, + char *filename, + char *acl_text, + int flags) +{ + int rv = 0; + ACLListHandle_t *new_acl_list = NULL; + char **acl_name_list = NULL; + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + // get the acl name. + new_acl_list = ACL_ParseString(errp, acl_text); + if ( new_acl_list == NULL ) { + rv = ACLERRPARSE; + goto set_cleanup; + } + + if ( ACL_ListGetNameList(errp, new_acl_list, &acl_name_list) < 0 ) { + rv = ACLERRNOMEM; + goto set_cleanup; + } + + + delete_acl_from_file(filename, acl_name_list[0]); + rv = append_acl_to_file(filename, acl_name_list[0], acl_text); + +set_cleanup: + + crit_exit( acl_parse_crit ); + if (new_acl_list) + ACL_ListDestroy(errp, new_acl_list); + if (acl_name_list) + free(acl_name_list); + return(rv); +} + + +/* + * Rename a named ACL in ACL text file + * + * Input: + * errp a error stack + * filename Target ACL file + * acl_name Name of the target ACL + * new_acl_name New ACL name + * Output: + * errp error stack is set on error + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_FileRenameAcl(NSErr_t *errp, + char *filename, + char *aclname, + char *newname, + int flags) +{ + int rv = 0; + + if ( acl_parse_crit == NULL ) + acl_parse_crit = crit_init(); + + crit_enter( acl_parse_crit ); + + rv = rename_acl_in_file(filename, aclname, newname); + + crit_exit( acl_parse_crit ); + return(rv); + +} + + +// +// Merge a list of ACLs into one ACL +// +// Input: +// filename the target acl file +// acl_list ACLs to merge +// new_acl_name resultant ACL +// flags currently ignored +// Returns: +// 0 success +// < 0 failure +// + +NSAPI_PUBLIC int +ACL_FileMergeAcl(NSErr_t *errp, + char *filename, + char **acl_name_list, + char *new_acl_name, + int flags) +{ + ACLListHandle_t *new_acl_list = NULL; + ACLListHandle_t *tmp_acl_list = NULL; + int ii; + int rv; + ACLHandle_t *tmp_acl; + ACLHandle_t *new_acl; + ACLExprHandle_t *expr; + + + tmp_acl_list = ACL_ParseFile(errp, filename); + if ( tmp_acl_list == NULL ) { + rv = ACLERRPARSE; + goto cleanup; + } + + new_acl_list = ACL_ParseFile(errp, filename); + if ( new_acl_list == NULL ) { + rv = ACLERRPARSE; + goto cleanup; + } + + // first get rid of all the ACLs that will be merged + + for (ii = 0; acl_name_list[ii]; ii++) { + rv = ACL_ListAclDelete(errp, new_acl_list, acl_name_list[ii], flags); + if ( rv < 0 ) + goto cleanup; + } + + // now create ACL to house the merged result + new_acl = ACL_AclNew(errp, new_acl_name); + if ( new_acl == NULL ) { + rv = ACLERRNOMEM; + goto cleanup; + } + + rv = ACL_ListAppend(errp, new_acl_list, new_acl, flags); + if ( rv < 0 ) + goto cleanup; + + for (ii = 0; acl_name_list[ii]; ii++) { + tmp_acl = ACL_ListFind(errp, tmp_acl_list, acl_name_list[ii], flags); + if ( tmp_acl == NULL ) { + rv = ACLERRUNDEF; + goto cleanup; + } + for (expr = tmp_acl->expr_list_head; expr; expr = expr->expr_next) { + // This call can't really fail unless we pass it a NULL + // or some memory is corrupt. + rv = ACL_ExprAppend(errp, new_acl, expr); + if ( rv < 0 ) + goto cleanup; + tmp_acl->expr_list_head = expr->expr_next; + tmp_acl->expr_count--; + } + + // Last bit of clean up so the destroy routine isn't confused. + + tmp_acl->expr_list_tail = NULL; + tmp_acl->expr_count = 0; + } + + rv = ACL_WriteFile(errp, filename, new_acl_list); + +cleanup: + if ( new_acl_list ) + ACL_ListDestroy(errp, new_acl_list); + if ( tmp_acl_list ) + ACL_ListDestroy(errp, tmp_acl_list); + return(rv); +} + +// +// Merge a list of ACL files into one ACL file +// +// Input: +// filename the target acl file +// file_list ACL files to merge +// flags currently ignored +// Returns: +// 0 success +// < 0 failure +// + +NSAPI_PUBLIC int +ACL_FileMergeFile(NSErr_t *errp, + char *filename, + char **file_list, + int flags) +{ + ACLListHandle_t *new_acl_list = NULL; + ACLListHandle_t *tmp_acl_list = NULL; + int ii; + int rv; + + // we don't care if they have nothing to do + + if ( filename == NULL || file_list == NULL ) + return(0); + + new_acl_list = ACL_ListNew(errp); + if (new_acl_list == NULL) + return(ACLERRNOMEM); + + for (ii = 0; file_list[ii]; ii++) { + tmp_acl_list = ACL_ParseFile(errp, file_list[ii]); + if (tmp_acl_list == NULL) { + rv = ACLERRPARSE; + goto cleanup; + } + rv = ACL_ListConcat(errp, new_acl_list, tmp_acl_list, flags); + if ( rv < 0 ) + goto cleanup; + ACL_ListDestroy(errp, tmp_acl_list); + tmp_acl_list = NULL; + } + + rv = ACL_WriteFile(errp, filename, new_acl_list); + +cleanup: + if ( new_acl_list ) + ACL_ListDestroy(errp, new_acl_list); + if ( tmp_acl_list ) + ACL_ListDestroy(errp, tmp_acl_list); + return(rv); +} + +/* + * Destroy a NameList + * + * Input: + * name_list a dynamically allocated array of strings + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_NameListDestroy(NSErr_t *errp, char **name_list) +{ + int list_index; + + if ( name_list == NULL ) + return(ACLERRUNDEF); + + for ( list_index = 0; name_list[list_index]; list_index++ ) { + PERM_FREE(name_list[list_index]); + } + PERM_FREE(name_list); + return(0); +} + + +/* + * Gets a name list of consisting of all ACL names for input list. + * + * Input: + * acl_list an ACL List handle + * name_list pointer to a list of string pointers + * Returns: + * 0 success + * < 0 failure + */ +NSAPI_PUBLIC int +ACL_ListGetNameList(NSErr_t *errp, ACLListHandle_t *acl_list, char ***name_list) +{ + const int block_size = 50; + ACLWrapper_t *wrapper; + int list_index; + int list_size; + char **tmp_list; + char **local_list; + char *name; + + + if ( acl_list == NULL ) + return(ACLERRUNDEF); + + list_size = block_size; + local_list = (char **) PERM_MALLOC(sizeof(char *) * list_size); + if ( local_list == NULL ) + return(ACLERRNOMEM); + list_index = 0; + local_list[list_index] = NULL; + + for ( wrapper = acl_list->acl_list_head; wrapper != NULL; + wrapper = wrapper->wrap_next ) { + if ( wrapper->acl->tag ) + name = wrapper->acl->tag; + else + name = "noname"; + if ( list_index + 2 > list_size ) { + list_size += block_size; + tmp_list = (char **) PERM_REALLOC(local_list, + sizeof(char *) * list_size); + if ( tmp_list == NULL ) { + ACL_NameListDestroy(errp, local_list); + return(ACLERRNOMEM); + } + local_list = tmp_list; + } + local_list[list_index] = PERM_STRDUP(name); + if ( local_list[list_index] == NULL ) { + ACL_NameListDestroy(errp, local_list); + return(ACLERRNOMEM); + } + list_index++; + local_list[list_index] = NULL; + } + *name_list = local_list; + return(0); +} + +/* + * Gets a name list of consisting of all ACL names from the input aclfile + * + * Input: + * filename acl file + * name_list pointer to a list of string pointers + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_FileGetNameList(NSErr_t *errp, char * filename, char ***name_list) +{ + + const int block_size = 50; + int rv, list_size, list_index; + char ** local_list; + char * block ; + char * name; + char * next; + long filesize; + FILE * file; + char * head; + + if ((rv=open_file_buf(&file, filename, "rb", &block, &filesize))<0) + goto list_cleanup; + + list_size = block_size; + local_list = (char **) PERM_MALLOC(sizeof(char *) * list_size); + if ( local_list == NULL ) { + rv = ACLERRNOMEM; + goto list_cleanup; + } + list_index = 0; + local_list[list_index] = NULL; + + head = block; + while ((acl_get_first_name(head, &name, &next))) { + + if (list_index+2 > list_size) { + list_size += block_size; + char ** tmp_list = (char **) PERM_REALLOC(local_list, sizeof(char *) * list_size); + if ( tmp_list == NULL ) { + rv = ACLERRNOMEM; + goto list_cleanup; + } + local_list = tmp_list; + } + // local_list[list_index] = PERM_STRDUP(name); + local_list[list_index] = name; + if ( local_list[list_index] == NULL ) { + rv = ACLERRNOMEM; + goto list_cleanup; + } + list_index++; + local_list[list_index] = NULL; + head = next; + } + + rv = 0; + *name_list = local_list; + +list_cleanup: + if (local_list && rv<0) + ACL_NameListDestroy(errp, local_list); + if (file) + close_file_buf(file, filename, "rb", block); + + return rv; +} + +/* + * Changes method to method plus DBTYPE, and registers + * databases. + * + * Input: + * errp error stack + * acl_list Target ACL list handle + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_ListPostParseForAuth(NSErr_t *errp, ACLListHandle_t *acl_list ) +{ + ACLHandle_t *acl; + ACLWrapper_t *wrap; + ACLExprHandle_t *expr; + char *method; + char *database; + int rv; + ACLDbType_t *dbtype; + ACLMethod_t *methodtype; + + if ( acl_list == NULL ) + return(0); + + for ( wrap = acl_list->acl_list_head; wrap; wrap = wrap->wrap_next ) { + + acl = wrap->acl; + if ( acl == NULL ) + continue; + + for ( expr = acl->expr_list_head; expr; expr = expr->expr_next ) { + + if ( expr->expr_type != ACL_EXPR_TYPE_AUTH || + expr->expr_auth == NULL) + continue; + + rv = PListGetValue(expr->expr_auth, ACL_ATTR_METHOD_INDEX, + (void **) &method, NULL); + if ( rv >= 0 ) { + methodtype = (ACLMethod_t *)PERM_MALLOC(sizeof(ACLMethod_t)); + rv = ACL_MethodFind(errp, method, methodtype); + if (rv) { + nserrGenerate(errp, ACLERRUNDEF, ACLERR3800, ACL_Program, + 3, acl->tag, "method", method); + PERM_FREE(methodtype); + return(ACLERRUNDEF); + } + + rv = PListSetValue(expr->expr_auth, ACL_ATTR_METHOD_INDEX, + methodtype, NULL); + if ( rv < 0 ) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR3810, ACL_Program, + 0); + return(ACLERRNOMEM); + } + PERM_FREE(method); + } + + rv = PListGetValue(expr->expr_auth, ACL_ATTR_DATABASE_INDEX, + (void **) &database, NULL); + + if (rv < 0) continue; + + /* The following function lets user use databases which are + * not registered by their administrators. This also fixes + * the backward compatibility. + */ + dbtype = (ACLDbType_t *)PERM_MALLOC(sizeof(ACLDbType_t)); + rv = ACL_RegisterDbFromACL(errp, (const char *) database, + dbtype); + + if (rv < 0) { + nserrGenerate(errp, ACLERRUNDEF, ACLERR3800, ACL_Program, + 3, acl->tag, "database", database); + PERM_FREE(dbtype); + return(ACLERRUNDEF); + } + + rv = PListInitProp(expr->expr_auth, ACL_ATTR_DBTYPE_INDEX, ACL_ATTR_DBTYPE, + dbtype, NULL); + if ( rv < 0 ) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR3810, ACL_Program, + 0); + return(ACLERRNOMEM); + } + + } + + } + + return(0); + +} + +/* + * LOCAL FUNCTION + * + * Output Authorization Expression Rights "right, right" + */ + +static int +acl_decompose_expr_arg( acl_string_t *str_t, ACLExprHandle_t *expr ) +{ +int ii; + + if ( expr->expr_argc <= 0 ) { + return(ACLERRINTERNAL); + } + + if ( expr->expr_type == ACL_EXPR_TYPE_RESPONSE ) { + acl_to_str_append(str_t, expr->expr_argv[0]); + acl_to_str_append(str_t, " \""); + acl_to_str_append(str_t, expr->expr_argv[1]); + acl_to_str_append(str_t, "\";\n"); + return(0); + } + + for (ii = 0; ii < expr->expr_argc; ii++) { + acl_to_str_append(str_t, expr->expr_argv[ii]); + if ( ii < expr->expr_argc - 1 ) { + acl_to_str_append(str_t, ","); + } + } + acl_to_str_append(str_t, ";\n"); + + return(0); +} + +/* + * LOCAL FUNCTION + * + * Walks through the authentication statement PList_t and + * prints the structure to a string. + */ + +static void +acl_decompose_auth_expr(char *lval, const void *rval, void *user_data) +{ + acl_string_t * p_aclstr = (acl_string_t *) user_data; + // #### + + acl_to_str_append(p_aclstr, " "); + acl_to_str_append(p_aclstr, lval); + acl_to_str_append(p_aclstr, "=\""); + acl_to_str_append(p_aclstr, (char *) rval); + acl_to_str_append(p_aclstr, "\""); + + return; +} + +/* + * LOCAL FUNCTION + * + * Output the logic part of the authencation statement to a string. + */ + +static int +acl_decompose_auth_logic( acl_string_t * str_t, ACLExprHandle_t *expr) +{ + + if ( expr->expr_auth == NULL ) + return(0); + + acl_to_str_append(str_t, "exprs"); + PListEnumerate(expr->expr_auth, acl_decompose_auth_expr, (void *) str_t); + acl_to_str_append(str_t, ";\n"); + + return(0); +} + +/* + * LOCAL FUNCTION + * + * Output the logic part of the authorization statement to a string. + */ + +static int +acl_decompose_expr_logic( acl_string_t *str_t, ACLExprHandle_t *expr, ACLExprStack_t *expr_stack) +{ +int rv = 0; +int ii; + + expr_stack->stack_index = 0; + expr_stack->found_subexpression = 0; + expr_stack->last_subexpression = -1; + + for (ii = 0; ii < expr->expr_raw_index; ii++) { + rv = acl_reduce_expr_logic(expr_stack, &expr->expr_raw[ii]); + if (rv) break; + } + + if (!rv && expr_stack->expr_text[0]) { + acl_to_str_append(str_t, "exprs "); + acl_to_str_append(str_t, expr_stack->expr_text[0]); + acl_to_str_append(str_t, ";\n"); + PERM_FREE(expr_stack->expr_text[0]); + } + + return(rv); +} + +static int +acl_decompose(acl_string_t *str_t, ACLListHandle_t *acl_list) +{ +ACLWrapper_t *wrap; +ACLHandle_t *acl; +ACLExprHandle_t *expr; +int rv = 0; +ACLExprStack_t *expr_stack; + + expr_stack = (ACLExprStack_t *) PERM_MALLOC(sizeof(ACLExprStack_t)); + if ( expr_stack == NULL ) + return(ACLERRNOMEM); + + if ( acl_list->acl_list_head == NULL ) { + PERM_FREE(expr_stack); + return(0); + } + + acl_to_str_append(str_t, "version 3.0;"); + for (wrap = acl_list->acl_list_head; wrap && !rv; + wrap = wrap->wrap_next ) { + acl = wrap->acl; + if ( acl->tag ) { + acl_to_str_append(str_t, "\nname \""); + acl_to_str_append(str_t, acl->tag); + acl_to_str_append(str_t, "\";\n"); + } else { + acl_to_str_append(str_t, "\nname;\n"); + } + + for (expr = acl->expr_list_head; expr && rv == 0; + expr = expr->expr_next ) { + + switch (expr->expr_type) { + case ACL_EXPR_TYPE_DENY: + acl_to_str_append(str_t, "type deny;\nrights "); + if ( (rv = acl_decompose_expr_arg(str_t, expr)) < 0 ) + break; + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute true;\n"); + rv = acl_decompose_expr_logic(str_t, expr, expr_stack); + break; + case ACL_EXPR_TYPE_ALLOW: + acl_to_str_append(str_t, "type allow;\nrights "); + if ( (rv = acl_decompose_expr_arg(str_t, expr)) < 0 ) + break; + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute true;\n"); + rv = acl_decompose_expr_logic(str_t, expr, expr_stack); + break; + case ACL_EXPR_TYPE_AUTH: + acl_to_str_append(str_t, "type authenticate;\nattrs "); + if ( (rv = acl_decompose_expr_arg(str_t, expr)) < 0 ) + break; + if ( IS_ABSOLUTE(expr->expr_flags) ) + acl_to_str_append(str_t, "absolute true;\n"); + rv = acl_decompose_auth_logic(str_t, expr); + break; + case ACL_EXPR_TYPE_RESPONSE: + acl_to_str_append(str_t, "type response;\nattrs "); + rv = acl_decompose_expr_arg(str_t, expr); + break; + } + } + } + + PERM_FREE(expr_stack); + return(rv); +} + +/* + * Converts an ACLListHandle_t to a parameter list suitable for passing + * to the ACL UI. + * + * Input: + * errp error stack + * acl a pointer to a string, holds the result of the + * decomposition. + * acl_list Target ACL list handle + * Returns: + * 0 success + * < 0 failure + */ + +NSAPI_PUBLIC int +ACL_Decompose(NSErr_t *errp, char **acl, ACLListHandle_t *acl_list) +{ + int rv ; + acl_string_t aclstr={NULL,0,0}; + + if ( acl_list == NULL || acl == NULL ) + return(ACLERRUNDEF); + + rv = acl_decompose(&aclstr, acl_list); + *acl = aclstr.str; + + return ( rv ); +} + +/* + * The following routines are used to validate input parameters. They always + * return 1, or cause an NS_ASSERT failure. The proper way to use them is + * with an NS_ASSERT in the calling function. E.g. + * NS_ASSERT(ACL_AssertAcllist(acllist)); + */ + +int +ACL_AssertAcllist(ACLListHandle_t *acllist) +{ + ACLWrapper_t *wrap; + + if (acllist == ACL_LIST_NO_ACLS) return 1; + NS_ASSERT(acllist); + NS_ASSERT(acllist->acl_list_head); + NS_ASSERT(acllist->acl_list_tail); + NS_ASSERT(acllist->acl_count); + NS_ASSERT(acllist->ref_count > 0); + + for (wrap=acllist->acl_list_head; wrap; wrap=wrap->wrap_next) { + NS_ASSERT(ACL_AssertAcl(wrap->acl)); + } + + /* Artificially limit ACL lists to 10 ACLs for now */ + NS_ASSERT(acllist->acl_count < 10); + + return 1; +} + +int +ACL_AssertAcl(ACLHandle_t *acl) +{ + NS_ASSERT(acl); + NS_ASSERT(acl->ref_count); + NS_ASSERT(acl->expr_count); + NS_ASSERT(acl->expr_list_head); + NS_ASSERT(acl->expr_list_tail); + + return 1; +} + +static PList_t ACLAttr2IndexPList = NULL; + +int +ACL_InitAttr2Index(void) +{ + int i; + + if (ACLAttr2IndexPList) return 0; + + ACLAttr2IndexPList = PListNew(NULL); + for (i = 1; i < ACL_ATTR_INDEX_MAX; i++) { + PListInitProp(ACLAttr2IndexPList, NULL, ACLAttrTable[i], (const void *)i, NULL); + } + + return 0; +} + +/* + * Attempt to locate the index number for one of the known attribute names + * that are stored in plists. If we can't match it, just return 0. + */ +int +ACL_Attr2Index(const char *attrname) +{ + int index = 0; + + if ( ACLAttr2IndexPList ) { + PListFindValue(ACLAttr2IndexPList, attrname, (void **)&index, NULL); + if (index < 0) index = 0; + } + return index; +} diff --git a/lib/libaccess/aclutil.cpp b/lib/libaccess/aclutil.cpp new file mode 100644 index 00000000..8d30fffb --- /dev/null +++ b/lib/libaccess/aclutil.cpp @@ -0,0 +1,223 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Source file for the TimeOfDay and DayOfWeek LAS drivers + */ + +#include <netsite.h> +#include <base/crit.h> +/* #include <plhash.h> */ +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/las.h> +#include <libaccess/nserror.h> +#include "aclutil.h" + +/* Generic evaluator of comparison operators in attribute evaluation + * statements. + * INPUT + * CmpOp_t ACL_TOKEN_EQ, ACL_TOKEN_NE etc. + * result 0 if equal, >0 if real > pattern, <0 if + * real < pattern. + * RETURNS + * LAS_EVAL_TRUE or LAS_EVAL_FALSE or LAS_EVAL_INVALID + * DEBUG + * Can add asserts that the strcmp failure cases are one of the + * remaining legal comparators. + */ +int +evalComparator(CmpOp_t ctok, int result) +{ + if (result == 0) { + switch(ctok) { + case CMP_OP_EQ: + case CMP_OP_GE: + case CMP_OP_LE: + return LAS_EVAL_TRUE; + case CMP_OP_NE: + case CMP_OP_GT: + case CMP_OP_LT: + return LAS_EVAL_FALSE; + default: + return LAS_EVAL_INVALID; + } + } else if (result > 0) { + switch(ctok) { + case CMP_OP_GT: + case CMP_OP_GE: + case CMP_OP_NE: + return LAS_EVAL_TRUE; + case CMP_OP_LT: + case CMP_OP_LE: + case CMP_OP_EQ: + return LAS_EVAL_FALSE; + default: + return LAS_EVAL_INVALID; + } + } else { /* real < pattern */ + switch(ctok) { + case CMP_OP_LT: + case CMP_OP_LE: + case CMP_OP_NE: + return LAS_EVAL_TRUE; + case CMP_OP_GT: + case CMP_OP_GE: + case CMP_OP_EQ: + return LAS_EVAL_FALSE; + default: + return LAS_EVAL_INVALID; + } + } +} + + +/* Takes a string and returns the same string with all uppercase +* letters converted to lowercase. +*/ +void +makelower(char *string) +{ + while (*string) { + *string = tolower(*string); + string++; + } +} + + +/* Given an LAS_EVAL_* value, translates to ACL_RES_* */ +int +EvalToRes(int value) +{ + switch (value) { + case LAS_EVAL_TRUE: + return ACL_RES_ALLOW; + case LAS_EVAL_FALSE: + return ACL_RES_DENY; + case LAS_EVAL_DECLINE: + return ACL_RES_FAIL; + case LAS_EVAL_FAIL: + return ACL_RES_FAIL; + case LAS_EVAL_INVALID: + return ACL_RES_INVALID; + case LAS_EVAL_NEED_MORE_INFO: + return ACL_RES_DENY; + default: + NS_ASSERT(1); + return ACL_RES_ERROR; + } +} + +const char *comparator_string (int comparator) +{ + static char invalid_cmp[32]; + + switch(comparator) { + case CMP_OP_EQ: return "CMP_OP_EQ"; + case CMP_OP_NE: return "CMP_OP_NE"; + case CMP_OP_GT: return "CMP_OP_GT"; + case CMP_OP_LT: return "CMP_OP_LT"; + case CMP_OP_GE: return "CMP_OP_GE"; + case CMP_OP_LE: return "CMP_OP_LE"; + default: + sprintf(invalid_cmp, "unknown comparator %d", comparator); + return invalid_cmp; + } +} + +/* Return the pointer to the next token after replacing the following 'delim' + * char with NULL. + * WARNING - Modifies the first parameter */ +char *acl_next_token (char **ptr, char delim) +{ + char *str = *ptr; + char *token = str; + char *comma; + + if (!token) { *ptr = 0; return 0; } + + /* ignore leading whitespace */ + while(*token && isspace(*token)) token++; + + if (!*token) { *ptr = 0; return 0; } + + if ((comma = strchr(token, delim)) != NULL) { + *comma++ = 0; + } + + { + /* ignore trailing whitespace */ + int len = strlen(token); + char *sptr = token+len-1; + + while(*sptr == ' ' || *sptr == '\t') *sptr-- = 0; + } + + *ptr = comma; + return token; +} + + +/* Returns a pointer to the next token and it's length */ +/* tokens are separated by 'delim' characters */ +/* ignores whitespace surrounding the tokens */ +const char *acl_next_token_len (const char *ptr, char delim, int *len) +{ + const char *str = ptr; + const char *token = str; + const char *comma; + + *len = 0; + + if (!token) { return 0; } + + /* ignore leading whitespace */ + while(*token && isspace(*token)) token++; + + if (!*token) { return 0; } + if (*token == delim) { return token; } /* str starts with delim! */ + + if ((comma = strchr(token, delim)) != NULL) { + *len = comma - token; + } + else { + *len = strlen(token); + } + + { + /* ignore trailing whitespace */ + const char *sptr = token + *len - 1; + + while(*sptr == ' ' || *sptr == '\t') { + sptr--; + (*len)--; + } + } + + return token; +} + +/* acl_get_req_time -- + * If the REQ_TIME is available on the 'resource' plist, return it. + * Otherwise, make a system call to get the time and insert the time on the + * 'resource' PList. Allocate the time_t structure using the 'resource' + * PList's pool. + */ +time_t *acl_get_req_time (PList_t resource) +{ + time_t *req_time = 0; + int rv = PListGetValue(resource, ACL_ATTR_TIME_INDEX, (void **)&req_time, + NULL); + + if (rv < 0) { + req_time = (time_t *)pool_malloc(PListGetPool(resource), sizeof(time_t)); + time(req_time); + PListInitProp(resource, ACL_ATTR_TIME_INDEX, ACL_ATTR_TIME, + (void *)req_time, NULL); + } + + return req_time; +} diff --git a/lib/libaccess/aclutil.h b/lib/libaccess/aclutil.h new file mode 100644 index 00000000..34a1d0a6 --- /dev/null +++ b/lib/libaccess/aclutil.h @@ -0,0 +1,25 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef ACLUTIL_H +#define ACLUTIL_H + +NSPR_BEGIN_EXTERN_C + +int evalComparator(CmpOp_t ctok, int result); +void makelower(char *string); +int EvalToRes(int value); +const char *comparator_string (int comparator); + +/*Warning: acl_next_token modifies 'ptr' */ +char *acl_next_token (char **ptr, char delim); + +const char *acl_next_token_len (const char *ptr, + char delim, int *len); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/attrec.cpp b/lib/libaccess/attrec.cpp new file mode 100644 index 00000000..8911e896 --- /dev/null +++ b/lib/libaccess/attrec.cpp @@ -0,0 +1,309 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (attrec.c) + * + * This module contains routines for encoding and decoding + * attribute records. See attrec.h for a description of attribute + * records. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#define __PRIVATE_ATTREC +#include "libaccess/attrec.h" + +/* + * Description (NTS_Length) + * + * This function returns the length of a null-terminated string. + * The length includes the terminating null octet. + * + * Use of the NTSLENGTH() macro is recommended (see attrec.h). + * + * Arguments: + * + * nts - a pointer to the null-terminate string + * (may be null) + * + * Returns: + * + * The length of the string. If 'nts' is null, the value is one, + * since there is always a null octet. + */ + +int NTS_Length(NTS_t nts) +{ + return ((nts) ? strlen((const char *)nts) + 1 : 1); +} + +/* + * Description (NTS_Decode) + * + * This function decodes a null-terminated string from a specified + * attribute record buffer. It copies the string into a dynamically + * allocated buffer, if 'pnts' is not null, and returns a pointer + * to it. The return value of the function is a pointer to the + * octet following the NTS in the attribute record buffer. + * + * Use of the NTSDECODE() macro is recommended (see attrec.h). + * + * Arguments: + * + * cp - pointer into the attribute record buffer + * pnts - pointer to returned reference to decoded + * NTS, or null, if the decoded NTS is not + * to be copied to a dynamic buffer + * + * Returns: + * + * The function return value is a pointer to the octet following + * the NTS in the attribute record buffer. A pointer to a + * dynamically allocated buffer containing the decoded NTS will + * be returned through 'pnts', if it is non-null. This returned + * pointer will be null if the NTS contains only a terminating + * octet. + */ + +ATR_t NTS_Decode(ATR_t cp, NTS_t * pnts) +{ + NTS_t nts = 0; + int len = NTSLENGTH(cp); /* length of the string */ + + /* Are we going to return a copy of the string? */ + if (pnts) { + + /* Yes, is it more than just a null octet? */ + if (len > 1) { + + /* Yes, allocate a buffer and copy the string to it */ + nts = (NTS_t)MALLOC(len); + if (nts) { + memcpy((void *)nts, (void *)cp, len); + } + } + + /* Return a pointer to the copied string, or null */ + *pnts = nts; + } + + /* Return pointer to octet after string */ + return cp + len; +} + +/* + * Description (NTS_Encode) + * + * This function encodes a null-terminated string into a specified + * attribute record buffer. It returns a pointer to the octet + * following the encoding. + * + * Use of the NTSENCODE() macro is recommended (see attrec.h). + * + * Arguments: + * + * cp - pointer into the attribute record buffer + * nts - pointer to the string to be encoded + * + * Returns: + * + * A pointer to the octet following the encoding in the attribute + * record buffer is returned. + */ + +ATR_t NTS_Encode(ATR_t cp, NTS_t nts) +{ + + /* Is the string pointer null? */ + if (nts) { + int len = NTSLENGTH(nts); + + /* No, copy the string to the attribute record buffer */ + memcpy((void *)cp, (void *)nts, len); + + /* Get pointer to octet after it */ + cp += len; + } + else { + + /* A null pointer indicates an empty NTS, i.e. just a null octet */ + *cp++ = 0; + } + + /* Return a pointer to the octet after the encoding */ + return cp; +} + +/* + * Description (USI_Decode) + * + * This function decodes an unsigned integer value from a specified + * attribute record buffer. + * + * Use of the USIDECODE() macro is recommended (see attrec.h). + * + * Arguments: + * + * cp - pointer into the attribute record buffer + * pval - pointer to returned integer value + * + * Returns: + * + * If 'pval' is not null, the decoded integer value is returned + * in the referenced location. The function return value is a + * pointer to the octet following the USI encoding in the attribute + * record buffer. + */ + +ATR_t USI_Decode(ATR_t cp, USI_t * pval) +{ + int val; + + /* Is this a length value? */ + if (*(cp) & 0x80) { + int i; + int len; + + /* Yes, build the value from the indicated number of octets */ + len = *cp++ & 0x7; + val = 0; + for (i = 0; i < len; ++i) { + val <<= 8; + val |= (cp[i] & 0xff); + } + cp += len; + } + else { + + /* This octet is the value */ + val = *cp++; + } + + /* Return the value if there's a place to put it */ + if (pval) *pval = val; + + /* Return a pointer to the next item in the attribute record */ + return cp; +} + +/* + * Description (USI_Encode) + * + * This function encodes an unsigned integer value into a specified + * attribute record buffer. + * + * Use of the USIENCODE() macro is recommended (see attrec.h). + * + * Arguments: + * + * cp - pointer into the attribute record buffer + * val - the value to be encoded + * + * Returns: + * + * A pointer to the octet following the generated encoding in the + * attribute record buffer is returned. + */ + +ATR_t USI_Encode(ATR_t cp, USI_t val) +{ + /* Check size of value to be encoded */ + if (val <= 0x7f) *cp++ = val; + else if (val <= 0xff) { + /* Length plus 8-bit value */ + *cp++ = 0x81; + *cp++ = val; + } + else if (val <= 0xffff) { + /* Length plus 16-bit value */ + *cp++ = 0x82; + cp[1] = val & 0xff; + val >>= 8; + cp[0] = val & 0xff; + cp += 2; + } + else if (val <= 0xffffff) { + /* Length plus 24-bit value */ + *cp++ = 0x83; + cp[2] = val & 0xff; + val >>= 8; + cp[1] = val & 0xff; + val >>= 8; + cp[0] = val & 0xff; + cp += 3; + } + else { + /* Length plus 32-bit value */ + *cp++ = 0x84; + cp[3] = val & 0xff; + val >>= 8; + cp[2] = val & 0xff; + val >>= 8; + cp[1] = val & 0xff; + val >>= 8; + cp[0] = val & 0xff; + cp += 4; + } + + /* Return a pointer to the next position in the attribute record */ + return cp; +} + +/* + * Description (USI_Insert) + * + * This function is a variation of USI_Encode() that always generates + * the maximum-length encoding for USI value, regardless of the + * actual specified value. For arguments, returns, see USI_Encode(). + * + * Use of the USIINSERT() macro is recommended. The USIALLOC() macro + * returns the number of octets that USIINSERT() will generate. + */ + +ATR_t USI_Insert(ATR_t cp, USI_t val) +{ + int i; + + assert(USIALLOC() == 5); + + *cp++ = 0x84; + for (i = 3; i >= 0; --i) { + cp[i] = val & 0xff; + val >>= 8; + } + + return cp + 5; +} + +/* + * Description (USI_Length) + * + * This function returns the number of octets required to encode + * an unsigned integer value. + * + * Use of the USILENGTH() macro is recommended (see attrec.h). + * + * Arguments: + * + * val - the unsigned integer value + * + * Returns: + * + * The number of octets required to encode the specified value is + * returned. + */ + +int USI_Length(USI_t val) +{ + return (((USI_t)(val) <= 0x7f) ? 1 + : (((USI_t)(val) <= 0xff) ? 2 + : (((USI_t)(val) <= 0xffff) ? 3 + : (((USI_t)(val) <= 0xffffff) ? 4 + : 5)))); +} + diff --git a/lib/libaccess/authdb.cpp b/lib/libaccess/authdb.cpp new file mode 100644 index 00000000..0d27635d --- /dev/null +++ b/lib/libaccess/authdb.cpp @@ -0,0 +1,339 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <stdio.h> +#include <string.h> + +#include <plhash.h> + +#include <netsite.h> +#include "permhash.h" +#include <ldaputil/errors.h> +#include <ldaputil/certmap.h> +#include <ldaputil/dbconf.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/authdb.h> +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include <libaccess/acl.h> +#include <libaccess/aclglobal.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +#define BIG_LINE 1024 + +char *ACL_default_dbname = 0; +ACLDbType_t ACL_default_dbtype = ACL_DBTYPE_INVALID; +ACLMethod_t ACL_default_method = ACL_METHOD_INVALID; +int acl_registered_dbcnt = 0; + +extern int acl_registered_names(PLHashTable *ht, int count, char ***names); + +/************************** Database Types *************************/ + +#define databaseNamesHashTable ACLDbNameHash + +int acl_num_databases () +{ + return acl_registered_dbcnt; +} + +static int reg_dbname_internal (NSErr_t *errp, ACLDbType_t dbtype, + const char *dbname, const char *url, + PList_t plist) +{ + DbParseFn_t parseFunc; + void *db; + int rv; + AuthdbInfo_t *authdb_info; + + if (!ACL_DbTypeIsRegistered(errp, dbtype)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4400, ACL_Program, 2, XP_GetAdminStr(DBT_DbtypeNotDefinedYet), dbname); + return -1; + } + + parseFunc = ACL_DbTypeParseFn(errp, dbtype); + + if (!parseFunc) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4400, ACL_Program, 2, XP_GetAdminStr(DBT_DbtypeNotDefinedYet), dbname); + return -1; + } + + rv = (*parseFunc)(errp, dbtype, dbname, url, plist, (void **)&db); + + if (rv < 0) { + /* plist contains error message/code */ + return rv; + } + + /* Store the db returned by the parse function in the hash table. + */ + + authdb_info = (AuthdbInfo_t *)pool_malloc(ACL_DATABASE_POOL, sizeof(AuthdbInfo_t)); + + if (!authdb_info) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4420, ACL_Program, 0); + return -1; + } + + authdb_info->dbname = pool_strdup(ACL_DATABASE_POOL, dbname); + authdb_info->dbtype = dbtype; + authdb_info->dbinfo = db; /* value returned from parseFunc */ + + PR_HashTableAdd(ACLDbNameHash, authdb_info->dbname, authdb_info); + acl_registered_dbcnt++; + + return 0; +} + +NSAPI_PUBLIC int ACL_DatabaseRegister (NSErr_t *errp, ACLDbType_t dbtype, + const char *dbname, const char *url, + PList_t plist) +{ + if (!dbname || !*dbname) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4500, ACL_Program, 1, XP_GetAdminStr(DBT_DatabaseRegisterDatabaseNameMissing)); + return -1; + } + + return reg_dbname_internal(errp, dbtype, dbname, url, plist); +} + +NSAPI_PUBLIC int +ACL_DatabaseNamesGet(NSErr_t *errp, char ***names, int *count) +{ + *count = acl_registered_dbcnt; + return acl_registered_names (ACLDbNameHash, *count, names); +} + +NSAPI_PUBLIC int +ACL_DatabaseNamesFree(NSErr_t *errp, char **names, int count) +{ + int i; + + for (i = count-1; i; i--) FREE(names[i]); + + FREE(names); + return 0; +} + +/* try to determine the dbtype from the database url */ +static int acl_url_to_dbtype (const char *url, ACLDbType_t *dbtype_out) +{ + ACLDbType_t dbtype; + NSErr_t *errp = 0; + + *dbtype_out = dbtype = ACL_DBTYPE_INVALID; + if (!url || !*url) return -1; + + // urls with ldap:, ldaps: and ldapdb: are all of type ACL_DBTYPE_LDAP. + if (!strncmp(url, URL_PREFIX_LDAP, URL_PREFIX_LDAP_LEN)) + dbtype = ACL_DbTypeLdap; + else { + /* treat prefix in the url as dbtype if it has been registered. + */ + int prefix_len = strcspn(url, ":"); + char dbtypestr[BIG_LINE]; + + if (prefix_len) { + strncpy(dbtypestr, url, prefix_len); + dbtypestr[prefix_len] = 0; + + if (!ACL_DbTypeFind(errp, dbtypestr, &dbtype)) { + /* prefix is not a registered dbtype */ + dbtype = ACL_DBTYPE_INVALID; + } + } + } + + if (ACL_DbTypeIsEqual(errp, dbtype, ACL_DBTYPE_INVALID)) { + /* try all the registered parse functions to determine the dbtype */ + } + + if (ACL_DbTypeIsEqual(errp, dbtype, ACL_DBTYPE_INVALID)) return -1; + + *dbtype_out = dbtype; + return 0; +} + +NSAPI_PUBLIC int ACL_RegisterDbFromACL (NSErr_t *errp, const char *url, + ACLDbType_t *dbtype) +{ + /* If the database by name url is already registered, don't do anything. + * If it is not registered, determine the dbtype from the url. + * If the dbtype can be determined, register the database with dbname same + * as the url. Return the dbtype. + */ + void *db; + int rv; + PList_t plist; + + if (ACL_DatabaseFind(errp, url, dbtype, &db) == LAS_EVAL_TRUE) + return 0; + + /* The database is not registered yet. Parse the url to find out its + * type. If parsing fails, return failure. + */ + rv = acl_url_to_dbtype(url, dbtype); + + if (rv < 0) { + return rv; + } + + plist = PListNew(NULL); + rv = ACL_DatabaseRegister(errp, *dbtype, url, url, plist); + PListDestroy(plist); + return rv; +} + +NSAPI_PUBLIC int ACL_DatabaseFind(NSErr_t *errp, const char *name, + ACLDbType_t *dbtype, void **db) +{ + AuthdbInfo_t *info; + + *dbtype = ACL_DBTYPE_INVALID; + *db = 0; + + if (ACLDbNameHash) { + info = (AuthdbInfo_t *)PR_HashTableLookup(ACLDbNameHash, +#ifdef NSPR20 + name +#else + (char *)name +#endif + ); + + if (info) { + *dbtype = info->dbtype; + *db = info->dbinfo; + + return LAS_EVAL_TRUE; + } + } + + return LAS_EVAL_FAIL; +} + + +NSAPI_PUBLIC int ACL_ReadDbMapFile (NSErr_t *errp, const char *map_file, + int default_only) +{ + DBConfInfo_t *info; + DBConfDBInfo_t *db_info; + DBPropVal_t *propval; + PList_t plist; + int rv; + int seen_default = 0; + + if (default_only) + rv = dbconf_read_default_dbinfo(map_file, &db_info); + else + rv = dbconf_read_config_file(map_file, &info); + + if (rv != LDAPU_SUCCESS) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4600, ACL_Program, 3, XP_GetAdminStr(DBT_ReadDbMapFileErrorReadingFile), map_file, ldapu_err2string(rv)); + return -1; + } + + rv = 0; + + if (!default_only) + db_info = info->firstdb; + + while(db_info) { + char *url = db_info->url; + char *dbname = db_info->dbname; + ACLDbType_t dbtype; + + /* process db_info */ + if (url) { + rv = acl_url_to_dbtype(url, &dbtype); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4610, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileCouldntDetermineDbtype), url); + break; + } + } + else { + nserrGenerate(errp, ACLERRFAIL, ACLERR4620, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileMissingUrl), dbname); + rv = -1; + break; + } + + /* convert any property-value pairs in db_info into plist */ + plist = PListNew(NULL); + propval = db_info->firstprop; + + while(propval) { + if (propval->prop) { + PListInitProp(plist, 0, propval->prop, propval->val, 0); + } + else { + nserrGenerate(errp, ACLERRINVAL, ACLERR4630, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileInvalidPropertyPair), dbname); + rv = -1; + break; + } + propval = propval->next; + } + + if (rv < 0) break; + + /* register the database */ + rv = ACL_DatabaseRegister(errp, dbtype, dbname, url, plist); + PListDestroy(plist); + + if (rv < 0) { + /* Failed to register database */ + nserrGenerate(errp, ACLERRFAIL, ACLERR4640, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileRegisterDatabaseFailed), dbname); + break; + } + + /* If the dbname is "default", set the default_dbtype */ + if (!strcmp(dbname, DBCONF_DEFAULT_DBNAME)) { + if (!ACL_DbTypeIsEqual(errp, dbtype, ACL_DbTypeLdap)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4350, ACL_Program, 1, + XP_GetAdminStr(DBT_ReadDbMapFileDefaultDatabaseNotLdap)); + rv = -1; + break; + } + if (seen_default) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4360, ACL_Program, 1, XP_GetAdminStr(DBT_ReadDbMapFileMultipleDefaultDatabases)); + rv = -1; + break; + } + seen_default = 1; + ACL_DatabaseSetDefault(errp, dbname); + } + + db_info = db_info->next; + } + + if (!seen_default) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4370, ACL_Program, 1, XP_GetAdminStr(DBT_ReadDbMapFileMissingDefaultDatabase)); + rv = -1; + } + + if (default_only) + dbconf_free_dbinfo(db_info); + else + dbconf_free_confinfo(info); + + return rv; +} + +void +ACL_DatabaseDestroy(void) +{ + pool_destroy(ACL_DATABASE_POOL); + ACL_DATABASE_POOL = NULL; + ACLDbNameHash = NULL; + return; +} + diff --git a/lib/libaccess/avadb.c b/lib/libaccess/avadb.c new file mode 100644 index 00000000..c0d98704 --- /dev/null +++ b/lib/libaccess/avadb.c @@ -0,0 +1,300 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libaccess/ava.h" +#include "libaccess/avadb.h" +#include "base/session.h" +#include "base/pblock.h" +#include "frame/req.h" +#include "frame/log.h" + +#include "libadmin/libadmin.h" +#include "libaccess/avapfile.h" + +#define DB_NAME "AvaMap" + +enum {AVA_DB_SUCCESS=0,AVA_DB_FAILURE}; + +#ifdef XP_UNIX +#include "mcom_ndbm.h" + +USE_NSAPI int AddEntry (char *key, char *value) { + datum keyd; + datum valued; + DBM *db = NULL; + char dbpath[150]; + + sprintf (dbpath, "%s%c%s", get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + db = dbm_open (dbpath, O_RDWR | O_CREAT, 0644); + + if (!db) + return AVA_DB_FAILURE; + + keyd.dptr = key; + keyd.dsize = strlen (key) + 1; + + valued.dptr = value; + valued.dsize = strlen(value) + 1; + + dbm_store (db, keyd, valued, DBM_REPLACE); + dbm_close (db); + + return AVA_DB_SUCCESS; +} + +USE_NSAPI int DeleteEntry (char *key) { + datum keyd; + DBM *db = NULL; + char dbpath[150]; + + sprintf (dbpath, "%s%c%s", get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + db = dbm_open (dbpath, O_RDWR, 0644); + + if (!db) + return AVA_DB_FAILURE; + + keyd.dptr = key; + keyd.dsize = strlen (key) + 1; + + dbm_delete (db, keyd); + + dbm_close (db); + + return AVA_DB_SUCCESS; +} + +USE_NSAPI char *GetValue (char *key) { + datum keyd; + datum valued; + DBM *db = NULL; + char dbpath[150]; + + sprintf (dbpath, "%s%c%s", get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + db = dbm_open (dbpath, O_RDONLY, 0644); + + if (!db) + return NULL; + + keyd.dptr = key; + keyd.dsize = strlen (key) + 1; + + valued = dbm_fetch (db, keyd); + + dbm_close (db); + + return valued.dptr; +} + +#else + +#include <stdio.h> + + +#define lmemcpy memcpy +#define lmemcmp memcmp +#define lmemset memset + +static int mkhash8(char *x,int len) { + unsigned int i,hash = 0; + for (i=0; i < len; i++) { hash += x[i]; } + + return (int) (hash & 0xff); +} + +static void mkpath(char *target, char *dir, char sep, char *name) { + int len; + + len = strlen(dir); + lmemcpy(target,dir,len); + target += len; + + *target++ = sep; + + len = strlen(name); + lmemcpy(target,name,len); + target += len; + + *target = 0; +} + +#define DELETED_LEN 8 +static char DELETED[] = { 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff , 0x0 }; + + +#define RECORD_SIZE 512 +USE_NSAPI int AddEntry (char *key, char *value) { + int empty, hash; + char dbpath[150]; + char record[RECORD_SIZE]; + int key_len, val_len,size; + FILE *f; + + mkpath (dbpath, get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + f = fopen(dbpath, "rb+"); + if (f == NULL) { + f = fopen(dbpath,"wb+"); + } + + if (f == NULL) + return AVA_DB_FAILURE; + + key_len = strlen(key)+1; + val_len = strlen(value); + + if ((key_len+val_len) > RECORD_SIZE) { + fclose(f); + return AVA_DB_FAILURE; + } + + + /* now hash the key */ + hash = mkhash8(key,key_len); + empty = -1; + + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + for (;;) { + size= fread(record,1,RECORD_SIZE,f); + if (size < RECORD_SIZE) { + break; + } + if (lmemcmp(record,key,key_len) == 0) { + break; + } + if ((empty == -1) && (lmemcmp(record,DELETED,DELETED_LEN) == 0)) { + empty = hash; + } + if (record == 0) { + break; + } + hash++; + } + + if (empty != -1) { hash = empty; } + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + /* build the record */ + lmemset(record,0,RECORD_SIZE); + + lmemcpy(record,key,key_len); + lmemcpy(&record[key_len],value,val_len); + size= fwrite(record,1,RECORD_SIZE,f); + if (size != RECORD_SIZE) { + fclose(f); + return AVA_DB_FAILURE; + } + fclose(f); + + return AVA_DB_SUCCESS; +} + +USE_NSAPI int DeleteEntry (char *key) { + int found,hash; + char dbpath[150]; + char record[RECORD_SIZE]; + int key_len,size; + FILE *f; + + mkpath (dbpath, get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + f = fopen(dbpath, "rb+"); + + if (f == NULL) + return AVA_DB_FAILURE; + + key_len = strlen(key)+1; + + + /* now hash the key */ + hash = mkhash8(key,key_len); + found = 0; + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + for (;;) { + size= fread(record,1,RECORD_SIZE,f); + if (size < RECORD_SIZE) { + break; + } + if (lmemcmp(record,key,key_len) == 0) { + found++; + break; + } + if (record == 0) { + break; + } + hash++; + } + + if (!found) { + fclose(f); + return AVA_DB_SUCCESS; + } + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + /* build the record */ + lmemset(record,0,RECORD_SIZE); + + lmemcpy(record,DELETED,DELETED_LEN); + size= fwrite(record,1,RECORD_SIZE,f); + if (size != RECORD_SIZE) { + fclose(f); + return AVA_DB_FAILURE; + } + fclose(f); + + return AVA_DB_SUCCESS; +} + +USE_NSAPI char *GetValue (char *key) { + int hash,size; + char dbpath[150]; + char record[RECORD_SIZE]; + int key_len,found = 0; + FILE *f; + + mkpath (dbpath, get_httpacl_dir(), FILE_PATHSEP, DB_NAME); + + f = fopen(dbpath, "rb"); + + if (f == NULL) + return NULL; + + key_len = strlen(key)+1; + + /* now hash the key */ + hash = mkhash8(key,key_len); + + fseek(f,hash*RECORD_SIZE,SEEK_SET); + + for(;;) { + size= fread(record,1,RECORD_SIZE,f); + if (size < RECORD_SIZE) { + break; + } + if (lmemcmp(record,key,key_len) == 0) { + found++; + break; + } + if (record == 0) { + break; + } + hash++; + } + + fclose(f); + if (!found) return NULL; + + return system_strdup(&record[key_len+1]); +} + +#endif diff --git a/lib/libaccess/avaparse.y b/lib/libaccess/avaparse.y new file mode 100644 index 00000000..c28ef165 --- /dev/null +++ b/lib/libaccess/avaparse.y @@ -0,0 +1,140 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +%{ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "libaccess/ava.h" +#include "libaccess/avapfile.h" +#include "netsite.h" + +extern int linenum; +extern char yytext[]; + +static void AddDefType (int defType, char *defId); +static void AddAVA (char* userID); + +void yyerror(const char* string); +extern void logerror(const char* string,int num, char *file); + +AVAEntry tempEntry; +AVATable entryTable; + +%} + +%union { + char *string; + int num; +} + +%token DEF_C DEF_CO DEF_OU DEF_CN EQ_SIGN DEF_START +%token DEF_L DEF_E DEF_ST +%token <string> USER_ID DEF_ID + +%type <num> def.type + +%start source + +%% + +source: ava.database + | + ; + + +ava.database: ava.database ava + | ava + ; + +ava: USER_ID definitions {AddAVA($1);}; + +definitions: definition.list + | + ; + +definition.list: definition.list definition + | definition + ; + + +definition: def.type EQ_SIGN DEF_ID {AddDefType($1, $3);}; + +def.type: DEF_C {$$ = DEF_C; } + | DEF_CO {$$ = DEF_CO;} + | DEF_OU {$$ = DEF_OU;} + | DEF_CN {$$ = DEF_CN;} + | DEF_L {$$ = DEF_L; } + | DEF_E {$$ = DEF_E; } + | DEF_ST {$$ = DEF_ST;} + ; + +%% + +void yyerror(const char* string) { + logerror(string,linenum,currFile); +} + + +void AddDefType (int defType, char *defId) { + switch (defType) { + case DEF_C: + tempEntry.country = defId; + break; + case DEF_CO: + tempEntry.company = defId; + break; + case DEF_OU: + if (tempEntry.numOrgs % ORGS_ALLOCSIZE == 0) { + if (tempEntry.numOrgs == 0) { + tempEntry.organizations = + PERM_MALLOC (sizeof (char*) * ORGS_ALLOCSIZE); + } else { + char **temp; + temp = + PERM_MALLOC(sizeof(char*) * (tempEntry.numOrgs + ORGS_ALLOCSIZE)); + memcpy (temp, tempEntry.organizations, + sizeof(char*)*tempEntry.numOrgs); + PERM_FREE (tempEntry.organizations); + tempEntry.organizations = temp; + } + } + tempEntry.organizations[tempEntry.numOrgs++] = defId; + break; + case DEF_CN: + tempEntry.CNEntry = defId; + break; + case DEF_E: + tempEntry.email = defId; + break; + case DEF_L: + tempEntry.locality = defId; + break; + case DEF_ST: + tempEntry.state = defId; + break; + default: + break; + } +} + +void AddAVA (char* userID) { + AVAEntry *newAVA; + + newAVA = (AVAEntry*)PERM_MALLOC(sizeof(AVAEntry)); + if (!newAVA) { + yyerror ("Out of Memory in AddAVA"); + return; + } + *newAVA = tempEntry; + newAVA->userid = userID; + + _addAVAtoTable (newAVA, &entryTable); + + tempEntry.CNEntry = tempEntry.userid = tempEntry.country = tempEntry.company = 0; + tempEntry.email = tempEntry.locality = tempEntry.state = NULL; + tempEntry.numOrgs = 0; +} diff --git a/lib/libaccess/avapfile.c b/lib/libaccess/avapfile.c new file mode 100644 index 00000000..8c955d9d --- /dev/null +++ b/lib/libaccess/avapfile.c @@ -0,0 +1,428 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libaccess/ava.h" + +#include "base/session.h" +#include "base/pblock.h" +#include "frame/req.h" +#include "frame/log.h" + +#include "libadmin/libadmin.h" +#include "libaccess/avapfile.h" + +#define ALLOC_SIZE 20 +#define SUCCESS 0 + +struct parsedStruct { + char *fileName; + AVATable *avaTable; +}; + +typedef struct parsedStruct Parsed; + +/* globals for yy_error if needed */ +Session *yy_sn = NULL; +Request *yy_rq = NULL; + +/*This will be a dynamic array of parsedStruct*. Re-sizing if necessary.*/ +struct ParsedTable { + Parsed **parsedTable; + int numEntries; +}; + +char *currFile; + +static struct ParsedTable parsedFiles = {NULL, 0}; + +extern AVATable entryTable; /*Table where entries are stored*/ +extern AVAEntry tempEntry; /*Used to restore parser's state*/ +extern linenum; + +AVAEntry * AVAEntry_Dup(AVAEntry *entry) { + int i; + AVAEntry *newAVA = NULL; +/* copy the AVA entry */ + + if (entry) { + newAVA = (AVAEntry *) PERM_MALLOC(sizeof(AVAEntry)); + memset(newAVA,0, sizeof(AVAEntry)); + newAVA->userid = 0; + newAVA->CNEntry = 0; + newAVA->email = 0; + newAVA->locality = 0; + newAVA->state = 0; + newAVA->country = 0; + newAVA->company = 0; + newAVA->organizations = 0; + newAVA->numOrgs = 0; + if (entry->userid) newAVA->userid = PERM_STRDUP(entry->userid); + if (entry->CNEntry) newAVA->CNEntry = PERM_STRDUP(entry->CNEntry); + if (entry->email) newAVA->email = PERM_STRDUP(entry->email); + if (entry->locality) newAVA->locality = PERM_STRDUP(entry->locality); + if (entry->state) newAVA->state = PERM_STRDUP(entry->state); + if (entry->country) newAVA->country = PERM_STRDUP(entry->country); + if (entry->company) newAVA->company = PERM_STRDUP(entry->company); + if (entry->organizations) { + newAVA->organizations = PERM_MALLOC(sizeof(char *)*entry->numOrgs); + newAVA->numOrgs = entry->numOrgs; + for (i=0; i<entry->numOrgs; i++) + newAVA->organizations[i] = PERM_STRDUP (entry->organizations[i]); + } + } + return newAVA; +} + +void _addAVAtoTable (AVAEntry *newAVA, AVATable *table) { + int i; + int insertIndex = -1; + + if (table->numEntries%ENTRIES_ALLOCSIZE == 0) { + if (table->numEntries == 0) { + table->enteredTable = + (AVAEntry**) PERM_MALLOC (sizeof(AVAEntry*) * ENTRIES_ALLOCSIZE); + } else { + AVAEntry **temp; + + temp = + PERM_MALLOC(sizeof(AVAEntry*)*(table->numEntries+ENTRIES_ALLOCSIZE)); + memmove(temp, table->enteredTable, sizeof(AVAEntry*)*table->numEntries); + PERM_FREE(table->enteredTable); + table->enteredTable = temp; + } + } + + for (i=table->numEntries-1; i >= 0; i--) { + if (strcmp(newAVA->userid, table->enteredTable[i]->userid) > 0) { + insertIndex = i+1; + break; + } else { + table->enteredTable[i+1] = table->enteredTable[i]; + } + } + + + table->enteredTable[(insertIndex == -1) ? 0 : insertIndex] = newAVA; + (table->numEntries)++; +} + +AVATable *AVATableDup(AVATable *table) { + AVATable *newTable = (AVATable*)PERM_MALLOC (sizeof(AVATable)); + /* round the puppy so _addAVAtoTable still works */ + int size = (table->numEntries + (ENTRIES_ALLOCSIZE-1))/ENTRIES_ALLOCSIZE; + int i; + + newTable->enteredTable = + (AVAEntry**)PERM_MALLOC(size*ENTRIES_ALLOCSIZE*sizeof(AVAEntry *)); + + for (i=0; i < table->numEntries; i++) { + newTable->enteredTable[i] = AVAEntry_Dup(table->enteredTable[i]); + } + newTable->numEntries = table->numEntries; + return newTable; +} + + + + +AVAEntry *_getAVAEntry(char *groupName, AVATable *mapTable) { + char line[BIG_LINE]; + int lh, rh, mid, cmp;; + + if (!mapTable) { + sprintf (line, "NULL Pointer passed as mapTable when trying to get entry %s", groupName); + report_error (SYSTEM_ERROR, "File Not Found", line); + } + + + lh = 0; + rh = mapTable->numEntries-1; + + while (lh <= rh) { + mid = lh + ((rh-lh)/2); + cmp = strcmp(groupName, mapTable->enteredTable[mid]->userid); + if (cmp == 0) + return mapTable->enteredTable[mid]; + else if (cmp > 0) + lh = mid + 1; + else + rh = mid - 1; + } + + return NULL; + +} + +AVATable *_getTable (char *fileName) { + int lh, rh, mid, cmp; + AVATable *table = NULL; + + /*First checks to see if it's already been parsed*/ + + lh = 0; + rh = parsedFiles.numEntries-1; + while (lh <= rh) { + mid = lh + ((rh - lh)/2); + cmp = strcmp(fileName, parsedFiles.parsedTable[mid]->fileName); + if (cmp == SUCCESS) { + return parsedFiles.parsedTable[mid]->avaTable; + } else if (cmp < SUCCESS) { + rh = mid-1; + } else { + lh = mid+1; + } + } + + yyin = fopen (fileName, "r"); + + if (yyin) { + if (!yyparse()) { + table = _wasParsed (fileName); + table->userdb = NULL; + } + fclose (yyin); + } + + return table; +} + +int _hasBeenParsed (char *aclFileName){ + return (_getTable(aclFileName) != NULL); +} + +AVATable* _wasParsed (char *inFileName) { + Parsed *newEntry; + int i; + + if (!inFileName) + return NULL; + + newEntry = (Parsed*) PERM_MALLOC (sizeof(Parsed)); + newEntry->fileName = PERM_STRDUP (inFileName); + newEntry->avaTable = AVATableDup(&entryTable); + + if (parsedFiles.numEntries % ALLOC_SIZE == 0) { + if (parsedFiles.numEntries) { + Parsed **temp; + + temp = PERM_MALLOC (sizeof(Parsed*)*(parsedFiles.numEntries + ALLOC_SIZE)); + if (!temp) + return NULL; + memcpy (temp, parsedFiles.parsedTable, sizeof(Parsed*)*parsedFiles.numEntries); + PERM_FREE (parsedFiles.parsedTable); + parsedFiles.parsedTable = temp; + } else { + parsedFiles.parsedTable = + (Parsed**) PERM_MALLOC (sizeof (Parsed*) * ALLOC_SIZE); + if (!parsedFiles.parsedTable) + return NULL; + } + } + for (i=parsedFiles.numEntries; i > 0; i--) { + if (strcmp(newEntry->fileName,parsedFiles.parsedTable[i-1]->fileName) < 0) { + parsedFiles.parsedTable[i] = parsedFiles.parsedTable[i-1]; + } else { + break; + } + } + parsedFiles.parsedTable[i] = newEntry; + parsedFiles.numEntries++; + +/*Initialize parser structures to resemble that before parse*/ + entryTable.numEntries = 0; + tempEntry.country = tempEntry.company = tempEntry.CNEntry = NULL; + tempEntry.email = tempEntry.locality = tempEntry.state = NULL; + linenum = 1; + + return newEntry->avaTable; +} + +AVAEntry *_deleteAVAEntry (char *group, AVATable *table) { + int removeIndex; + int lh, rh, mid, cmp; + AVAEntry *entry = NULL; + + if (!group || !table) + return NULL; + + lh = 0; + rh = table->numEntries - 1; + + while (lh <= rh) { + mid = lh + ((rh-lh)/2); + cmp = strcmp (group, table->enteredTable[mid]->userid); + if (cmp == SUCCESS) { + removeIndex = mid; + break; + } else if (cmp < SUCCESS) { + rh = mid-1; + } else { + lh = mid+1; + } + } + + if (lh > rh) + return NULL; + + entry = table->enteredTable[removeIndex]; + + memmove ((char*)(table->enteredTable)+(sizeof(AVAEntry*)*removeIndex), + (char*)(table->enteredTable)+(sizeof(AVAEntry*)*(removeIndex+1)), + (table->numEntries - removeIndex - 1)*sizeof(AVAEntry*)); + + (table->numEntries)--; + + return entry; +} + +void AVAEntry_Free (AVAEntry *entry) { + int i; + + if (entry) { + if (entry->userid) + PERM_FREE (entry->userid); + if (entry->CNEntry) + PERM_FREE (entry->CNEntry); + if (entry->email) + PERM_FREE (entry->email); + if (entry->locality) + PERM_FREE (entry->locality); + if (entry->state) + PERM_FREE (entry->state); + if (entry->country) + PERM_FREE (entry->country); + if (entry->company) + PERM_FREE (entry->company); + if (entry->organizations) { + for (i=0; i<entry->numOrgs; i++) + PERM_FREE (entry->organizations[i]); + PERM_FREE(entry->organizations); + } + } +} + +void PrintHeader(FILE *outfile){ + + fprintf (outfile,"/*This file is generated automatically by the admin server\n"); + fprintf (outfile," *Any changes you make manually may be lost if other\n"); + fprintf (outfile," *changes are made through the admin server.\n"); + fprintf (outfile," */\n\n\n"); + +} + +void writeOutEntry (FILE *outfile, AVAEntry *entry) { + int i; + + /*What should I do if the group id is not there?*/ + if (!entry || !(entry->userid)) + report_error (SYSTEM_ERROR, "AVA-DB Failure", + "Bad entry passed to write out function"); + + fprintf (outfile,"%s: {\n", entry->userid); + if (entry->CNEntry) + fprintf (outfile,"\tCN=\"%s\"\n", entry->CNEntry); + if (entry->email) + fprintf (outfile,"\tE=\"%s\"\n", entry->email); + if (entry->company) + fprintf (outfile,"\tO=\"%s\"\n", entry->company); + if (entry->organizations) { + for (i=0; i < entry->numOrgs; i++) { + fprintf (outfile, "\tOU=\"%s\"\n", entry->organizations[i]); + } + } + if (entry->locality) + fprintf (outfile,"\tL=\"%s\"\n",entry->locality); + if (entry->state) + fprintf (outfile,"\tST=\"%s\"\n",entry->state); + if (entry->country) + fprintf (outfile,"\tC=\"%s\"\n", entry->country); + + fprintf (outfile,"}\n\n\n"); + +} + +void writeOutFile (char *authdb, AVATable *table) { + char line[BIG_LINE]; + char mess[200]; + FILE *newfile; + int i; + + sprintf (line, "%s%c%s%c%s.%s", get_authdb_dir(), FILE_PATHSEP, authdb, FILE_PATHSEP, + AUTH_DB_FILE, AVADB_TAG); + + if (!table) { + sprintf (mess, "The structure for file %s was not loaded before writing out", line); + report_error (SYSTEM_ERROR, "Internal Error", mess); + } + + newfile = fopen (line, "w"); + + if (!newfile) { + sprintf (mess, "Could not open file %s for writing.", line); + report_error(FILE_ERROR, "No File", mess); + } + + PrintHeader (newfile); + + for (i=0;i < table->numEntries; i++) { + writeOutEntry (newfile, table->enteredTable[i]); + } + + fclose(newfile); +} + + +void +logerror(char *error,int line,char *file) { + /* paranoia */ + /*ava-mapping is only functin that initializes yy_sn and yy_rq*/ + if ((yy_sn != NULL) && (yy_rq != NULL)) { + log_error (LOG_FAILURE, "ava-mapping", yy_sn, yy_rq, + "Parse error line %d of %s: %s", line, file, error); + } else { + char errMess[250]; + + sprintf (errMess, "Parse error line %d of %s: %s", line, file, error); + report_error (SYSTEM_ERROR, "Failure: Loading AVA-DB Table", errMess); + } +} + + +void outputAVAdbs(char *chosen) { + char *authdbdir = get_authdb_dir(); + char **listings; + int i; + int numListings = 0; + int hasOptions = 0; + + listings = list_auth_dbs(authdbdir); + + while (listings[numListings++] != NULL); + + for (i=0; listings[i] != NULL ; i++) { + if (!hasOptions) { + printf ("<select name=\"%s\"%s onChange=\"form.submit()\">",AVA_DB_SEL, + (numListings > SELECT_OVERFLOW)?"size=5":""); + hasOptions = 1; + } + + printf ("<option value=\"%s\"%s>%s\n",listings[i], + (strcmp(chosen, listings[i]) == 0) ? "SELECTED":"",listings[i]); + } + + if (hasOptions) + printf ("</select>\n"); + else + printf ("<i><b>Insert an AVA-Database entry first</b></i>\n");/*This should never happen, + *since I never create an empty + *avadb file, + *but one never knows + */ + +} diff --git a/lib/libaccess/avascan.l b/lib/libaccess/avascan.l new file mode 100644 index 00000000..ceae1426 --- /dev/null +++ b/lib/libaccess/avascan.l @@ -0,0 +1,106 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +%{ + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "y.tab.h" +#include "libaccess/ava.h" +#include "netsite.h" + +int linenum = 1; +int first_time = 1; +int old_state; +int num_nested_comments = 0; + +extern AVAEntry tempEntry; +extern AVATable entryTable; + +void strip_quotes(void); + +%} + +%s COMMENT NORM DEFINES DEF_TYPE + +uc_letter [A-Z] +lc_letter [a-z] +digit [0-9] +under_score _ + +letter ([A-Z,a-z]) + +white_space ([ \t]*) +identifier ([_,A-Z,a-z][_,A-Z,a-z,0-9]*) +def_identifier (({white_space}{identifier})+) +text (\"[^\"]*\") +comments (([^"*/"\n])*) + + + +%% + +%{ + if (first_time) { + BEGIN NORM; + first_time = tempEntry.numOrgs = 0; + old_state = NORM; + tempEntry.userid = 0; + tempEntry.country = 0; + tempEntry.CNEntry = 0; + tempEntry.email = 0; + tempEntry.locality = 0; + tempEntry.state = 0; + entryTable.numEntries = 0; + } +%} + + +"/*" {BEGIN COMMENT; num_nested_comments++;} +<COMMENT>"*/" {num_nested_comments--; + if (!num_nested_comments) BEGIN old_state;} +<COMMENT>. {;} + +<NORM>{identifier} {yylval.string = PERM_STRDUP(yytext); + return USER_ID;} +<NORM>":"{white_space}\{ {BEGIN DEF_TYPE; + old_state = DEF_TYPE;} + +<DEF_TYPE>"C" {BEGIN DEFINES; old_state = DEFINES; + return DEF_C; } +<DEF_TYPE>"O" {BEGIN DEFINES; old_state = DEFINES; + return DEF_CO;} +<DEF_TYPE>"OU" {BEGIN DEFINES; old_state = DEFINES; + return DEF_OU;} +<DEF_TYPE>"CN" {BEGIN DEFINES; old_state = DEFINES; + return DEF_CN;} +<DEF_TYPE>"L" {BEGIN DEFINES; old_state = DEFINES; + return DEF_L;} +<DEF_TYPE>"E" {BEGIN DEFINES; old_state = DEFINES; + return DEF_E;} +<DEF_TYPE>"ST" {BEGIN DEFINES; old_state = DEFINES; + return DEF_ST;} +<DEF_TYPE>"}" {BEGIN NORM;old_state = NORM;} + +<DEFINES>= {return EQ_SIGN;} +<DEFINES>{text} {BEGIN DEF_TYPE; old_state = DEF_TYPE; + strip_quotes(); + return DEF_ID;} + +{white_space} {;} +\n {linenum++;} +. {yyerror("Bad input character");} +%% + +int yywrap () { + return 1; +} + +void strip_quotes(void) { + yytext[strlen(yytext)-1]= '\0'; + yylval.string = PERM_STRDUP(&yytext[1]); +} diff --git a/lib/libaccess/las.h b/lib/libaccess/las.h new file mode 100644 index 00000000..3dfdf1e9 --- /dev/null +++ b/lib/libaccess/las.h @@ -0,0 +1,53 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef LAS_H +#define LAS_H + +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include <base/plist.h> + +NSPR_BEGIN_EXTERN_C + +extern int LASTimeOfDayEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASDayOfWeekEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASIpEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASDnsEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASGroupEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +extern int LASUserEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); +/* MLM - for admin delegation */ +extern int LASProgramEval(NSErr_t *errp, char *attribute, CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); + +extern void LASTimeOfDayFlush(void **cookie); +extern void LASDayOfWeekFlush(void **cookie); +extern void LASIpFlush(void **cookie); +extern void LASDnsFlush(void **cookie); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/lasdns.cpp b/lib/libaccess/lasdns.cpp new file mode 100644 index 00000000..22a37dac --- /dev/null +++ b/lib/libaccess/lasdns.cpp @@ -0,0 +1,373 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* lasdns.c + * This file contains the DNS LAS code. + */ + +#include <stdio.h> +#include <string.h> + +#ifdef XP_WIN32 +/* #include <winsock2.h> */ +#include <winsock.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#endif + +#include <netsite.h> +extern "C" { +#include <prnetdb.h> +} +#include <base/plist.h> +#include <base/pool.h> +#include <libaccess/nserror.h> +#include <libaccess/nsauth.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include "lasdns.h" +#include "aclutil.h" +#include "aclcache.h" +#include "permhash.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> +#include "access_plhash.h" +extern "C" { +#include <nspr.h> +} + +#ifdef UTEST +extern int LASDnsGetDns(char **dnsv); +#endif + +/* LASDnsMatch + * Given an array of fully-qualified dns names, tries to match them + * against a given hash table. + * INPUT + * dns DNS string + * context pointer to an LAS DNS context structure + * + * In our usage, this context is derived from + * an acllist cache, which is a representation of + * of some global acis--in other words it's a global thing + * shared between threads--hence the use + * of the "read-only" hash lookup routines here. + */ +int +LASDnsMatch(char *token, LASDnsContext_t *context) +{ + + /* Test for the unusual case where "*" is allowed */ + if (ACL_HashTableLookup_const(context->Table, "*")) + return LAS_EVAL_TRUE; + + /* Start with the full name. Then strip off each component + * leaving the remainder starting with a period. E.g. + * splash.mcom.com + * .mcom.com + * .com + * Search the hash table for each remaining portion. Remember that + * wildcards were put in with the leading period intact. + */ + do { + if (ACL_HashTableLookup_const(context->Table, token)) + return LAS_EVAL_TRUE; + + token = strchr(&token[1], '.'); + } while (token != NULL); + + return LAS_EVAL_FALSE; + +} + +/* LASDNSBuild + * Builds a hash table of all the hostnames provided (plus their aliases + * if aliasflg is true). Wildcards are only permitted in the leftmost + * field. They're represented in the hash table by a leading period. + * E.g. ".mcom.com". + * + * RETURNS Zero on success, else LAS_EVAL_INVALID + */ +int +LASDnsBuild(NSErr_t *errp, char *attr_pattern, LASDnsContext_t *context, int aliasflg) +{ + int delimiter; /* length of valid token */ + char token[256]; /* max length dns name */ + int i; + int ipcnt; + char **p; + unsigned long *ipaddrs=0; + pool_handle_t *pool; + PRStatus error=PR_SUCCESS; + char buffer[PR_NETDB_BUF_SIZE]; +#ifdef UTEST + struct hostent *he, host; +#else + PRHostEnt *he, host; +#endif + + context->Table = PR_NewHashTable(0, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + pool = pool_create(); + context->pool = pool; + if ((!context->Table) || (!context->pool)) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4700, ACL_Program, 1, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAllocateHashT_)); + return LAS_EVAL_INVALID; + } + + do { + /* Get a single hostname from the pattern string */ + delimiter = strcspn(attr_pattern, ", \t"); + strncpy(token, attr_pattern, delimiter); + token[delimiter] = '\0'; + + /* Skip any white space after the token */ + attr_pattern += delimiter; + attr_pattern += strspn(attr_pattern, ", \t"); + + /* If there's a wildcard, strip it off but leave the "." + * Can't have aliases for a wildcard pattern. + * Treat "*" as a special case. If so, go ahead and hash it. + */ + if (token[0] == '*') { + if (token[1] != '\0') { + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, &token[1]), (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4710, ACL_Program, 2, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); + return LAS_EVAL_INVALID; + } + } else { + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, token), (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4720, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); + return LAS_EVAL_INVALID; + } + } + } else { + /* This is a single hostname add it to the hash table */ + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, &token[0]), (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4730, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); + return LAS_EVAL_INVALID; + } + + if (aliasflg) { + /* memset(buffer, '\0', PR_NETDB_BUF_SIZE); + */ +#ifdef UTEST + he = gethostbyname(token); +#else + error = PR_GetHostByName(token, + buffer, + PR_NETDB_BUF_SIZE, + &host); + if (error == PR_SUCCESS) he = &host; + else he = NULL; +#endif + if (he) { + /* Make a copy of the list of IP addresses if any */ + if (he->h_addr_list && he->h_addr_list[0]) { + + /* Count the IP addresses */ + for (p = he->h_addr_list, ipcnt = 0; *p; ++p) { + ++ipcnt; + } + + /* Allocate space */ + ipaddrs = (unsigned long *)PERM_MALLOC(ipcnt * sizeof(unsigned long)); + + /* Copy IP addresses */ + for (i = 0; i < ipcnt; ++i) { + ipaddrs[i] = 0; + memcpy((void *)&ipaddrs[i], he->h_addr_list[i], 4); + } + } + + /* Add each of the aliases to the list */ + if (he->h_aliases && he->h_aliases[0]) { + + for (p = he->h_aliases; *p; ++p) { + /* Add it to the hash table */ + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, *p), (void*)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4740, ACL_Program, 2, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), *p); + PERM_FREE(ipaddrs); + return LAS_EVAL_INVALID; + } + } + } + + for (i = 0; i < ipcnt; ++i) { + +#ifdef UTEST + he = gethostbyaddr((char *)&ipaddrs[i], 4, AF_INET); +#else + error = PR_GetHostByAddr((PRNetAddr *)&ipaddrs[i], + buffer, + PR_NETDB_BUF_SIZE, + &host); + if (error == PR_SUCCESS) he = &host; + else he = NULL; +#endif + if (he) { + if (he->h_name) { + /* Add it to the hash table */ + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, he->h_name), + (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4750, ACL_Program, 2, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), he->h_name); + PERM_FREE(ipaddrs); + return LAS_EVAL_INVALID; + } + } + + if (he->h_aliases && he->h_aliases[0]) { + for (p = he->h_aliases; *p; ++p) { + /* Add it to the hash table */ + if (!PR_HashTableAdd(context->Table, pool_strdup(pool, *p), (void *)-1)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4760, ACL_Program, 2, + XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), *p); + PERM_FREE(ipaddrs); + return LAS_EVAL_INVALID; + } + } + } + } + } + + PERM_FREE(ipaddrs); + + } /* if he */ + + + } /* if aliasflg */ + + } /* else - single hostname */ + + + } while ((attr_pattern != NULL) && + (attr_pattern[0] != '\0') && + (delimiter != (int)NULL)); + + return 0; +} + +/* LASDnsFlush + * Given the address of a las_cookie for a DNS expression entry, frees up + * all allocated memory for it. This includes the hash table, plus the + * context structure. + */ +void +LASDnsFlush(void **las_cookie) +{ + if (*las_cookie == NULL) + return; + + pool_destroy(((LASDnsContext_t *)*las_cookie)->pool); + PR_HashTableDestroy(((LASDnsContext_t *)*las_cookie)->Table); + PERM_FREE(*las_cookie); + *las_cookie = NULL; + return; +} + +/* + * LASDnsEval + * INPUT + * attr_name The string "dns" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of DNS names + * Any segment(s) in a DNS name can be wildcarded using + * "*". Note that this is not a true Regular Expression + * form. + * *cachable Always set to ACL_INDEF_CACHE + * subject Subject property list + * resource Resource property list + * auth_info Authentication info, if any + * RETURNS + * ret code The usual LAS return codes. + */ +int LASDnsEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, + char *attr_pattern, ACLCachable_t *cachable, void **LAS_cookie, + PList_t subject, PList_t resource, + PList_t auth_info, PList_t global_auth) +{ + int result; + int aliasflg; + char *my_dns; + LASDnsContext_t *context; + int rv; + + *cachable = ACL_INDEF_CACHABLE; + + if (strcmp(attr_name, "dns") == 0) + aliasflg = 0; + else if (strcmp(attr_name, "dnsalias") == 0) + aliasflg = 1; + else { + nserrGenerate(errp, ACLERRINVAL, ACLERR4800, ACL_Program, 2, XP_GetAdminStr(DBT_lasDnsBuildReceivedRequestForAtt_), attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4810, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsevalIllegalComparatorDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + /* If this is the first time through, build the pattern tree first. */ + if (*LAS_cookie == NULL) { + ACL_CritEnter(); + if (*LAS_cookie == NULL) { /* Must check again */ + *LAS_cookie = context = + (LASDnsContext_t *)PERM_MALLOC(sizeof(LASDnsContext_t)); + if (context == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4820, ACL_Program, 1, XP_GetAdminStr(DBT_lasdnsevalUnableToAllocateContex_)); + ACL_CritExit(); + return LAS_EVAL_FAIL; + } + context->Table = NULL; + LASDnsBuild(errp, attr_pattern, context, aliasflg); + } + ACL_CritExit(); + } else + context = (LASDnsContext *) *LAS_cookie; + + /* Call the DNS attribute getter */ +#ifdef UTEST + LASDnsGetDns(&my_dns); /* gets stuffed on return */ +#else + rv = ACL_GetAttribute(errp, ACL_ATTR_DNS, (void **)&my_dns, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + if (subject || resource) { + char rv_str[16]; + /* Don't ereport if called from ACL_CachableAclList */ + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRINVAL, ACLERR4830, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsevalUnableToGetDnsErrorDN_), rv_str); + } + return LAS_EVAL_FAIL; + } +#endif + + result = LASDnsMatch(my_dns, context); + + if (comparator == CMP_OP_NE) { + if (result == LAS_EVAL_FALSE) + return LAS_EVAL_TRUE; + else if (result == LAS_EVAL_TRUE) + return LAS_EVAL_FALSE; + } + return (result); +} diff --git a/lib/libaccess/lasdns.h b/lib/libaccess/lasdns.h new file mode 100644 index 00000000..3d86dc98 --- /dev/null +++ b/lib/libaccess/lasdns.h @@ -0,0 +1,10 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +typedef struct LASDnsContext { + PRHashTable *Table; + pool_handle_t *pool; +} LASDnsContext_t; diff --git a/lib/libaccess/lasgroup.cpp b/lib/libaccess/lasgroup.cpp new file mode 100644 index 00000000..6ff5d1bb --- /dev/null +++ b/lib/libaccess/lasgroup.cpp @@ -0,0 +1,164 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* #define DBG_PRINT */ + +/* lasgroup.c + * This file contains the Group LAS code. + */ +#include <stdio.h> +#include <string.h> +#include <netsite.h> +#include "aclpriv.h" +#include <libaccess/usrcache.h> +#include <libaccess/las.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> +#include <ldaputil/errors.h> /* for DBG_PRINT */ +#include "aclutil.h" + +#ifdef UTEST +extern char *LASGroupGetUser(); +#endif /* UTEST */ + + +/* + * LASGroupEval + * INPUT + * attr_name The string "group" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of groups + * *cachable Always set to ACL_NOT_CACHABLE + * subject Subjust property list + * resource Resource property list + * auth_info Authentication info, if any + * RETURNS + * retcode The usual LAS return codes. + */ +int LASGroupEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, + char *attr_pattern, ACLCachable_t *cachable, + void **LAS_cookie, PList_t subject, PList_t resource, + PList_t auth_info, PList_t global_auth) +{ + char *groups = attr_pattern; + int retcode; + char *member_of; + char *user; + char *dbname; + time_t *req_time = 0; + const char *group; + char delim; + int len; + int rv; + + *cachable = ACL_NOT_CACHABLE; + *LAS_cookie = (void *)0; + + if (strcmp(attr_name, ACL_ATTR_GROUP) != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4900, ACL_Program, 2, XP_GetAdminStr(DBT_lasGroupEvalReceivedRequestForAt_), attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR4910, ACL_Program, 2, XP_GetAdminStr(DBT_lasgroupevalIllegalComparatorDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + if (!strcmp(attr_pattern, "anyone")) { + *cachable = ACL_INDEF_CACHABLE; + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + /* Get the authenticated user */ + rv = ACL_GetAttribute(errp, ACL_ATTR_USER, (void **)&user, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRFAIL, ACLERR4920, ACL_Program, 2, XP_GetAdminStr(DBT_lasGroupEvalUnableToGetDatabaseName), rv_str); + return LAS_EVAL_FAIL; + } + + rv = LAS_EVAL_FALSE; + + if (acl_usr_cache_enabled()) { + /* avoid unnecessary system call to get time if cache is disabled */ + req_time = acl_get_req_time(resource); + + /* Loop through all the groups and check if any is in the cache */ + group = groups; + delim = ','; + while((group = acl_next_token_len(group, delim, &len)) != NULL) { + rv = acl_usr_cache_group_len_check(user, dbname, group, len, *req_time); + if (rv == LAS_EVAL_TRUE) { + /* cached group exists */ + break; + } + if (0 != (group = strchr(group+len, delim))) + group++; + else + break; + } + /* group need not be NULL-terminated */ + /* If you need to use it, copy it properly */ + group = 0; + } + + if (rv != LAS_EVAL_TRUE) { + /* not found in the cache or not one of the groups we want */ + PListDeleteProp(subject, ACL_ATTR_GROUPS_INDEX, ACL_ATTR_GROUPS); + PListInitProp(subject, ACL_ATTR_GROUPS_INDEX, ACL_ATTR_GROUPS, groups, 0); + PListDeleteProp(subject, ACL_ATTR_USER_ISMEMBER_INDEX, ACL_ATTR_USER_ISMEMBER); + rv = ACL_GetAttribute(errp, ACL_ATTR_USER_ISMEMBER, (void **)&member_of, + subject, resource, auth_info, global_auth); + + PListDeleteProp(subject, ACL_ATTR_GROUPS_INDEX, ACL_ATTR_GROUPS); + + if (rv != LAS_EVAL_TRUE && rv != LAS_EVAL_FALSE) { + return rv; + } + + if (rv == LAS_EVAL_TRUE) { + /* User is a member of one of the groups */ + /* update the user's cache */ + acl_usr_cache_set_group(user, dbname, member_of, *req_time); + } + } + + if (rv == LAS_EVAL_TRUE) { + retcode = (comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE); + } + else { + /* User is not a member of any of the groups */ + retcode = (comparator == CMP_OP_EQ ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } + + DBG_PRINT4("%s LASGroupEval: uid = \"%s\" groups = \"%s\"\n", + (retcode == LAS_EVAL_FALSE) ? "LAS_EVAL_FALSE" + : (retcode == LAS_EVAL_TRUE) ? "LAS_EVAL_TRUE" + : "Error", + user, attr_pattern); + + return retcode; +} + + +/* LASGroupFlush + * Deallocates any memory previously allocated by the LAS + */ +void +LASGroupFlush(void **las_cookie) +{ + /* do nothing */ + return; +} diff --git a/lib/libaccess/lasip.cpp b/lib/libaccess/lasip.cpp new file mode 100644 index 00000000..2c70a22b --- /dev/null +++ b/lib/libaccess/lasip.cpp @@ -0,0 +1,487 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* aclip.c + * This file contains the IP LAS code. + */ + +#include <stdio.h> +#include <string.h> +#include <netsite.h> +#include <base/plist.h> +#include <libaccess/nserror.h> +#include <libaccess/nsauth.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include "lasip.h" +#include "aclutil.h" +#include "aclcache.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +#define LAS_IP_IS_CONSTANT(x) (((x) == (LASIpTree_t *)LAS_EVAL_TRUE) || ((x) == (LASIpTree_t *)LAS_EVAL_FALSE)) + +#ifdef UTEST +extern int LASIpGetIp(); +#endif + +static int +LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop); + +/* dotdecimal + * Takes netmask and ip strings and returns the numeric values, + * accounting for wildards in the ip specification. Wildcards in the + * ip override the netmask where they conflict. + * INPUT + * ipstr e.g. "123.45.67.89" + * netmaskstr e.g. "255.255.255.0" + * RETURNS + * *ip + * *netmask e.g. 0xffffff00 + * result NULL on success or else one of the LAS_EVAL_* codes. + */ +int +dotdecimal(char *ipstr, char *netmaskstr, int *ip, int *netmask) +{ + int i; + char token[64]; + char *dotptr; /* location of the "." */ + int dotidx; /* index of the period char */ + + /* Sanity check the patterns */ + + /* Netmask can only have digits and periods. */ + if (strcspn(netmaskstr, "0123456789.")) + return LAS_EVAL_INVALID; + + /* IP can only have digits, periods and "*" */ + if (strcspn(ipstr, "0123456789.*")) + return LAS_EVAL_INVALID; + + *netmask = *ip = 0; /* Start with "don't care" */ + + for (i=0; i<4; i++) { + dotptr = strchr(netmaskstr, '.'); + + /* copy out the token, then point beyond it */ + if (dotptr == NULL) + strcpy(token, netmaskstr); + else { + dotidx = dotptr-netmaskstr; + strncpy(token, netmaskstr, dotidx); + token[dotidx] = '\0'; + netmaskstr = ++dotptr; /* skip the period */ + } + + /* Turn into a number and shift left as appropriate */ + *netmask += (atoi(token))<<(8*(4-i-1)); + + if (dotptr == NULL) + break; + } + + for (i=0; i<4; i++) { + dotptr = strchr(ipstr, '.'); + + /* copy out the token, then point beyond it */ + if (dotptr == NULL) + strcpy(token, ipstr); + else { + dotidx = dotptr-ipstr; + strncpy(token, ipstr, dotidx); + token[dotidx] = '\0'; + ipstr = ++dotptr; + } + + /* check for wildcard */ + if (strcmp(token, "*") == 0) { + switch(i) { + case 0: + if (dotptr == NULL) + *netmask &= 0x00000000; + else + *netmask &= 0x00ffffff; + break; + case 1: + if (dotptr == NULL) + *netmask &= 0xff000000; + else + *netmask &= 0xff00ffff; + break; + case 2: + if (dotptr == NULL) + *netmask &= 0xffff0000; + else + *netmask &= 0xffff00ff; + break; + case 3: + *netmask &= 0xffffff00; + break; + } + continue; + } else { + /* Turn into a number and shift left as appropriate */ + *ip += (atoi(token))<<(8*(4-i-1)); + } + + /* check for end of string */ + if (dotptr == NULL) { + switch(i) { + case 0: + *netmask &= 0xff000000; + break; + case 1: + *netmask &= 0xffff0000; + break; + case 2: + *netmask &= 0xffffff00; + break; + } + break; + } + } + + return (int)NULL; +} + + +/* LASIpTreeAlloc + * Malloc a node and set the actions to LAS_EVAL_FALSE + */ +static LASIpTree_t * +LASIpTreeAllocNode(NSErr_t *errp) +{ + LASIpTree_t *newnode; + + newnode = (LASIpTree_t *)PERM_MALLOC(sizeof(LASIpTree_t)); + if (newnode == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR5000, ACL_Program, 1, XP_GetAdminStr(DBT_lasiptreeallocNoMemoryN_)); + return NULL; + } + newnode->action[0] = (LASIpTree_t *)LAS_EVAL_FALSE; + newnode->action[1] = (LASIpTree_t *)LAS_EVAL_FALSE; + return newnode; +} + + +/* LASIpTreeDealloc + * Deallocates a Tree starting from a given node down. + * INPUT + * startnode Starting node to remove. Could be a constant in + * which case, just return success. + * OUTPUT + * N/A + */ +static void +LASIpTreeDealloc(LASIpTree_t *startnode) +{ + int i; + + if (startnode == NULL) + return; + + /* If this is just a constant then we're done */ + if (LAS_IP_IS_CONSTANT(startnode)) + return; + + /* Else recursively call ourself for each branch down */ + for (i=0; i<2; i++) { + if (!(LAS_IP_IS_CONSTANT(startnode->action[i]))) + LASIpTreeDealloc(startnode->action[i]); + } + + /* Now deallocate the local node */ + PERM_FREE(startnode); +} + +/* + * LASIpBuild + * INPUT + * attr_name The string "ip" - in lower case. + * comparator CmpOpEQ or CmpOpNE only + * attr_pattern A comma-separated list of IP addresses and netmasks + * in dotted-decimal form. Netmasks are optionally + * prepended to the IP address using a plus sign. E.g. + * 255.255.255.0+123.45.67.89. Any byte in the IP address + * (but not the netmask) can be wildcarded using "*" + * context A pointer to the IP LAS context structure. + * RETURNS + * ret code The usual LAS return codes. + */ +static int +LASIpBuild(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, LASIpTree_t **treetop) +{ + unsigned int delimiter; /* length of valid token */ + char token[64], token2[64]; /* a single ip[+netmask] */ + char *curptr; /* current place in attr_pattern */ + int netmask, ip; + char *plusptr; + int retcode; + + /* ip address can be delimited by space, tab, comma, or carriage return + * only. + */ + curptr = attr_pattern; + do { + delimiter = strcspn(curptr, ", \t"); + delimiter = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr); + strncpy(token, curptr, delimiter); + token[delimiter] = '\0'; + /* skip all the white space after the token */ + curptr = strpbrk((curptr+delimiter), "1234567890+.*"); + + /* Is there a netmask? */ + plusptr = strchr(token, '+'); + if (plusptr == NULL) { + if (curptr && (*curptr == '+')) { + /* There was a space before (and possibly after) the plus sign*/ + curptr = strpbrk((++curptr), "1234567890.*"); + delimiter = strcspn(curptr, ", \t"); + delimiter = (delimiter <= strlen(curptr)) ? delimiter : strlen(curptr); + strncpy(token2, curptr, delimiter); + token2[delimiter] = '\0'; + retcode = dotdecimal(token, token2, &ip, &netmask); + if (retcode) + return (retcode); + curptr = strpbrk((++curptr), "1234567890+.*"); + } else { + retcode =dotdecimal(token, "255.255.255.255", &ip, &netmask); + if (retcode) + return (retcode); + } + } else { + /* token is the IP addr string in both cases */ + *plusptr ='\0'; /* truncate the string */ + retcode =dotdecimal(token, ++plusptr, &ip, &netmask); + if (retcode) + return (retcode); + } + + if (LASIpAddPattern(errp, netmask, ip, treetop) != (int)NULL) + return LAS_EVAL_INVALID; + + } while ((curptr != NULL) && (delimiter != (int)NULL)); + + return (int)NULL; +} + + +/* LASIpAddPattern + * Takes a netmask and IP address and a pointer to an existing IP + * tree and adds nodes as appropriate to recognize the new pattern. + * INPUT + * netmask e.g. 0xffffff00 + * pattern IP address in 4 bytes + * *treetop An existing IP tree or 0 if a new tree + * RETURNS + * ret code NULL on success, ACL_RES_ERROR on failure + * **treetop If this is a new tree, the head of the tree. + */ +static int +LASIpAddPattern(NSErr_t *errp, int netmask, int pattern, LASIpTree_t **treetop) +{ + int stopbit; /* Don't care after this point */ + int curbit; /* current bit we're working on */ + int curval; /* value of pattern[curbit] */ + LASIpTree_t *curptr; /* pointer to the current node */ + LASIpTree_t *newptr; + + /* stop at the first 1 in the netmask from low to high */ + for (stopbit=0; stopbit<32; stopbit++) { + if ((netmask&(1<<stopbit)) != 0) + break; + } + + /* Special case if there's no tree. Allocate the first node */ + if (*treetop == (LASIpTree_t *)NULL) { /* No tree at all */ + curptr = LASIpTreeAllocNode(errp); + if (curptr == NULL) { + nserrGenerate(errp, ACLERRFAIL, ACLERR5100, ACL_Program, 1, XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_)); + return ACL_RES_ERROR; + } + *treetop = curptr; + } + + /* Special case if the netmask is 0. + */ + if (stopbit > 31) { + curptr->action[0] = (LASIpTree_t *)LAS_EVAL_TRUE; + curptr->action[1] = (LASIpTree_t *)LAS_EVAL_TRUE; + return 0; + } + + + /* follow the tree down the pattern path bit by bit until the + * end of the tree is reached (i.e. a constant). + */ + for (curbit=31,curptr=*treetop; curbit >= 0; curbit--) { + + /* Is the current bit ON? If so set curval to 1 else 0 */ + curval = (pattern & (1<<curbit)) ? 1 : 0; + + /* Are we done, if so remove the rest of the tree */ + if (curbit == stopbit) { + LASIpTreeDealloc(curptr->action[curval]); + curptr->action[curval] = + (LASIpTree_t *)LAS_EVAL_TRUE; + + /* This is the normal exit point. Most other + * exits must be due to errors. + */ + return 0; + } + + /* Oops reached the end - must allocate */ + if (LAS_IP_IS_CONSTANT(curptr->action[curval])) { + newptr = LASIpTreeAllocNode(errp); + if (newptr == NULL) { + LASIpTreeDealloc(*treetop); + nserrGenerate(errp, ACLERRFAIL, ACLERR5110, ACL_Program, 1, XP_GetAdminStr(DBT_ipLasUnableToAllocateTreeNodeN_1)); + return ACL_RES_ERROR; + } + curptr->action[curval] = newptr; + } + + /* Keep going down the tree */ + curptr = curptr->action[curval]; + } + + return ACL_RES_ERROR; +} + +/* LASIpFlush + * Deallocates any memory previously allocated by the LAS + */ +void +LASIpFlush(void **las_cookie) +{ + if (*las_cookie == NULL) + return; + + LASIpTreeDealloc(((LASIpContext_t *)*las_cookie)->treetop); + PERM_FREE(*las_cookie); + *las_cookie = NULL; + return; +} + +/* + * LASIpEval + * INPUT + * attr_name The string "ip" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of IP addresses and netmasks + * in dotted-decimal form. Netmasks are optionally + * prepended to the IP address using a plus sign. E.g. + * 255.255.255.0+123.45.67.89. Any byte in the IP address + * (but not the netmask) can be wildcarded using "*" + * *cachable Always set to ACL_INDEF_CACHABLE + * subject Subject property list + * resource Resource property list + * auth_info The authentication info if any + * RETURNS + * ret code The usual LAS return codes. + */ +int LASIpEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, + char *attr_pattern, ACLCachable_t *cachable, void **LAS_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth) +{ + int bit; + int value; + IPAddr_t ip; + int retcode; + LASIpTree_t *node; + LASIpContext_t *context; + int rv; + char ip_str[124]; + + *cachable = ACL_INDEF_CACHABLE; + + if (strcmp(attr_name, "ip") != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5200, ACL_Program, 2, XP_GetAdminStr(DBT_lasIpBuildReceivedRequestForAttr_), attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5210, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalIllegalComparatorDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + /* GET THE IP ADDR FROM THE SESSION CONTEXT AND STORE IT IN THE + * VARIABLE ip. + */ +#ifndef UTEST + rv = ACL_GetAttribute(errp, ACL_ATTR_IP, (void **)&ip, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + if (subject || resource) { + /* Don't ereport if called from ACL_CachableAclList */ + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRINVAL, ACLERR5220, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalUnableToGetSessionAddre_), rv_str); + } + return LAS_EVAL_FAIL; + } +#else + ip = (IPAddr_t)LASIpGetIp(); +#endif + + /* If this is the first time through, build the pattern tree first. + */ + if (*LAS_cookie == NULL) { + if (strcspn(attr_pattern, "0123456789.*,+ \t")) { + return LAS_EVAL_INVALID; + } + ACL_CritEnter(); + if (*LAS_cookie == NULL) { /* must check again */ + *LAS_cookie = context = + (LASIpContext_t *)PERM_MALLOC(sizeof(LASIpContext_t)); + if (context == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR5230, ACL_Program, 1, XP_GetAdminStr(DBT_lasipevalUnableToAllocateContext_)); + ACL_CritExit(); + return LAS_EVAL_FAIL; + } + context->treetop = NULL; + retcode = LASIpBuild(errp, attr_name, comparator, attr_pattern, + &context->treetop); + if (retcode) { + ACL_CritExit(); + return (retcode); + } + } + ACL_CritExit(); + } else + context = (LASIpContext *) *LAS_cookie; + + node = context->treetop; + + for (bit=31; bit >=0; bit--) { + value = (ip & (IPAddr_t) (1<<bit)) ? 1 : 0; + if (LAS_IP_IS_CONSTANT(node->action[value])) + /* Reached a result, so return it */ + if (comparator == CMP_OP_EQ) + return((int)(PRSize)node->action[value]); + else + return(((int)(PRSize)node->action[value] == + LAS_EVAL_TRUE) ? + LAS_EVAL_FALSE : LAS_EVAL_TRUE); + + else + /* Move on to the next bit */ + node = node->action[value]; + } + + /* Cannot reach here. Even a 32 bit mismatch has a conclusion in + * the pattern tree. + */ + sprintf(ip_str, "%x", ip); + nserrGenerate(errp, ACLERRINTERNAL, ACLERR5240, ACL_Program, 2, XP_GetAdminStr(DBT_lasipevalReach32BitsWithoutConcl_), ip_str); + return LAS_EVAL_INVALID; +} + diff --git a/lib/libaccess/lasip.h b/lib/libaccess/lasip.h new file mode 100644 index 00000000..0e162d9b --- /dev/null +++ b/lib/libaccess/lasip.h @@ -0,0 +1,13 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +typedef struct LASIpTree { + struct LASIpTree *action[2]; +} LASIpTree_t; + +typedef struct LASIpContext { + LASIpTree_t *treetop; /* Top of the pattern tree */ +} LASIpContext_t; diff --git a/lib/libaccess/lastod.cpp b/lib/libaccess/lastod.cpp new file mode 100644 index 00000000..d304986d --- /dev/null +++ b/lib/libaccess/lastod.cpp @@ -0,0 +1,170 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* Source file for the TimeOfDay and DayOfWeek LAS drivers +*/ +#include <time.h> + +#include <netsite.h> +#include <base/util.h> +#include <base/plist.h> +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include "aclutil.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +/* Day of the week LAS driver + * Note that everything is case-insensitive. + * INPUT + * attr must be the string "dayofweek". + * comparator can only be "=" or "!=". + * pattern any sequence of 3-letter day names. I.e. sun, mon, + * tue, wed, thu, fri, sat. Comma delimiters can be used + * but are not necessary. E.g. mon,TueweDThuFRISat + * OUTPUT + * cachable Will be set to ACL_NOT_CACHABLE. + * return code set to LAS_EVAL_* + */ +int +LASDayOfWeekEval(NSErr_t *errp, char *attr, CmpOp_t comparator, char *pattern, + ACLCachable_t *cachable, void **las_cookie, PList_t subject, + PList_t resource, PList_t auth_info, PList_t global_auth) +{ +#ifndef UTEST + struct tm *tm_p, tm; +#endif + time_t t; + char daystr[5]; /* Current local day in ddd */ + char lcl_pattern[512]; + char *compare; + + /* Sanity checking */ + if (strcmp(attr, "dayofweek") != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5400, ACL_Program, 2, XP_GetAdminStr(DBT_unexpectedAttributeInDayofweekSN_), attr); + return LAS_EVAL_INVALID; + } + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5410, ACL_Program, 2, XP_GetAdminStr(DBT_illegalComparatorForDayofweekDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + *cachable = ACL_NOT_CACHABLE; + + /* Obtain and format the local time */ +#ifndef UTEST + t = time(NULL); + tm_p = system_localtime(&t, &tm); + util_strftime(daystr, "%a", tm_p); +#else + t = (0x1000000); /* Mon 2120 hr */ + strftime(daystr, 4, "%a", localtime(&t)); +#endif + makelower(daystr); + strcpy(lcl_pattern, pattern); + makelower(lcl_pattern); + + /* Compare the value to the pattern */ + compare = strstr(lcl_pattern, daystr); + + if ((compare != NULL) && (comparator == CMP_OP_EQ)) + return LAS_EVAL_TRUE; + if ((compare == NULL) && (comparator == CMP_OP_NE)) + return LAS_EVAL_TRUE; + return LAS_EVAL_FALSE; +} + + +/* Time of day LAS + * INPUT + * attr must be "timeofday". + * comparator one of =, !=, >, <, >=, <= + * pattern HHMM military 24-hour clock. E.g. 0700, 2315. + * OUTPUT + * cachable will be set to ACL_NOT_CACHABLE. + * return code set to LAS_EVAL_* + */ +int +LASTimeOfDayEval(NSErr_t *errp, char *attr, CmpOp_t comparator, char *pattern, + ACLCachable_t *cachable, void **LAS_cookie, PList_t subject, + PList_t resource, PList_t auth_info, PList_t global_auth) +{ +#ifndef UTEST + struct tm *tm_p, tm; +#endif + time_t t; + char timestr[6]; /* Current local time in HHMM */ + char start[6], end[6]; + int compare; /* >0, 0, <0 means that current time is greater, equal to, or less than the pattern */ + char *dash; + int intpattern, inttime, intstart, intend; + + if (strcmp(attr, "timeofday") != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5600, ACL_Program, 2, XP_GetAdminStr(DBT_unexpectedAttributeInTimeofdaySN_), attr); + return LAS_EVAL_INVALID; + } + *cachable = ACL_NOT_CACHABLE; + + /* Obtain and format the local time */ +#ifndef UTEST + t = time(NULL); + tm_p = system_localtime(&t, &tm); + util_strftime(timestr, "%H%M", tm_p); +#else + t = (0x1000000); /* Mon 2120 hr */ + strftime(timestr, 5, "%H%M", localtime(&t)); +#endif +#ifdef DEBUG + printf ("timestr = %s\n", timestr); +#endif + inttime = atoi(timestr); + + + dash = strchr(pattern, '-'); + if (dash) { + if (comparator != CMP_OP_EQ && comparator != CMP_OP_NE) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5610, ACL_Program, 2, XP_GetAdminStr(DBT_illegalComparatorForTimeOfDayDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + strncpy(start, pattern, dash-pattern); + start[dash-pattern]='\0'; + intstart = atoi(start); + + strcpy(end, dash+1); + intend = atoi(end); + + if (intend >= intstart) { + return(evalComparator(comparator, !(inttime >= intstart && inttime <= intend))); + } else { /* range wraps around midnight */ + return(evalComparator(comparator, !(inttime >= intstart || inttime <= intend))); + } + } + + + /* ELSE - Just a single time value given. */ + + /* Compare the value to the pattern */ + intpattern = atoi(pattern); + compare = inttime - intpattern; + + /* Test against what the user wanted done */ + return(evalComparator(comparator, compare)); +} + +void +LASDayOfWeekFlush(void **cookie) +{ + return; +} + +void +LASTimeOfDayFlush(void **cookie) +{ + return; +} + diff --git a/lib/libaccess/lasuser.cpp b/lib/libaccess/lasuser.cpp new file mode 100644 index 00000000..d58f9a3f --- /dev/null +++ b/lib/libaccess/lasuser.cpp @@ -0,0 +1,154 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* lasuser.c + * This file contains the User LAS code. + */ + +#include <netsite.h> +#include <base/shexp.h> +#include <base/util.h> +#include <libaccess/las.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> +#include "aclutil.h" + +#ifdef UTEST +extern char * LASUserGetUser(); +#endif + + +/* + * LASUserEval + * INPUT + * attr_name The string "user" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of users + * *cachable Always set to ACL_NOT_CACHABLE. + * subject Subject property list + * resource Resource property list + * auth_info Authentication info, if any + * RETURNS + * retcode The usual LAS return codes. + */ +int LASUserEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, + char *attr_pattern, ACLCachable_t *cachable, + void **LAS_cookie, PList_t subject, PList_t resource, + PList_t auth_info, PList_t global_auth) +{ + char *uid; + char *users; + char *user; + char *comma; + int retcode; + int matched; + int is_owner; + int rv; + + *cachable = ACL_NOT_CACHABLE; + *LAS_cookie = (void *)0; + + if (strcmp(attr_name, ACL_ATTR_USER) != 0) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5700, ACL_Program, 2, XP_GetAdminStr(DBT_lasUserEvalReceivedRequestForAtt_), attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5710, ACL_Program, 2, XP_GetAdminStr(DBT_lasuserevalIllegalComparatorDN_), comparator_string(comparator)); + return LAS_EVAL_INVALID; + } + + if (!strcmp(attr_pattern, "anyone")) { + *cachable = ACL_INDEF_CACHABLE; + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + /* get the authenticated user name */ +#ifndef UTEST + + rv = ACL_GetAttribute(errp, ACL_ATTR_USER, (void **)&uid, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } +#else + uid = (char *)LASUserGetUser(); +#endif + + /* We have an authenticated user */ + if (!strcmp(attr_pattern, "all")) { + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + users = STRDUP(attr_pattern); + + if (!users) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR5720, ACL_Program, 1, + XP_GetAdminStr(DBT_lasuserevalRanOutOfMemoryN_)); + return LAS_EVAL_FAIL; + } + + user = users; + matched = 0; + + /* check if the uid is one of the users */ + while(user != 0 && *user != 0 && !matched) { + if ((comma = strchr(user, ',')) != NULL) { + *comma++ = 0; + } + + /* ignore leading whitespace */ + while(*user == ' ' || *user == '\t') user++; + + if (*user) { + /* ignore trailing whitespace */ + int len = strlen(user); + char *ptr = user+len-1; + + while(*ptr == ' ' || *ptr == '\t') *ptr-- = 0; + } + + if (!strcasecmp(user, ACL_ATTR_OWNER)) { + rv = ACL_GetAttribute(errp, ACL_ATTR_IS_OWNER, (void **)&is_owner, + subject, resource, auth_info, global_auth); + if (rv == LAS_EVAL_TRUE) + matched = 1; + else + /* continue checking for next user */ + user = comma; + } + else if (!WILDPAT_CASECMP(uid, user)) { + /* uid is one of the users */ + matched = 1; + } + else { + /* continue checking for next user */ + user = comma; + } + } + + if (comparator == CMP_OP_EQ) { + retcode = (matched ? LAS_EVAL_TRUE : LAS_EVAL_FALSE); + } + else { + retcode = (matched ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } + + FREE(users); + return retcode; +} + + +/* LASUserFlush + * Deallocates any memory previously allocated by the LAS + */ +void +LASUserFlush(void **las_cookie) +{ + /* do nothing */ + return; +} diff --git a/lib/libaccess/lcache.h b/lib/libaccess/lcache.h new file mode 100644 index 00000000..0e39a3a6 --- /dev/null +++ b/lib/libaccess/lcache.h @@ -0,0 +1,23 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef CACHE_H +#define CACHE_H + +NSPR_BEGIN_EXTERN_C + +extern void ACL_ListHashUpdate(ACLListHandle_t **acllistp); +extern void ACL_Init(void); +extern void ACL_CritEnter(void); +extern void ACL_CritExit(void); +extern ENTRY *ACL_GetUriHash(ENTRY item, ACTION action); +extern int ACL_CacheCheck(char *uri, ACLListHandle_t **acllist_p); +extern void ACL_CacheEnter(char *uri, ACLListHandle_t **acllist_p); +extern void ACL_CacheAbort(ACLListHandle_t **acllist_p); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/ldapacl.cpp b/lib/libaccess/ldapacl.cpp new file mode 100644 index 00000000..ad9d7f7f --- /dev/null +++ b/lib/libaccess/ldapacl.cpp @@ -0,0 +1,822 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* #define DBG_PRINT */ + +#include <netsite.h> +#include <base/rwlock.h> +#include <base/ereport.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include "aclutil.h" +#include <ldaputil/errors.h> +#include <ldaputil/certmap.h> +#include <ldaputil/ldaputil.h> +#include <ldaputil/dbconf.h> +#include <ldaputil/ldapauth.h> +#include <libaccess/authdb.h> +#include <libaccess/ldapacl.h> +#include <libaccess/usrcache.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclglobal.h> +#include <libaccess/aclerror.h> + +#define BIG_LINE 1024 + +static int need_ldap_over_ssl = 0; +static RWLOCK ldb_rwlock = (RWLOCK)0; + +void init_ldb_rwlock () +{ + ldb_rwlock = rwlock_Init(); +} + +void ldb_write_rwlock (LDAPDatabase_t *ldb, RWLOCK lock) +{ + DBG_PRINT1("ldb_write_rwlock\n"); + /* Don't lock for local database -- let ldapsdk handle thread safety*/ + if (!ldapu_is_local_db(ldb)) + rwlock_WriteLock(lock); +} + +void ldb_read_rwlock (LDAPDatabase_t *ldb, RWLOCK lock) +{ + DBG_PRINT1("ldb_read_rwlock\n"); + /* Don't lock for local database -- let ldapsdk handle thread safety*/ + if (!ldapu_is_local_db(ldb)) + rwlock_ReadLock(lock); +} + +void ldb_unlock_rwlock (LDAPDatabase_t *ldb, RWLOCK lock) +{ + DBG_PRINT1("ldb_unlock_rwlock\n"); + /* we don't lock for local database */ + if (!ldapu_is_local_db(ldb)) + rwlock_Unlock(lock); +} + +int ACL_NeedLDAPOverSSL () +{ + return need_ldap_over_ssl; +} + +NSAPI_PUBLIC int parse_ldap_url (NSErr_t *errp, ACLDbType_t dbtype, + const char *dbname, const char *url, + PList_t plist, void **db) +{ + LDAPDatabase_t *ldb; + char *binddn = 0; + char *bindpw = 0; + int rv; + + *db = 0; + + if (!url || !*url) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5800, ACL_Program, 1, XP_GetAdminStr(DBT_ldapaclDatabaseUrlIsMissing)); + return -1; + } + + if (!dbname || !*dbname) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5810, ACL_Program, 1, XP_GetAdminStr(DBT_ldapaclDatabaseNameIsMissing)); + return -1; + } + + /* look for binddn and bindpw in the plist */ + if (plist) { + PListFindValue(plist, LDAPU_ATTR_BINDDN, (void **)&binddn, NULL); + PListFindValue(plist, LDAPU_ATTR_BINDPW, (void **)&bindpw, NULL); + } + + rv = ldapu_url_parse(url, binddn, bindpw, &ldb); + + if (rv != LDAPU_SUCCESS) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5820, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclErrorParsingLdapUrl), ldapu_err2string(rv)); + return -1; + } + + /* success */ + *db = ldb; + + /* Check if we need to do LDAP over SSL */ + if (!need_ldap_over_ssl) { + need_ldap_over_ssl = ldb->use_ssl; + } + + return 0; +} + +int get_is_valid_password_basic_ldap (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + /* If the raw-user-name and raw-user-password attributes are present then + * verify the password against the LDAP database. + * Otherwise call AttrGetter for raw-user-name. + */ + char *raw_user; + char *raw_pw; + char *userdn = 0; + int rv; + char *dbname; + ACLDbType_t dbtype; + LDAPDatabase_t *ldb; + time_t *req_time = 0; + pool_handle_t *subj_pool = PListGetPool(subject) + + DBG_PRINT1("get_is_valid_password_basic_ldap\n"); + rv = ACL_GetAttribute(errp, ACL_ATTR_RAW_USER, (void **)&raw_user, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + rv = ACL_GetAttribute(errp, ACL_ATTR_RAW_PASSWORD, (void **)&raw_pw, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + if (!raw_pw || !*raw_pw) { + /* Null password is not allowed in LDAP since most LDAP servers let + * the bind call succeed as anonymous login (with limited privileges). + */ + return LAS_EVAL_FALSE; + } + + /* Authenticate the raw_user and raw_pw against LDAP database. */ + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRFAIL, ACLERR5830, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetDatabaseName), rv_str); + return LAS_EVAL_FAIL; + } + + rv = ACL_DatabaseFind(errp, dbname, &dbtype, (void **)&ldb); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRFAIL, ACLERR5840, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetParsedDatabaseName), dbname); + return rv; + } + + if (acl_usr_cache_enabled()) { + /* avoid unnecessary system call to get time if cache is disabled */ + req_time = acl_get_req_time(resource); + + /* We have user name and password. */ + /* Check the cache to see if the password is valid */ + rv = acl_usr_cache_passwd_check(raw_user, dbname, raw_pw, *req_time, + &userdn, subj_pool); + } + else { + rv = LAS_EVAL_FALSE; + } + + if (rv != LAS_EVAL_TRUE) { + LDAPMessage *res = 0; + const char *some_attrs[] = { "C", 0 }; + LDAP *ld; + char *udn; + /* Not found in the cache */ + + /* Since we will bind with the user/password and other code relying on + * ldb being bound as ldb->binddn and ldb->bindpw may fail. So block + * them until we are done. + */ + ldb_write_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + ldb_unlock_rwlock(ldb, ldb_rwlock); + nserrGenerate(errp, ACLERRFAIL, ACLERR5850, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclCoudlntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + /* LDAPU_REQ will reconnect & retry once if LDAP server went down */ + ld = ldb->ld; + LDAPU_REQ(rv, ldb, ldapu_find_uid_attrs(ld, raw_user, + ldb->basedn, some_attrs, + 1, &res)); + + if (rv == LDAPU_SUCCESS) { + LDAPMessage *entry = ldap_first_entry(ld, res); + + userdn = ldap_get_dn(ld, entry); + + /* LDAPU_REQ will reconnect & retry once if LDAP server went down */ + LDAPU_REQ(rv, ldb, ldapu_auth_userdn_password(ld, userdn, raw_pw)); + + /* Make sure we rebind with the server's DN + * ignore errors from ldapu_ldap_rebind -- we will get the same + * errors in subsequent calls to LDAP. Return status from the + * above call is our only interest now. + */ + ldapu_ldap_rebind(ldb); + } + + if (res) ldap_msgfree(res); + ldb_unlock_rwlock(ldb, ldb_rwlock); + + if (rv == LDAPU_FAILED || rv == LDAP_INVALID_CREDENTIALS) { + /* user entry not found or incorrect password */ + if (userdn) ldap_memfree(userdn); + return LAS_EVAL_FALSE; + } + else if (rv != LDAPU_SUCCESS) { + /* some unexpected LDAP error */ + nserrGenerate(errp, ACLERRFAIL, ACLERR5860, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclPassworkCheckLdapError), ldapu_err2string(rv)); + if (userdn) ldap_memfree(userdn); + return LAS_EVAL_FAIL; + } + + /* Make an entry in the cache */ + if (acl_usr_cache_enabled()) { + acl_usr_cache_insert(raw_user, dbname, userdn, raw_pw, 0, 0, + *req_time); + } + udn = pool_strdup(subj_pool, userdn); + ldap_memfree(userdn); + userdn = udn; + } + + PListInitProp(subject, ACL_ATTR_IS_VALID_PASSWORD_INDEX, ACL_ATTR_IS_VALID_PASSWORD, raw_user, 0); + PListInitProp(subject, ACL_ATTR_USERDN_INDEX, ACL_ATTR_USERDN, userdn, 0); + return LAS_EVAL_TRUE; +} + +static int acl_grpcmpfn (const void *groupids, const char *group, + const int len) +{ + const char *token = (const char *)groupids; + int tlen; + char delim = ','; + + while((token = acl_next_token_len(token, delim, &tlen)) != NULL) { + if (tlen > 0 && tlen == len && !strncmp(token, group, len)) + return LDAPU_SUCCESS; + else if (tlen == 0 || 0 != (token = strchr(token+tlen, delim))) + token++; + else + break; + } + + return LDAPU_FAILED; +} + +int get_user_ismember_ldap (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + int retval; + int rv; + char *userdn; + char *groups; + char *member_of = 0; + LDAPDatabase_t *ldb; + char *dbname; + ACLDbType_t dbtype; + + DBG_PRINT1("get_user_ismember_ldap\n"); + + rv = ACL_GetAttribute(errp, ACL_ATTR_USERDN, (void **)&userdn, subject, + resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return LAS_EVAL_FAIL; + } + + rv = ACL_GetAttribute(errp, ACL_ATTR_GROUPS, (void **)&groups, subject, + resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRINVAL, ACLERR5900, ACL_Program, 2, XP_GetAdminStr(DBT_GetUserIsMemberLdapUnabelToGetDatabaseName), rv_str); + return rv; + } + + rv = ACL_DatabaseFind(errp, dbname, &dbtype, (void **)&ldb); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRINVAL, ACLERR5910, ACL_Program, 2, XP_GetAdminStr(DBT_GetUserIsMemberLdapUnableToGetParsedDatabaseName), dbname); + return rv; + } + + ldb_read_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + ldb_unlock_rwlock(ldb, ldb_rwlock); + nserrGenerate(errp, ACLERRFAIL, ACLERR5930, ACL_Program, 2, + XP_GetAdminStr(DBT_GetUserIsMemberLdapCouldntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + /* check if the user is member of any of the groups */ + /* LDAPU_REQ will reconnect & retry once if LDAP server went down */ + LDAPU_REQ(rv, ldb, ldapu_auth_userdn_groupids(ldb->ld, + userdn, + groups, + acl_grpcmpfn, + ldb->basedn, + &member_of)); + + ldb_unlock_rwlock(ldb, ldb_rwlock); + + if (rv == LDAPU_SUCCESS) { + /* User is a member of one of the groups */ + if (member_of) { + PListInitProp(subject, ACL_ATTR_USER_ISMEMBER_INDEX, + ACL_ATTR_USER_ISMEMBER, + pool_strdup(PListGetPool(subject), member_of), 0); + retval = LAS_EVAL_TRUE; + } + else { + /* This shouldn't happen */ + retval = LAS_EVAL_FALSE; + } + } + else if (rv == LDAPU_FAILED) { + /* User is not a member of any of the groups */ + retval = LAS_EVAL_FALSE; + } + else { + /* unexpected LDAP error */ + nserrGenerate(errp, ACLERRFAIL, ACLERR5950, ACL_Program, 2, + XP_GetAdminStr(DBT_GetUserIsMemberLdapError), + ldapu_err2string(rv)); + retval = LAS_EVAL_FAIL; + } + + return retval; +} + + +/* This function returns LDAPU error codes so that the caller can call + * ldapu_err2string to get the error string. + */ +int acl_map_cert_to_user (NSErr_t *errp, const char *dbname, + LDAPDatabase_t *ldb, void *cert, + PList_t resource, pool_handle_t *pool, + char **user, char **userdn) +{ + int rv; + LDAPMessage *res; + LDAPMessage *entry; + char *uid; + time_t *req_time = 0; + + if (acl_usr_cache_enabled()) { + req_time = acl_get_req_time(resource); + + rv = acl_cert_cache_get_uid (cert, dbname, *req_time, user, userdn, + pool); + } + else { + rv = LAS_EVAL_FALSE; + } + + if (rv != LAS_EVAL_TRUE) { + /* Not found in the cache */ + + ldb_read_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + /* LDAPU_REQ will reconnect & retry once if LDAP server went down */ + /* it sets the variable rv */ + if (rv == LDAPU_SUCCESS) { + char *dn = 0; + + LDAPU_REQ(rv, ldb, ldapu_cert_to_user(cert, ldb->ld, ldb->basedn, + &res, &uid)); + + if (rv == LDAPU_SUCCESS) { + char *dn; + + *user = pool_strdup(pool, uid); + if (!*user) rv = LDAPU_ERR_OUT_OF_MEMORY; + free(uid); + + entry = ldap_first_entry(ldb->ld, res); + dn = ldap_get_dn(ldb->ld, entry); + if (acl_usr_cache_enabled()) { + acl_cert_cache_insert (cert, dbname, *user, dn, *req_time); + } + *userdn = dn ? pool_strdup(pool, dn) : 0; + if (!*userdn) rv = LDAPU_ERR_OUT_OF_MEMORY; + ldap_memfree(dn); + } + if (res) ldap_msgfree(res); + } + ldb_unlock_rwlock(ldb, ldb_rwlock); + } + else { + rv = LDAPU_SUCCESS; + } + + return rv; +} + + +/* + * ACL_LDAPDatabaseHandle - + * Finds the internal structure representing the 'dbname'. If it is an LDAP + * database, returns the 'LDAP *ld' pointer. Also, binds to the LDAP server. + * The LDAP *ld handle can be used in calls to LDAP API. + * Returns LAS_EVAL_TRUE if successful, otherwise logs an error in + * LOG_SECURITY and returns LAS_EVAL_FAIL. + */ +int ACL_LDAPDatabaseHandle (NSErr_t *errp, const char *dbname, LDAP **ld, + char **basedn) +{ + int rv; + ACLDbType_t dbtype; + void *db; + LDAPDatabase_t *ldb; + + *ld = 0; + if (!dbname || !*dbname) dbname = DBCONF_DEFAULT_DBNAME; + + /* Check if the ldb is already in the ACLUserLdbHash */ + ldb = (LDAPDatabase_t *)PR_HashTableLookup(ACLUserLdbHash, dbname); + + if (!ldb) { + + rv = ACL_DatabaseFind(errp, dbname, &dbtype, &db); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRINVAL, ACLERR6000, ACL_Program, 2, XP_GetAdminStr(DBT_LdapDatabaseHandleNotARegisteredDatabase), dbname); + return LAS_EVAL_FAIL; + } + + if (!ACL_DbTypeIsEqual(errp, dbtype, ACL_DbTypeLdap)) { + /* Not an LDAP database -- error */ + nserrGenerate(errp, ACLERRINVAL, ACLERR6010, ACL_Program, 2, XP_GetAdminStr(DBT_LdapDatabaseHandleNotAnLdapDatabase), dbname); + return LAS_EVAL_FAIL; + } + + ldb = ldapu_copy_LDAPDatabase_t((LDAPDatabase_t *)db); + + if (!ldb) { + /* Not an LDAP database -- error */ + nserrGenerate(errp, ACLERRNOMEM, ACLERR6020, ACL_Program, 1, XP_GetAdminStr(DBT_LdapDatabaseHandleOutOfMemory)); + return LAS_EVAL_FAIL; + } + + PR_HashTableAdd(ACLUserLdbHash, PERM_STRDUP(dbname), ldb); + } + + if (!ldb->ld) { + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + nserrGenerate(errp, ACLERRFAIL, ACLERR6030, ACL_Program, 2, XP_GetAdminStr(DBT_LdapDatabaseHandleCouldntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + } + + /* + * Force the rebind -- we don't know whether the customer has used this ld + * to bind as somebody else. It will also check if the LDAP server is up + * and running, reestablish the connection if the LDAP server has rebooted + * since it was last used. + */ + rv = ldapu_ldap_rebind(ldb); + + if (rv != LDAPU_SUCCESS) { + nserrGenerate(errp, ACLERRFAIL, ACLERR6040, ACL_Program, 2, XP_GetAdminStr(DBT_LdapDatabaseHandleCouldntBindToLdapServer), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + *ld = ldb->ld; + + if (basedn) { + /* They asked for the basedn too */ + *basedn = PERM_STRDUP(ldb->basedn); + } + + return LAS_EVAL_TRUE; +} + +int get_userdn_ldap (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + char *uid; + char *dbname; + char *userdn; + time_t *req_time = 0; + pool_handle_t *subj_pool = PListGetPool(subject); + int rv; + + rv = ACL_GetAttribute(errp, ACL_ATTR_USER, (void **)&uid, subject, + resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return LAS_EVAL_FAIL; + } + + /* The getter for ACL_ATTR_USER may have put the USERDN on the PList */ + rv = PListGetValue(subject, ACL_ATTR_USERDN_INDEX, (void **)&userdn, NULL); + + if (rv >= 0) { + /* Nothing to do */ + return LAS_EVAL_TRUE; + } + + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRFAIL, ACLERR5830, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetDatabaseName), rv_str); + return LAS_EVAL_FAIL; + } + + /* Check if the userdn is available in the usr_cache */ + if (acl_usr_cache_enabled()) { + /* avoid unnecessary system call to get time if cache is disabled */ + req_time = acl_get_req_time(resource); + + rv = acl_usr_cache_get_userdn(uid, dbname, *req_time, &userdn, + subj_pool); + } + else { + rv = LAS_EVAL_FALSE; + } + + if (rv == LAS_EVAL_TRUE) { + /* Found in the cache */ + PListInitProp(subject, ACL_ATTR_USERDN_INDEX, ACL_ATTR_USERDN, + userdn, 0); + } + else { + ACLDbType_t dbtype; + LDAPDatabase_t *ldb = 0; + + /* Perform LDAP lookup */ + rv = ACL_DatabaseFind(errp, dbname, &dbtype, (void **)&ldb); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRFAIL, ACLERR5840, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetParsedDatabaseName), dbname); + return rv; + } + + ldb_read_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + ldb_unlock_rwlock(ldb, ldb_rwlock); + nserrGenerate(errp, ACLERRFAIL, ACLERR5850, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclCoudlntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + LDAPU_REQ(rv, ldb, ldapu_find_userdn(ldb->ld, uid, ldb->basedn, + &userdn)); + + ldb_unlock_rwlock(ldb, ldb_rwlock); + + if (rv == LDAPU_SUCCESS) { + /* Found it. Store it in the cache also. */ + PListInitProp(subject, ACL_ATTR_USERDN_INDEX, ACL_ATTR_USERDN, + pool_strdup(subj_pool, userdn), 0); + if (acl_usr_cache_enabled()) { + acl_usr_cache_set_userdn(uid, dbname, userdn, *req_time); + } + ldapu_free(userdn); + rv = LAS_EVAL_TRUE; + } + else if (rv == LDAPU_FAILED) { + /* Not found but not an error */ + rv = LAS_EVAL_FALSE; + } + else { + /* some unexpected LDAP error */ + nserrGenerate(errp, ACLERRFAIL, ACLERR5860, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclPassworkCheckLdapError), ldapu_err2string(rv)); + rv = LAS_EVAL_FAIL; + } + } + + return rv; +} + +/* Attr getter for LDAP database to check if the user exists */ +int get_user_exists_ldap (NSErr_t *errp, PList_t subject, + PList_t resource, PList_t auth_info, + PList_t global_auth, void *unused) +{ + int rv; + char *user; + char *userdn; + + /* See if the userdn is already available */ + rv = PListGetValue(subject, ACL_ATTR_USERDN_INDEX, (void **)&userdn, NULL); + + if (rv >= 0) { + /* Check if the DN is still valid against the database */ + /* Get the database name */ + char *dbname; + ACLDbType_t dbtype; + LDAPDatabase_t *ldb = 0; + LDAPMessage *res; + const char *some_attrs[] = { "c", 0 }; + + rv = ACL_AuthInfoGetDbname(auth_info, &dbname); + + if (rv < 0) { + char rv_str[16]; + sprintf(rv_str, "%d", rv); + nserrGenerate(errp, ACLERRFAIL, ACLERR5830, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetDatabaseName), rv_str); + return LAS_EVAL_FAIL; + } + + /* Perform LDAP lookup */ + rv = ACL_DatabaseFind(errp, dbname, &dbtype, (void **)&ldb); + + if (rv != LAS_EVAL_TRUE) { + nserrGenerate(errp, ACLERRFAIL, ACLERR5840, ACL_Program, 2, + XP_GetAdminStr(DBT_ldapaclUnableToGetParsedDatabaseName), dbname); + return rv; + } + + ldb_read_rwlock(ldb, ldb_rwlock); + rv = ldapu_ldap_init_and_bind(ldb); + + if (rv != LDAPU_SUCCESS) { + ldb_unlock_rwlock(ldb, ldb_rwlock); + nserrGenerate(errp, ACLERRFAIL, ACLERR5850, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclCoudlntInitializeConnectionToLdap), ldapu_err2string(rv)); + return LAS_EVAL_FAIL; + } + + LDAPU_REQ(rv, ldb, ldapu_find (ldb->ld, ldb->basedn, LDAP_SCOPE_BASE, + NULL, some_attrs, 1, &res)); + + ldb_unlock_rwlock(ldb, ldb_rwlock); + + if (rv == LDAPU_SUCCESS) { + /* Found it. */ + rv = LAS_EVAL_TRUE; + } + else if (rv == LDAPU_FAILED) { + /* Not found but not an error */ + rv = LAS_EVAL_FALSE; + } + else { + /* some unexpected LDAP error */ + nserrGenerate(errp, ACLERRFAIL, ACLERR5860, ACL_Program, 2, XP_GetAdminStr(DBT_ldapaclPassworkCheckLdapError), ldapu_err2string(rv)); + rv = LAS_EVAL_FAIL; + } + } + else { + /* If the DN doesn't exist, should we just return an error ? */ + /* If yes, we don't need rest of the code */ + + /* If we don't have a DN, we must have a user at least */ + rv = PListGetValue(subject, ACL_ATTR_USER_INDEX, (void **)&user, NULL); + + if (rv < 0) { + /* We don't even have a user name */ + return LAS_EVAL_FAIL; + } + + rv = ACL_GetAttribute(errp, ACL_ATTR_USERDN, (void **)&userdn, subject, + resource, auth_info, global_auth); + } + + /* If we can get the userdn then the user exists */ + if (rv == LAS_EVAL_TRUE) { + PListInitProp(subject, ACL_ATTR_USER_EXISTS_INDEX, + ACL_ATTR_USER_EXISTS, userdn, 0); + } + + return rv; +} + +/* acl_user_exists - */ +/* Function to check if the user still exists */ +/* This function works for all kinds of databases */ +/* Returns 0 on success and -ve value on failure */ +NSAPI_PUBLIC int acl_user_exists (const char *user, const char *userdn, + const char *dbname, const int logerr) +{ + NSErr_t err = NSERRINIT; + NSErr_t *errp = &err; + pool_handle_t *pool = 0; + time_t *req_time = 0; + PList_t subject = 0; + PList_t resource = 0; + PList_t auth_info = 0; + PList_t global_auth = NULL; + char *olddn = 0; + int rv; + + /* Check if the userdn is available in the usr_cache */ + if (acl_usr_cache_enabled() && userdn) { + /* avoid unnecessary system call to get time if cache is disabled */ + req_time = (time_t *)MALLOC(sizeof(time_t)); + + if (req_time) { + time(req_time); + rv = acl_usr_cache_userdn_check(user, dbname, userdn, *req_time); + FREE((void *)req_time); + } + + if (rv == LAS_EVAL_TRUE) + { + /* Found in the cache with the same DN */ + return 0; + } + } + + pool = pool_create(); + subject = PListCreate(pool, ACL_ATTR_INDEX_MAX, NULL, NULL); + resource = PListCreate(pool, ACL_ATTR_INDEX_MAX, NULL, NULL); + auth_info = PListCreate(pool, ACL_ATTR_INDEX_MAX, NULL, NULL); + + if (!pool || !subject || !resource || !auth_info) { + /* ran out of memory */ + goto no_mem; + } + + /* store a pointer to the user rather than a copy */ + rv = PListInitProp(subject, ACL_ATTR_USER_INDEX, ACL_ATTR_USER, + user, 0); + if (rv < 0) { /* Plist error */ goto plist_err; } + + if (userdn && *userdn) { + /* store a pointer to the userdn rather than a copy */ + rv = PListInitProp(subject, ACL_ATTR_USERDN_INDEX, ACL_ATTR_USERDN, + userdn, 0); + if (rv < 0) { /* Plist error */ goto plist_err; } + } + + /* store the cached dbname on auth_info */ + rv = ACL_AuthInfoSetDbname(errp, auth_info, dbname); + if (rv < 0) { /* auth_info error */ goto err; } + + rv = ACL_GetAttribute(errp, ACL_ATTR_USER_EXISTS, (void **)&user, + subject, resource, auth_info, global_auth); + + if (rv == LAS_EVAL_TRUE) { + /* User still exists */ + rv = 0; + } + else if (rv == LAS_EVAL_FALSE) { + /* User doesn't exist anymore */ + nserrGenerate(errp, ACLERRFAIL, 5880, ACL_Program, 2, XP_GetAdminStr(DBT_AclUserExistsNot), user); + goto err; + } + else { + /* Unexpected error while checking the existence of the user */ + goto err; + } + + goto done; + +plist_err: + nserrGenerate(errp, ACLERRFAIL, 5890, ACL_Program, 1, XP_GetAdminStr(DBT_AclUserPlistError)); + goto err; + +no_mem: + nserrGenerate(errp, ACLERRNOMEM, 5870, ACL_Program, 1, XP_GetAdminStr(DBT_AclUserExistsOutOfMemory)); + goto err; + +err: + if (logerr) { + /* Unexpected error while checking the existence of the user */ + char buf[BIG_LINE]; + /* generate error message (upto depth 6) into buf */ + aclErrorFmt(errp, buf, BIG_LINE, 6); + ereport(LOG_SECURITY, "Error while checking the existence of user: %s", buf); + } + + nserrDispose(errp); + rv = -1; + +done: + /* Destroy the PLists & the pool */ + if (subject) PListDestroy(subject); + if (resource) PListDestroy(resource); + if (auth_info) PListDestroy(auth_info); + if (pool) pool_destroy(pool); + return rv; +} diff --git a/lib/libaccess/ldapauth.h b/lib/libaccess/ldapauth.h new file mode 100644 index 00000000..409aa6e9 --- /dev/null +++ b/lib/libaccess/ldapauth.h @@ -0,0 +1,42 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef LDAP_AUTH_H +#define LDAP_AUTH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ldap/ldap.h" + +const int LDAP_ACL_SUCCESS = 0; +const int LDAP_ACL_FAILED = -1; + +extern int ldap_auth_userdn_groupdn (LDAP *ld, char *userdn, + char *groupdn); +extern int ldap_auth_uid_groupdn (LDAP *ld, char *uid, + char *groupdn); +extern int ldap_auth_uid_groupid (LDAP *ld, char *uid, + char *groupid); +extern int ldap_auth_userdn_groupid (LDAP *ld, char *userdn, + char *groupid); +extern int ldap_auth_userdn_attrfilter (LDAP *ld, char *userdn, + char *attrfilter); +extern int ldap_auth_uid_attrfilter (LDAP *ld, char *uid, + char *attrfilter); +extern int ldap_auth_userdn_password (LDAP *ld, char *userdn, + char *password); +extern int ldap_find_uid (LDAP *ld, char *uid, LDAPMessage **res); +extern int ldap_auth_uid_password (LDAP *ld, char *uid, + char *password); +extern LDAP *init_ldap(); + +#ifdef __cplusplus +} +#endif + +#endif /* LDAP_AUTH_H */ diff --git a/lib/libaccess/leval.h b/lib/libaccess/leval.h new file mode 100644 index 00000000..22d3ee8a --- /dev/null +++ b/lib/libaccess/leval.h @@ -0,0 +1,18 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef LEVAL_H +#define LEVAL_H + +NSPR_BEGIN_EXTERN_C + +int +freeLAS(NSErr_t *errp, char *attribute, void **las_cookie); + +NSPR_END_EXTERN_C + +#endif + diff --git a/lib/libaccess/lparse.h b/lib/libaccess/lparse.h new file mode 100644 index 00000000..6386d56e --- /dev/null +++ b/lib/libaccess/lparse.h @@ -0,0 +1,27 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * This grammar is intended to parse the version 3.0 ACL + * and output an ACLParseACE_t structure. + */ + +#ifndef LPARSE_H +#define LPARSE_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern int aclPushListHandle(ACLListHandle_t *handle); +extern int aclparse(void); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/libaccess/method.cpp b/lib/libaccess/method.cpp new file mode 100644 index 00000000..1425ad55 --- /dev/null +++ b/lib/libaccess/method.cpp @@ -0,0 +1,163 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include <netsite.h> +#include <libaccess/las.h> +#include <libaccess/acl.h> +#include <libaccess/aclerror.h> +#include <libaccess/dbtlibaccess.h> +#include "aclpriv.h" + +NSAPI_PUBLIC int ACL_ModuleRegister (NSErr_t *errp, const char *module_name, + AclModuleInitFunc func) +{ + int rv; + + if (!module_name || !*module_name) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4200, ACL_Program, 1, + XP_GetAdminStr(DBT_ModuleRegisterModuleNameMissing)); + return -1; + } + + rv = (*func)(errp); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4210, ACL_Program, 2, + XP_GetAdminStr(DBT_ModuleRegisterFailed), module_name); + return rv; + } + + return 0; +} + + +static int attr_getter_is_matching(NSErr_t *errp, ACLAttrGetter_t *getter, + ACLMethod_t method, ACLDbType_t dbtype) +{ + if ((ACL_MethodIsEqual(errp, getter->method, method) || + ACL_MethodIsEqual(errp, getter->method, ACL_METHOD_ANY)) && + (ACL_DbTypeIsEqual(errp, getter->dbtype, dbtype) || + ACL_DbTypeIsEqual(errp, getter->dbtype, ACL_DBTYPE_ANY))) + { + return 1; + } + else { + return 0; + } +} + + +NSAPI_PUBLIC int ACL_GetAttribute(NSErr_t *errp, const char *attr, void **val, + PList_t subject, PList_t resource, + PList_t auth_info, PList_t global_auth) +{ + int rv; + void *attrval; + ACLAttrGetterFn_t func; + ACLAttrGetterList_t getters; + ACLAttrGetter_t *getter; + ACLMethod_t method; + ACLDbType_t dbtype; + + /* If subject PList is NULL, we will fail anyway */ + if (!subject) return LAS_EVAL_FAIL; + + /* Is the attribute already present in the subject property list? */ + + rv = PListFindValue(subject, attr, &attrval, NULL); + if (rv >= 0) { + + /* Yes, take it from there */ + *val = attrval; + return LAS_EVAL_TRUE; + } + + /* Get the authentication method and database type */ + + rv = ACL_AuthInfoGetMethod(errp, auth_info, &method); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4300, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeCouldntDetermineMethod), attr); + return LAS_EVAL_FAIL; + } + + rv = ACL_AuthInfoGetDbType (errp, auth_info, &dbtype); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4380, ACL_Program, 2, + XP_GetAdminStr(DBT_ReadDbMapFileCouldntDetermineDbtype), attr); + return LAS_EVAL_FAIL; + } + + /* Get the list of attribute getters */ + + rv = ACL_AttrGetterFind(errp, attr, &getters); + + if ((rv < 0) || (getters == 0)) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4310, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeCouldntLocateGetter), attr); + return LAS_EVAL_FAIL; + } + + /* Iterate over each getter and see if it should be called + * Call each matching getter until a getter which doesn't decline is + * found. + */ + + for (getter = ACL_AttrGetterFirst(&getters); + getter != 0; + getter = ACL_AttrGetterNext(&getters, getter)) { + + /* Require matching method and database type */ + + if (attr_getter_is_matching(errp, getter, method, dbtype)) { + + /* Call the getter function */ + func = getter->fn; + rv = (*func)(errp, subject, resource, auth_info, global_auth, + getter->arg); + + /* Did the getter succeed? */ + if (rv == LAS_EVAL_TRUE) { + + /* + * Yes, it should leave the attribute on the subject + * property list. + */ + rv = PListFindValue(subject, attr, (void **)&attrval, NULL); + + if (rv < 0) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4320, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeDidntSetAttr), attr); + return LAS_EVAL_FAIL; + } + + /* Got it */ + *val = attrval; + return LAS_EVAL_TRUE; + } + + /* Did the getter decline? */ + if (rv != LAS_EVAL_DECLINE) { + + /* No, did it fail to get the attribute */ + if (rv == LAS_EVAL_FAIL || rv == LAS_EVAL_INVALID) { + nserrGenerate(errp, ACLERRFAIL, ACLERR4330, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeDidntGetAttr), attr); + } + + return rv; + } + } + } + + /* If we fall out of the loop, all the getters declined */ + nserrGenerate(errp, ACLERRFAIL, ACLERR4340, ACL_Program, 2, + XP_GetAdminStr(DBT_GetAttributeAllGettersDeclined), attr); + return LAS_EVAL_FAIL; +} + diff --git a/lib/libaccess/nsadb.cpp b/lib/libaccess/nsadb.cpp new file mode 100644 index 00000000..a87951b8 --- /dev/null +++ b/lib/libaccess/nsadb.cpp @@ -0,0 +1,582 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsadb.c) + * + * This module contains routines for retrieving information from + * a Netscape authentication database. An authentication database + * consists of a user database and a group database. This module + * implements an authentication database based on Netscape user and + * group databases defined in nsuser.h and nsgroup.h, which in turn + * are based on the Netscape (server) database implementation + * defined in nsdb.h. The interface for managing information in + * an authentication database is described separately in nsamgmt.h. + */ + +#include <base/systems.h> +#include <netsite.h> +#include <base/file.h> +#include <base/fsmutex.h> +#include <libaccess/nsdbmgmt.h> +#define __PRIVATE_NSADB +#include <libaccess/nsadb.h> +#include <libaccess/nsuser.h> +#include <libaccess/nsgroup.h> + +/* + * Description (NSADB_AuthIF) + * + * This structure defines a generic authentication database + * interface for this module. It does not currently support + * user/group id lookup. + */ +AuthIF_t NSADB_AuthIF = { + 0, /* find user/group by id */ + nsadbFindByName, /* find user/group by name */ + nsadbIdToName, /* lookup name for user/group id */ + nsadbOpen, /* open a named database */ + nsadbClose, /* close a database */ +}; + +/* + * Description (nsadbClose) + * + * This function closes an authentication database previously opened + * via nsadbOpen(). + * + * Arguments: + * + * authdb - handle returned by nsadbOpen() + * flags - unused (must be zero) + */ + +NSAPI_PUBLIC void nsadbClose(void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + + if (adb->adb_userdb != 0) { + ndbClose(adb->adb_userdb, 0); + } + + if (adb->adb_groupdb != 0) { + ndbClose(adb->adb_groupdb, 0); + } + +#if defined(CLIENT_AUTH) + nsadbCloseCerts(authdb, flags); +#endif + + if (adb->adb_dbname) { + FREE(adb->adb_dbname); + } + + FREE(adb); +} + +/* + * Description (nsadbOpen) + * + * This function is used to open an authentication database. + * The caller specifies a name for the database, which is actually + * the name of a directory containing the files which comprise the + * database. The caller also indicates whether this is a new + * database, in which case it is created. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * adbname - name of this database (directory) + * flags - open flags: + * AIF_CREATE - new database (create) + * rptr - pointer to returned handle + * + * Returns: + * + * A handle for accessing the database is always returned via 'rptr' + * unless there was a shortage of dynamic memory, in which case a + * null handle is returned. The return value of the function is + * 0 if it completes successfully. An error is indicated by a + * negative return value (see nsautherr.h). + */ + +NSAPI_PUBLIC int nsadbOpen(NSErr_t * errp, + char * adbname, int flags, void **rptr) +{ + AuthDB_t * authdb = 0; /* pointer to database descriptor */ + SYS_DIR dbdir; /* database directory handle */ + int eid; /* error id code */ + int rv; /* result value */ + + /* Make sure we have a place to return the database handle */ + if (rptr == 0) goto err_inval; + + /* Allocate the database descriptor */ + authdb = (AuthDB_t *)MALLOC(sizeof(AuthDB_t)); + if (authdb == 0) goto err_nomem; + + /* Return the descriptor pointer as the database handle */ + *rptr = (void *)authdb; + + authdb->adb_dbname = STRDUP(adbname); + authdb->adb_userdb = 0; + authdb->adb_groupdb = 0; +#if defined(CLIENT_AUTH) + authdb->adb_certdb = 0; + authdb->adb_certlock = 0; + authdb->adb_certnm = 0; +#endif + authdb->adb_flags = 0; + + /* See if the database directory exists */ + dbdir = dir_open(adbname); + if (dbdir == 0) { + /* No, create it if this is a new database, else error */ + if (flags & AIF_CREATE) { + rv = dir_create(adbname); + if (rv < 0) goto err_mkdir; + authdb->adb_flags |= ADBF_NEW; + } + else goto err_dopen; + } + else { + /* Ok, it's there */ + dir_close(dbdir); + } + + return 0; + + err_inval: + eid = NSAUERR3000; + rv = NSAERRINVAL; + goto err_ret; + + err_nomem: + /* Error - insufficient dynamic memory */ + eid = NSAUERR3020; + rv = NSAERRNOMEM; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; + + err_mkdir: + eid = NSAUERR3040; + rv = NSAERRMKDIR; + goto err_dir; + + err_dopen: + eid = NSAUERR3060; + rv = NSAERROPEN; + goto err_dir; + + err_dir: + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, adbname); + goto punt; + + punt: + /* Fatal error - free database descriptor and return null handle */ + if (authdb) { + if (authdb->adb_dbname) { + FREE(authdb->adb_dbname); + } + FREE(authdb); + } + + if (rptr) *rptr = 0; + + return rv; +} + +/* + * Description (nsadbOpenUsers) + * + * This function is called to open the users subdatabase of an + * open authentication database. The caller specifies flags to + * indicate whether read or write access is required. This + * function is normally called only by routines below the + * nsadbOpen() API, in response to perform particular operations + * on user or group objects. If the open is successful, the + * resulting handle is stored in the AuthDB_t structure. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * flags - open flags: + * ADBF_UREAD - open for read + * ADBF_UWRITE - open for read/write + * Returns: + * + * The return value is zero if the operation is successfully + * completed. An error is indicated by a negative return value + * (see nsautherr.h), and an error frame is generated if an error + * frame list was provided. + */ + +NSAPI_PUBLIC int nsadbOpenUsers(NSErr_t * errp, void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + char * userfn = 0; /* user database name */ + int dblen; /* strlen(adb_dbname) */ + int uversion; /* user database version number */ + int eid; /* error id code */ + int rv; /* result value */ + + if (adb == 0) goto err_inval; + + /* Is the user database already open? */ + if (adb->adb_userdb != 0) { + + /* Yes, is it open for the desired access? */ + if (adb->adb_flags & flags) { + + /* Yes, that was easy */ + return 0; + } + } + else { + + /* We need to open the database */ + + /* Allocate space for the user database filename */ + dblen = strlen(adb->adb_dbname); + + userfn = (char *)MALLOC(dblen + strlen(ADBUSERDBNAME) + 2); + if (userfn == 0) goto err_nomem; + + /* Construct user database name */ + strcpy(userfn, adb->adb_dbname); + + /* Put in a '/' (or '\') if it's not there */ + if (userfn[dblen-1] != FILE_PATHSEP) { + userfn[dblen] = FILE_PATHSEP; + userfn[dblen+1] = 0; + ++dblen; + } + + strcpy(&userfn[dblen], ADBUSERDBNAME); + + adb->adb_userdb = ndbOpen(errp, + userfn, 0, NDB_TYPE_USERDB, &uversion); + if (adb->adb_userdb == 0) goto err_uopen; + + FREE(userfn); + } + + /* + * We don't really reopen the database to get the desired + * access mode, since that is handled at the nsdb level. + * But we do update the flags, just for the record. + */ + adb->adb_flags &= ~(ADBF_UREAD|ADBF_UWRITE); + if (flags & ADBF_UWRITE) adb->adb_flags |= ADBF_UWRITE; + else adb->adb_flags |= ADBF_UREAD; + + return 0; + + err_inval: + eid = NSAUERR3200; + rv = NSAERRINVAL; + goto err_ret; + + err_nomem: + eid = NSAUERR3220; + rv = NSAERRNOMEM; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; + + err_uopen: + eid = NSAUERR3240; + rv = NSAERROPEN; + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, userfn); + goto punt; + + punt: + return rv; +} + +/* + * Description (nsadbOpenGroups) + * + * This function is called to open the groups subdatabase of an + * open authentication database. The caller specifies flags to + * indicate whether read or write access is required. This + * function is normally called only by routines below the + * nsadbOpen() API, in response to perform particular operations + * on user or group objects. If the open is successful, the + * resulting handle is stored in the AuthDB_t structure. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * flags - open flags: + * ADBF_GREAD - open for read + * ADBF_GWRITE - open for read/write + * Returns: + * + * The return value is zero if the operation is successfully + * completed. An error is indicated by a negative return value + * (see nsautherr.h), and an error frame is generated if an error + * frame list was provided. + */ + +NSAPI_PUBLIC int nsadbOpenGroups(NSErr_t * errp, void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + char * groupfn = 0; /* group database name */ + int dblen; /* strlen(adb_dbname) */ + int gversion; /* group database version number */ + int eid; /* error id code */ + int rv; /* result value */ + + if (adb == 0) goto err_inval; + + /* Is the group database already open? */ + if (adb->adb_groupdb != 0) { + + /* Yes, is it open for the desired access? */ + if (adb->adb_flags & flags) { + + /* Yes, that was easy */ + return 0; + } + } + else { + + /* We need to open the database */ + + /* Allocate space for the group database filename */ + dblen = strlen(adb->adb_dbname); + + groupfn = (char *)MALLOC(dblen + strlen(ADBGROUPDBNAME) + 2); + if (groupfn == 0) goto err_nomem; + + /* Construct group database name */ + strcpy(groupfn, adb->adb_dbname); + + /* Put in a '/' (or '\') if it's not there */ + if (groupfn[dblen-1] != FILE_PATHSEP) { + groupfn[dblen] = FILE_PATHSEP; + groupfn[dblen+1] = 0; + ++dblen; + } + + strcpy(&groupfn[dblen], ADBGROUPDBNAME); + + adb->adb_groupdb = ndbOpen(errp, + groupfn, 0, NDB_TYPE_GROUPDB, &gversion); + if (adb->adb_groupdb == 0) goto err_gopen; + + FREE(groupfn); + } + + /* + * We don't really reopen the database to get the desired + * access mode, since that is handled at the nsdb level. + * But we do update the flags, just for the record. + */ + adb->adb_flags &= ~(ADBF_GREAD|ADBF_GWRITE); + if (flags & ADBF_GWRITE) adb->adb_flags |= ADBF_GWRITE; + else adb->adb_flags |= ADBF_GREAD; + + return 0; + + err_inval: + eid = NSAUERR3300; + rv = NSAERRINVAL; + goto err_ret; + + err_nomem: + eid = NSAUERR3320; + rv = NSAERRNOMEM; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; + + err_gopen: + eid = NSAUERR3340; + rv = NSAERROPEN; + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, groupfn); + goto punt; + + punt: + return rv; +} + +/* + * Description (nsadbIdToName) + * + * This function looks up a specified user or group id in the + * authentication database. The name associated with the specified + * id is returned. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * id - user or group id + * flags - AIF_USER or AIF_GROUP (defined in nsauth.h) + * rptr - pointer to returned group or user name + * + * Returns: + * + * The return value is zero if no error occurs, + * A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbIdToName(NSErr_t * errp, + void * authdb, USI_t id, int flags, char **rptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + void * whichdb = 0; + char * name; + int rv; + + if (rptr != 0) *rptr = 0; + + /* Decide whether to use user or group database */ + if (flags & AIF_USER) { + + whichdb = adb->adb_userdb; + if (whichdb == 0) { + rv = nsadbOpenUsers(errp, authdb, ADBF_UREAD); + if (rv < 0) goto punt; + whichdb = adb->adb_userdb; + } + } + else if (flags & AIF_GROUP) { + + whichdb = adb->adb_groupdb; + if (whichdb == 0) { + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + whichdb = adb->adb_groupdb; + } + } + + if (whichdb != 0) { + + /* Get the name corresponding to the id */ + rv = ndbIdToName(errp, whichdb, id, 0, &name); + if (rv < 0) goto punt; + + if ((rptr != 0)) *rptr = name; + rv = 0; + } + + punt: + return rv; +} + +/* + * Description (nsadbFindByName) + * + * This function looks up a specified name in the authentication + * database. Flags specified by the caller indicate whether a + * group name, user name, or either should be found. The caller + * may optionally provide for the return of a user or group object + * pointer, in which case the information associated with a + * matching group or user is used to create a group or user object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * name - name of group or user + * flags - search flags (defined in nsauth.h) + * rptr - pointer to returned group or user + * object pointer (may be null) + * + * Returns: + * + * The return value is a non-negative value if no error occurs, + * and the value indicates whether the name matched a group or + * user: + * + * AIF_NONE - name did not match a group or user name + * AIF_GROUP - name matched a group name + * AIF_USER - name matched a user name + * + * If the value is AIF_GROUP or AIF_USER, and rptr is non-null, + * then a group or user object is created, and a pointer to it is + * returned in the location indicated by rptr. + * + * A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbFindByName(NSErr_t * errp, void * authdb, + char * name, int flags, void **rptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + ATR_t recptr; + int reclen; + int rv; + + if (rptr != 0) *rptr = 0; + + /* Search for group name? */ + if (flags & AIF_GROUP) { + + if (adb->adb_groupdb == 0) { + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + } + + /* Look up the name in the group database */ + rv = ndbFindName(errp, adb->adb_groupdb, 0, (char *)name, + &reclen, (char **)&recptr); + if (rv == 0) { + + /* Found it. Make a group object if requested. */ + if (rptr != 0) { + + /* Got the group record. Decode into a group object. */ + *rptr = (void *)groupDecode((NTS_t)name, reclen, recptr); + } + + return AIF_GROUP; + } + } + + /* Search for user name? */ + if (flags & AIF_USER) { + + if (adb->adb_userdb == 0) { + rv = nsadbOpenUsers(errp, authdb, ADBF_UREAD); + if (rv < 0) goto punt; + } + + /* Look up the name in the user database */ + rv = ndbFindName(errp, adb->adb_userdb, 0, (char *)name, + &reclen, (char **)&recptr); + if (rv == 0) { + + /* Found it. Make a user object if requested. */ + if (rptr != 0) { + + /* Got the user record. Decode into a user object. */ + *rptr = (void *)userDecode((NTS_t)name, reclen, recptr); + } + + return AIF_USER; + } + } + + /* Nothing found */ + nserrDispose(errp); + return AIF_NONE; + + punt: + return rv; +} diff --git a/lib/libaccess/nsamgmt.cpp b/lib/libaccess/nsamgmt.cpp new file mode 100644 index 00000000..544d3617 --- /dev/null +++ b/lib/libaccess/nsamgmt.cpp @@ -0,0 +1,1567 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsamgmt.c) + * + * This module contains routines for managing information in a + * Netscape authentication database. An authentication database + * consists of a user database and a group database. This module + * implements an authentication database based on Netscape user and + * group databases defined in nsuser.h and nsgroup.h, which in turn + * are based on the Netscape (server) database implementation + * defined in nsdb.h. The interface for retrieving information + * from an authentication database is described separately in + * nsadb.h. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "base/file.h" +#define __PRIVATE_NSADB +#include "libaccess/nsamgmt.h" +#include "libaccess/nsumgmt.h" +#include "libaccess/nsgmgmt.h" + +/* + * Description (nsadbEnumUsersHelp) + * + * This is a local function that is called by NSDB during user + * database enumeration. It decodes user records into user + * objects, and presents them to the caller of nsadbEnumerateUsers(), + * via the specified call-back function. The call-back function + * return value may be a negative error code, which will cause + * enumeration to stop, and the error code will be returned from + * nsadbEnumerateUsers(). If the return value of the call-back + * function is not negative, it can contain one or more of the + * following flags: + * + * ADBF_KEEPOBJ - do not free the UserObj_t structure + * that was passed to the call-back function + * ADBF_STOPENUM - stop the enumeration without an error + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to UserEnumArgs_t structure + * namelen - user record key length including null + * terminator + * name - user record key (user account name) + * reclen - length of user record + * recptr - pointer to user record contents + * + * Returns: + * + * If the call-back returns a negative result, that value is + * returned. If the call-back returns ADBF_STOPENUM, then + * -1 is returned, causing the enumeration to stop. Otherwise + * the return value is zero. + */ + +typedef struct EnumUserArgs_s EnumUserArgs_t; +struct EnumUserArgs_s { + void * authdb; + int (*func)(NSErr_t * ferrp, + void * authdb, void * argp, UserObj_t * uoptr); + void * user; + int rv; +}; + +static int nsadbEnumUsersHelp(NSErr_t * errp, void * parg, + int namelen, char * name, + int reclen, char * recptr) +{ + EnumUserArgs_t * ue = (EnumUserArgs_t *)parg; + UserObj_t * uoptr; /* user object pointer */ + int rv; + + uoptr = userDecode((NTS_t)name, reclen, (ATR_t)recptr); + if (uoptr != 0) { + rv = (*ue->func)(errp, ue->authdb, ue->user, uoptr); + if (rv >= 0) { + + /* Count the number of users seen */ + ue->rv += 1; + + /* Free the user object unless the call-back says not to */ + if (!(rv & ADBF_KEEPOBJ)) { + userFree(uoptr); + } + /* Return either 0 or -1, depending on ADBF_STOPENUM */ + rv = (rv & ADBF_STOPENUM) ? -1 : 0; + } + else { + /* Free the user object in the event of an error */ + userFree(uoptr); + + /* Also return the error code */ + ue->rv = rv; + } + } + + return rv; +} + +/* + * Description (nsadbEnumGroupsHelp) + * + * This is a local function that is called by NSDB during group + * database enumeration. It decodes group records into group + * objects, and presents them to the caller of nsadbEnumerateGroups(), + * via the specified call-back function. The call-back function + * return value may be a negative error code, which will cause + * enumeration to stop, and the error code will be returned from + * nsadbEnumerateGroups(). If the return value of the call-back + * function is not negative, it can contain one or more of the + * following flags: + * + * ADBF_KEEPOBJ - do not free the GroupObj_t structure + * that was passed to the call-back function + * ADBF_STOPENUM - stop the enumeration without an error + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to GroupEnumArgs_t structure + * namelen - group record key length including null + * terminator + * name - group record key (group name) + * reclen - length of group record + * recptr - pointer to group record contents + * + * Returns: + * + * If the call-back returns a negative result, that value is + * returned. If the call-back returns ADBF_STOPENUM, then + * -1 is returned, causing the enumeration to stop. Otherwise + * the return value is zero. + */ + +typedef struct EnumGroupArgs_s EnumGroupArgs_t; +struct EnumGroupArgs_s { + void * authdb; + int (*func)(NSErr_t * ferrp, + void * authdb, void * argp, GroupObj_t * goptr); + void * user; + int rv; +}; + +static int nsadbEnumGroupsHelp(NSErr_t * errp, void * parg, + int namelen, char * name, + int reclen, char * recptr) +{ + EnumGroupArgs_t * eg = (EnumGroupArgs_t *)parg; + GroupObj_t * goptr; /* group object pointer */ + int rv; + + goptr = groupDecode((NTS_t)name, reclen, (ATR_t)recptr); + if (goptr != 0) { + rv = (*eg->func)(errp, eg->authdb, eg->user, goptr); + if (rv >= 0) { + + /* Count the number of groups seen */ + eg->rv += 1; + + /* Free the group object unless the call-back says not to */ + if (!(rv & ADBF_KEEPOBJ)) { + groupFree(goptr); + } + /* Return either 0 or -1, depending on ADBF_STOPENUM */ + rv = (rv & ADBF_STOPENUM) ? -1 : 0; + } + else { + /* Free the group object in the event of an error */ + groupFree(goptr); + + /* Also return the error code */ + eg->rv = rv; + } + } + + return rv; +} + +NSPR_BEGIN_EXTERN_C + +/* + * Description (nsadbAddGroupToGroup) + * + * This function adds a child group, C, to the definition of a + * parent group P. This involves updating the group entries of + * C and P in the group database. It also involves updating + * the group lists of any user descendants of C, to reflect the + * fact that these users are now members of P and P's ancestors. + * A check is made for an attempt to create a cycle in the group + * hierarchy, and this is rejected as an error. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * pgoptr - pointer to parent group object + * cgoptr - pointer to child group object + * + * Returns: + * + * The return value is zero if group C was not already a direct + * member of group P, and was added successfully. A return value + * of +1 indicates that group C was already a direct member of + * group P. A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbAddGroupToGroup(NSErr_t * errp, void * authdb, + GroupObj_t * pgoptr, GroupObj_t * cgoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t gsuper; /* list of ancestors of group P */ + USIList_t dglist; /* descendant groups of C */ + GroupObj_t * dgoptr; /* descendant group object pointer */ + UserObj_t * uoptr; /* user object pointer */ + USI_t id; /* current descendant group id */ + int usercount; /* count of users for descendant */ + USI_t * userlist; /* pointer to array of descendant user ids */ + USI_t * idlist; /* pointer to array of descendant group ids */ + int pass; /* loop pass number */ + int i; /* loop index */ + int rv; /* result value */ + + /* Is C a direct member of P already? */ + if (usiPresent(&pgoptr->go_groups, cgoptr->go_gid)) { + /* Yes, indicate that */ + return 0; + } + + dgoptr = 0; + uoptr = 0; + + /* Initialize a list of the group descendants of group C */ + UILINIT(&dglist); + + /* Initialize a list of P and its ancestors */ + UILINIT(&gsuper); + + /* Add P to the ancestor list */ + rv = usiInsert(&gsuper, pgoptr->go_gid); + if (rv < 0) goto punt; + + /* Open user database since the group lists of users may be modified */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Open group database since group entries will be modified */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* Merge all the ancestors of group P into the list */ + rv = nsadbSuperGroups(errp, authdb, pgoptr, &gsuper); + if (rv < 0) goto punt; + + /* + * Each pass through the following loop visits C and all of C's + * descendant groups. + * + * The first pass checks to see if making group C a member of + * group P would create a cycle in the group structure. It does + * this by examining C and all of its dependents to see if any + * appear in the list containing P and P's ancestors. + * + * The second pass updates the group lists of all users contained + * in group C to include P and P's ancestors. + */ + + for (pass = 1; pass < 3; ++pass) { + + /* Use the group C as the first descendant */ + id = cgoptr->go_gid; + dgoptr = cgoptr; + + for (;;) { + + if (pass == 1) { + /* + * Check for attempt to create a cycle in the group + * hierarchy. See if this descendant is a member of + * the list of P and P's ancestors (gsuper). + */ + if (usiPresent(&gsuper, id)) { + /* + * Error - operation would create a cycle + * in the group structure. + */ + return -1; + } + } + else { + + /* + * Merge the list of ancestors of P (gsuper) with the + * group lists of any direct user members of the current + * descendant group, referenced by dgoptr. + */ + + /* Get direct user member list size and pointer */ + usercount = UILCOUNT(&dgoptr->go_users); + userlist = UILLIST(&dgoptr->go_users); + + /* For each direct user member of this descendant ... */ + for (i = 0; i < usercount; ++i) { + + /* Get a user object for the user */ + uoptr = userFindByUid(errp, + adb->adb_userdb, userlist[i]); + if (uoptr == 0) { + /* + * Error - user not found, + * databases are inconsistent. + */ + rv = -1; + goto punt; + } + + /* Merge gsuper into the user's group list */ + rv = uilMerge(&uoptr->uo_groups, &gsuper); + if (rv < 0) goto punt; + + /* Write out the user object */ + uoptr->uo_flags |= UOF_MODIFIED; + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + if (rv) goto punt; + + /* Free the user object */ + userFree(uoptr); + uoptr = 0; + } + } + + /* + * Merge the direct member groups of the current descendant + * group into the list of descendants to be processed. + */ + rv = uilMerge(&dglist, &dgoptr->go_groups); + if (rv < 0) goto punt; + + /* Free the group object for the current descendant */ + if (dgoptr != cgoptr) { + groupFree(dgoptr); + dgoptr = 0; + } + + /* Exit the loop if the descendant list is empty */ + if (UILCOUNT(&dglist) <= 0) break; + + /* Otherwise remove the next descendant from the list */ + idlist = UILLIST(&dglist); + id = idlist[0]; + rv = usiRemove(&dglist, id); + if (rv < 0) goto punt; + + /* Now get a group object for this descendant group */ + dgoptr = groupFindByGid(errp, adb->adb_groupdb, id); + if (dgoptr == 0) { + /* Error - group not found, databases are inconsistent */ + rv = -1; + goto punt; + } + } + } + + /* Now add C to P's list of member groups */ + rv = usiInsert(&pgoptr->go_groups, cgoptr->go_gid); + if (rv < 0) goto punt; + + /* Add P to C's list of parent groups */ + rv = usiInsert(&cgoptr->go_pgroups, pgoptr->go_gid); + if (rv < 0) goto punt; + + /* Update the database entry for group C */ + cgoptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, cgoptr); + if (rv) goto punt; + + /* Update the database entry for group P */ + pgoptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, pgoptr); + + return rv; + + punt: + /* Handle errors */ + UILFREE(&gsuper); + UILFREE(&dglist); + if (dgoptr) { + groupFree(dgoptr); + } + if (uoptr) { + userFree(uoptr); + } + return rv; +} + +/* + * Description (nsadbAddUserToGroup) + * + * This function adds a user to a group definition. This involves + * updating the group entry in the group database, and the user + * entry in the user database. The caller provides a pointer to + * a user object for the user to be added, a pointer to a group + * object for the group being modified, and a handle for the + * authentication databases (from nsadbOpen()). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to group object + * uoptr - pointer to user object + * + * Returns: + * + * The return value is zero if the user was not already a direct + * member of the group, and was added successfully. A return value + * of +1 indicates that the user was already a direct member of the + * group. A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbAddUserToGroup(NSErr_t * errp, void * authdb, + GroupObj_t * goptr, UserObj_t * uoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t nglist; /* new group list for specified user */ + USIList_t gsuper; /* groups containing+ the specified group */ + GroupObj_t * aoptr; /* group object for 'id' group */ + USI_t * idlist; /* pointer to gsuper gid array */ + USI_t id; /* current gid from gsuper */ + int rv; /* result value */ + + /* Is the user already a direct member of the group? */ + if (usiPresent(&goptr->go_users, uoptr->uo_uid)) { + + /* Yes, nothing to do */ + return 1; + } + + /* + * The user object contains a list of all of the groups that contain + * the user, either directly or indirectly. We need to add the + * specified group and its ancestors to this list. Each group contains + * a list of the group's parents, which is used to locate all of the + * group's ancestors. As an optimization, we need not consider any + * ancestors which are already on the user's current group list. + */ + + /* + * The following loop will deal with two lists of group ids. One + * is the list that will become the new group list for the user, + * which is initialized to the user's current group list. The other + * is a list of ancestors of the group to be considered for addition + * to the user's group list. This list is initialized to the specified + * group. + */ + + /* Initialize both lists to be empty */ + UILINIT(&nglist); + UILINIT(&gsuper); + + /* Make a copy of the user's current group list */ + rv = uilDuplicate(&nglist, &uoptr->uo_groups); + if (rv < 0) goto punt; + + /* Start the other list with the specified group */ + rv = usiInsert(&gsuper, goptr->go_gid); + if (rv < 0) goto punt; + + /* Open user database since the group lists of users may be modified */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Open group database since group entries will be modified */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* While entries remain on the ancestor list */ + while (UILCOUNT(&gsuper) > 0) { + + /* Get pointer to array of ancestor group ids */ + idlist = UILLIST(&gsuper); + + /* Remove the first ancestor */ + id = idlist[0]; + usiRemove(&gsuper, id); + + /* Is the ancestor on the user's current group list? */ + if (!usiPresent(&uoptr->uo_groups, id)) { + + /* No, add its parents to the ancestor list */ + + /* Look up the ancestor group (get a group object for it) */ + aoptr = groupFindByGid(errp, adb->adb_groupdb, id); + if (aoptr == 0) { + /* Error - group not found, database inconsistent */ + rv = -1; + goto punt; + } + + /* Merge the ancestors parents into the ancestor list */ + rv = uilMerge(&gsuper, &aoptr->go_pgroups); + + /* Lose the ancestor group object */ + groupFree(aoptr); + + /* See if the merge worked */ + if (rv < 0) goto punt; + } + + /* Add the ancestor to the new group list for the user */ + rv = usiInsert(&nglist, id); + if (rv < 0) goto punt; + } + + /* Add the user to the group's user member list */ + rv = usiInsert(&goptr->go_users, uoptr->uo_uid); + if (rv < 0) goto punt; + + /* Replace the user's group list with the new one */ + UILREPLACE(&uoptr->uo_groups, &nglist); + + /* Write out the updated user object */ + uoptr->uo_flags |= UOF_MODIFIED; + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + if (rv < 0) goto punt; + + /* Write out the updated group object */ + goptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, goptr); + + return rv; + + punt: + /* Handle error */ + + /* Free ancestor and new group lists */ + UILFREE(&nglist); + UILFREE(&gsuper); + + return rv; +} + +/* + * Description (nsadbCreateGroup) + * + * This function creates a new group in a specified authentication + * database. The group is described by a group object. A group + * object can be created by calling nsadbGroupNew(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to group object + * + * Returns: + */ + +NSAPI_PUBLIC int nsadbCreateGroup(NSErr_t * errp, void * authdb, GroupObj_t * goptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + int rv; + + /* Open the group database for write access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* Add this group to the database */ + rv = groupStore(errp, adb->adb_groupdb, 0, goptr); + + punt: + return rv; +} + +/* + * Description (nsadbCreateUser) + * + * This function creates a new user in a specified authentication + * database. The user is described by a user object. A user + * object can be created by calling nsadbUserNew(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * uoptr - pointer to user object + * + * Returns: + */ + +NSAPI_PUBLIC int nsadbCreateUser(NSErr_t * errp, void * authdb, UserObj_t * uoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + int rv; + + /* Open the user database for write access */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Add this user to the database */ + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + + punt: + return rv; +} + +/* + * Description (nsadbEnumerateUsers) + * + * This function is called to enumerate all of the users in a + * given authentication database to a call-back function specified + * by the caller. The call-back function is provided with a + * handle for the authentication database, an opaque value provided + * by the caller, and a pointer to a user object. See the + * description of nsadbEnumUsersHelp above for the interpretation + * of the call-back function's return value. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * argp - opaque value for call-back function + * func - pointer to call-back function + * + * Returns: + * + * If the call-back function returns a negative error code, this + * value is returned. A negative value may also be returned if + * nsadb encounters an error. Otherwise the result is the number + * of users enumerated. + */ + +NSAPI_PUBLIC int nsadbEnumerateUsers(NSErr_t * errp, void * authdb, void * argp, +#ifdef UnixWare + ArgFn_EnumUsers func) /* for ANSI C++ standard, see nsamgmt.h */ +#else + int (*func)(NSErr_t * ferrp, void * authdb, void * parg, UserObj_t * uoptr)) +#endif +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + EnumUserArgs_t args; /* arguments for enumeration helper */ + int rv; /* result value */ + + /* Open the users subdatabase for read access */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UREAD); + if (rv < 0) goto punt; + + args.authdb = authdb; + args.func = func; + args.user = argp; + args.rv = 0; + + rv = ndbEnumerate(errp, adb->adb_userdb, + NDBF_ENUMNORM, (void *)&args, nsadbEnumUsersHelp); + if (rv < 0) goto punt; + + rv = args.rv; + + punt: + return rv; +} + +/* + * Description (nsadbEnumerateGroups) + * + * This function is called to enumerate all of the groups in a + * given authentication database to a call-back function specified + * by the caller. The call-back function is provided with a + * handle for the authentication database, an opaque value provided + * by the caller, and a pointer to a group object. See the + * description of nsadbEnumGroupsHelp above for the interpretation + * of the call-back function's return value. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * argp - opaque value for call-back function + * func - pointer to call-back function + * + * Returns: + * + * If the call-back function returns a negative error code, this + * value is returned. A negative value may also be returned if + * nsadb encounters an error. Otherwise the result is the number + * of groups enumerated. + */ + +NSAPI_PUBLIC int nsadbEnumerateGroups(NSErr_t * errp, void * authdb, void * argp, +#ifdef UnixWare + ArgFn_EnumGroups func) /* for ANSI C++ standard, see nsamgmt.h */ +#else + int (*func)(NSErr_t * ferrp, void * authdb, void * parg, GroupObj_t * goptr)) +#endif +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + EnumGroupArgs_t args; + int rv; /* result value */ + + /* Open group database for read access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + + args.authdb = authdb; + args.func = func; + args.user = argp; + args.rv = 0; + + rv = ndbEnumerate(errp, adb->adb_groupdb, + NDBF_ENUMNORM, (void *)&args, nsadbEnumGroupsHelp); + if (rv < 0) goto punt; + + rv = args.rv; + + punt: + return rv; +} + +/* + * Description (nsadbIsUserInGroup) + * + * This function tests whether a given user id is a member of the + * group associated with a specified group id. The caller may + * provide a list of group ids for groups to which the user is + * already known to belong, and this may speed up the check. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * uid - user id + * gid - group id + * ngroups - number of group ids in grplist + * grplist - groups the user is known to belong to + * + * Returns: + * + * The return value is +1 if the user is found to belong to the + * indicated group, or 0 if the user does not belong to the group. + * An error is indicated by a negative return value. + */ + +NSAPI_PUBLIC int nsadbIsUserInGroup(NSErr_t * errp, void * authdb, + USI_t uid, USI_t gid, int ngroups, USI_t * grplist) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t dglist; /* descendant group list */ + GroupObj_t * goptr = 0; /* group object pointer */ + USI_t * idlist; /* pointer to array of group ids */ + USI_t tgid; /* test group id */ + int i; /* loop index */ + int rv; /* result value */ + + UILINIT(&dglist); + + /* Open group database for read access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + + for (tgid = gid;;) { + + /* Get a group object for this group id */ + goptr = groupFindByGid(errp, adb->adb_groupdb, tgid); + if (goptr == 0) { + /* Error - group id not found, databases are inconsistent */ + rv = -1; + goto punt; + } + + /* Is the user a direct member of this group? */ + if (usiPresent(&goptr->go_users, uid)) goto is_member; + + /* + * Is there any group to which the user is already known to + * belong that is a direct group member of this group? If so, + * the user is also a member of this group. + */ + + /* Scan list of groups to which the user is known to belong */ + for (i = 0; i < ngroups; ++i) { + + if (usiPresent(&goptr->go_groups, grplist[i])) goto is_member; + } + + /* Merge group member list of this group with descendants list */ + rv = uilMerge(&dglist, &goptr->go_groups); + if (rv < 0) goto punt; + + /* + * If descendants list is empty, the user is not contained in + * the specified group. + */ + if (UILCOUNT(&dglist) <= 0) { + rv = 0; + goto punt; + } + + /* Remove the next id from the descendants list */ + idlist = UILLIST(&dglist); + tgid = idlist[0]; + + rv = usiRemove(&dglist, tgid); + if (rv < 0) goto punt; + + groupFree(goptr); + goptr = 0; + } + + is_member: + rv = 1; + + punt: + if (goptr) { + groupFree(goptr); + } + UILFREE(&dglist); + return rv; +} + +/* + * Description (nsadbModifyGroup) + * + * This function is called to write modifications to a group to + * a specified authentication database. The group is assumed to + * already exist in the database. Information about the group + * is passed in a group object. This function should not be used + * to alter the lists of group members or parents. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to modified group object + * + * Returns: + * + * The return value is zero if the group information is successfully + * updated. An error is indicated by a negative return value, and + * an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbModifyGroup(NSErr_t * errp, void * authdb, GroupObj_t * goptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + int rv; + + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + rv = groupStore(errp, adb->adb_groupdb, 0, goptr); + + punt: + return rv; +} + +/* + * Description (nsadbModifyUser) + * + * This function is called to write modifications to a user to + * a specified authentication database. The user is assumed to + * already exist in the database. Information about the user + * is passed in a user object. This function should not be used + * to modify the list of groups which contain the user. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * uoptr - pointer to modified user object + * + * Returns: + * + * The return value is zero if the user information is successfully + * updated. An error is indicated by a negative return value, and + * an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbModifyUser(NSErr_t * errp, void * authdb, UserObj_t * uoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + int rv; + + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + + punt: + return rv; +} + +/* + * Description (nsadbRemoveGroup) + * + * This function is called to remove a given group name from + * a specified authentication database. This can cause updates + * to both the user and group subdatabases. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * name - pointer to name of group to remove + * + * Returns: + * + * The return value is zero if the group information is successfully + * removed. An error is indicated by a negative return value, and + * an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbRemoveGroup(NSErr_t * errp, void * authdb, char * name) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + UserObj_t * uoptr = 0; /* user object pointer */ + GroupObj_t * goptr = 0; /* group object pointer */ + GroupObj_t * ogoptr = 0; /* other group object pointer */ + char * ugname; /* user or group name */ + USI_t * list; /* pointer into user/group id list */ + int cnt; /* count of user or group ids */ + int i; /* loop index */ + int eid; /* error id code */ + int rv; /* result value */ + + /* Open the groups subdatabase for write access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* Look up the group to be removed, and get a group object */ + rv = nsadbFindByName(errp, authdb, name, AIF_GROUP, (void **)&goptr); + if (rv != AIF_GROUP) { + if (rv < 0) goto punt; + goto err_nogroup; + } + + /* Mark the group for delete */ + goptr->go_flags |= GOF_DELPEND; + + /* Does the specified group belong to any groups? */ + cnt = UILCOUNT(&goptr->go_pgroups); + if (cnt > 0) { + + /* Yes, for each parent group ... */ + for (i = 0; i < cnt; ++i) { + + /* Note that nsadbRemGroupFromGroup() will shrink this list */ + list = UILLIST(&goptr->go_pgroups); + + /* Get group name associated with the group id */ + rv = nsadbIdToName(errp, authdb, *list, AIF_GROUP, &ugname); + if (rv < 0) goto punt; + + /* Look up the group by name and get a group object for it */ + rv = nsadbFindByName(errp, + authdb, ugname, AIF_GROUP, (void **)&ogoptr); + if (rv < 0) goto punt; + + /* Remove the specified group from the parent group */ + rv = nsadbRemGroupFromGroup(errp, authdb, ogoptr, goptr); + if (rv < 0) goto punt; + + /* Free the parent group object */ + groupFree(ogoptr); + ogoptr = 0; + } + } + + /* Are there any group members of this group? */ + cnt = UILCOUNT(&goptr->go_groups); + if (cnt > 0) { + + /* For each group member of the group ... */ + + for (i = 0; i < cnt; ++i) { + + /* Note that nsadbRemGroupFromGroup() will shrink this list */ + list = UILLIST(&goptr->go_groups); + + /* Get group name associated with the group id */ + rv = nsadbIdToName(errp, authdb, *list, AIF_GROUP, &ugname); + if (rv < 0) goto punt; + + /* Look up the group by name and get a group object for it */ + rv = nsadbFindByName(errp, + authdb, ugname, AIF_GROUP, (void **)&ogoptr); + if (rv < 0) goto punt; + + /* Remove member group from the specified group */ + rv = nsadbRemGroupFromGroup(errp, authdb, goptr, ogoptr); + if (rv < 0) goto punt; + + /* Free the member group object */ + groupFree(ogoptr); + ogoptr = 0; + } + } + + /* Are there any direct user members of this group? */ + cnt = UILCOUNT(&goptr->go_users); + if (cnt > 0) { + + /* Yes, open users subdatabase for write access */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* For each user member of the group ... */ + for (i = 0; i < cnt; ++i) { + + /* Note that nsadbRemUserFromGroup() will shrink this list */ + list = UILLIST(&goptr->go_users); + + /* Get user name associated with the user id */ + rv = nsadbIdToName(errp, authdb, *list, AIF_USER, &ugname); + if (rv < 0) goto punt; + + /* Look up the user by name and get a user object for it */ + rv = nsadbFindByName(errp, + authdb, ugname, AIF_USER, (void **)&uoptr); + if (rv < 0) goto punt; + + /* Remove user from the group */ + rv = nsadbRemUserFromGroup(errp, authdb, goptr, uoptr); + if (rv < 0) goto punt; + + /* Free the member user object */ + userFree(uoptr); + uoptr = 0; + } + } + + /* Free the group object for the specified group */ + groupFree(goptr); + goptr = 0; + + /* Now we can remove the group entry */ + rv = groupRemove(errp, adb->adb_groupdb, 0, (NTS_t)name); + + return rv; + + err_nogroup: + eid = NSAUERR4100; + rv = NSAERRNAME; + nserrGenerate(errp, rv, eid, NSAuth_Program, 2, adb->adb_dbname, name); + goto punt; + + punt: + /* Free any user or group objects that we created */ + if (ogoptr != 0) { + groupFree(ogoptr); + } + if (uoptr != 0) { + userFree(uoptr); + } + if (goptr != 0) { + groupFree(goptr); + } + return rv; +} + +/* + * Description (nsadbRemoveUser) + * + * This function is called to remove a given user name from + * a specified authentication database. This can cause updates + * to both the user and user subdatabases. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * name - pointer to name of user to remove + * + * Returns: + * + * The return value is zero if the user information is successfully + * removed. An error is indicated by a negative return value, and + * an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbRemoveUser(NSErr_t * errp, void * authdb, char * name) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + UserObj_t * uoptr = 0; /* user object pointer */ + GroupObj_t * goptr = 0; /* group object pointer */ + char * gname; /* group name */ + USI_t * list; /* pointer into group id list */ + int gcnt; /* number of groups containing user */ + int i; /* loop index */ + int eid; /* error id code */ + int rv; /* result value */ + + /* Open the users subdatabase for write access */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Look up the user to be removed, and get a user object */ + rv = nsadbFindByName(errp, authdb, name, AIF_USER, (void **)&uoptr); + if (rv != AIF_USER) { + if (rv < 0) goto punt; + goto err_nouser; + } + + /* Mark the user for delete */ + uoptr->uo_flags |= UOF_DELPEND; + + /* Does this user belong to any groups? */ + gcnt = UILCOUNT(&uoptr->uo_groups); + if (gcnt > 0) { + + /* Yes, get pointer to list of group ids */ + list = UILLIST(&uoptr->uo_groups); + + /* Open groups subdatabase for write access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* For each group that the user belongs to ... */ + for (i = 0; i < gcnt; ++i) { + + /* Get group name associated with the group id */ + rv = nsadbIdToName(errp, authdb, *list, AIF_GROUP, &gname); + if (rv < 0) goto punt; + + /* Look up the group by name and get a group object for it */ + rv = nsadbFindByName(errp, + authdb, gname, AIF_GROUP, (void **)&goptr); + if (rv < 0) goto punt; + + /* Remove user from group if it's a direct member */ + rv = nsadbRemUserFromGroup(errp, authdb, goptr, uoptr); + if (rv < 0) goto punt; + + /* Free the group object */ + groupFree(goptr); + goptr = 0; + + ++list; + } + } + +#ifdef CLIENT_AUTH + /* Remove certificate mapping for user, if any */ + rv = nsadbRemoveUserCert(errp, authdb, name); +#endif + + /* Free the user object */ + userFree(uoptr); + + /* Now we can remove the user entry */ + rv = userRemove(errp, adb->adb_userdb, 0, (NTS_t)name); + + return rv; + + err_nouser: + eid = NSAUERR4000; + rv = NSAERRNAME; + nserrGenerate(errp, rv, eid, NSAuth_Program, 2, adb->adb_dbname, name); + goto punt; + + punt: + if (goptr != 0) { + groupFree(goptr); + } + if (uoptr != 0) { + userFree(uoptr); + } + return rv; +} + +/* + * Description (nsadbRemGroupFromGroup) + * + * This function removes a given group C from a parent group P. + * The group C must be a direct member of the group P. However, + * group C may also be a member of one or more of P's ancestor or + * descendant groups, and this function deals with that. The + * group entries for C and P are updated in the group database. + * But the real work is updating the groups lists of all of the + * users contained in C. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * pgoptr - pointer to parent group object + * cgoptr - pointer to child group object + * + * Returns: + * + * The return value is zero if group C was a direct member of + * group P, and was removed successfully. A return value of +1 + * indicates that group C was not a direct member of the group P. + * A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbRemGroupFromGroup(NSErr_t * errp, void * authdb, + GroupObj_t * pgoptr, GroupObj_t * cgoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t dglist; /* list of descendant groups of C */ + GroupObj_t * dgoptr; /* descendant group object pointer */ + UserObj_t * uoptr; /* user object pointer */ + USI_t * gidlist; /* pointer to group id array */ + USI_t * userlist; /* pointer to array of descendant user ids */ + USI_t dgid; /* descendant group id */ + int iusr; /* index on descendant user list */ + int usercnt; /* count of descendant users */ + int igrp; /* index of group in user group id list */ + int rv; /* result value */ + + dgoptr = 0; + uoptr = 0; + + /* Initialize a list of descendant groups of C */ + UILINIT(&dglist); + + /* Is group C a direct member of group P? */ + if (!usiPresent(&pgoptr->go_groups, cgoptr->go_gid)) { + + /* No, nothing to do */ + return 1; + } + + /* Remove group C from group P's group member list */ + rv = usiRemove(&pgoptr->go_groups, cgoptr->go_gid); + if (rv < 0) goto punt; + + /* Remove group P from group C's parent group list */ + rv = usiRemove(&cgoptr->go_pgroups, pgoptr->go_gid); + if (rv < 0) goto punt; + + /* Open user database since the group lists of users may be modified */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Open group database since group entries will be modified */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + + /* Write out the updated group C object */ + cgoptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, cgoptr); + if (rv) goto punt; + + /* Write out the updated group P object */ + pgoptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, pgoptr); + if (rv) goto punt; + + /* Now check the group lists of all users contained in group C */ + dgoptr = cgoptr; + dgid = cgoptr->go_gid; + + for (;;) { + + /* Scan the direct user members of this descendant group */ + usercnt = UILCOUNT(&dgoptr->go_users); + userlist = UILLIST(&dgoptr->go_users); + + for (iusr = 0; iusr < usercnt; ++iusr) { + + /* Get a user object for this user member */ + uoptr = userFindByUid(errp, adb->adb_userdb, userlist[iusr]); + if (uoptr == 0) { + /* Error - user id not found, databases are inconsistent */ + rv = -1; + goto punt; + } + + /* Scan the group list for this user */ + for (igrp = 0; igrp < UILCOUNT(&uoptr->uo_groups); ) { + + gidlist = UILLIST(&uoptr->uo_groups); + + /* Is the user a member of this group? */ + if (nsadbIsUserInGroup(errp, authdb, + uoptr->uo_uid, gidlist[igrp], + igrp, gidlist)) { + + /* Yes, step to next group id */ + ++igrp; + } + else { + /* + * No, remove it from the user's list of groups. The + * next group id to consider will be shifted into the + * igrp position when the current id is removed. + */ + rv = usiRemove(&uoptr->uo_groups, gidlist[igrp]); + if (rv < 0) goto punt; + uoptr->uo_flags |= UOF_MODIFIED; + } + } + + /* Write out the user object if it was changed */ + if (uoptr->uo_flags & UOF_MODIFIED) { + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + if (rv < 0) goto punt; + } + + /* Free the user object */ + userFree(uoptr); + uoptr = 0; + } + + /* + * Merge the direct member groups of this group into the + * descendants list. + */ + rv = uilMerge(&dglist, &dgoptr->go_groups); + if (rv < 0) goto punt; + + /* Free this descendant group object */ + if (dgoptr != cgoptr) { + groupFree(dgoptr); + dgoptr = 0; + } + + /* If the descendants list is empty, we're done */ + if (UILCOUNT(&dglist) <= 0) break; + + /* Remove the next group id from the descendants list */ + gidlist = UILLIST(&dglist); + dgid = gidlist[0]; + rv = usiRemove(&dglist, dgid); + if (rv < 0) goto punt; + + /* Get a group object for this descendant group */ + dgoptr = groupFindByGid(errp, adb->adb_groupdb, dgid); + if (dgoptr == 0) { + /* Error - group id not found, databases are inconsistent */ + rv = -1; + goto punt; + } + } + + UILFREE(&dglist); + return 0; + + punt: + if (dgoptr) { + groupFree(dgoptr); + } + if (uoptr) { + userFree(uoptr); + } + UILFREE(&dglist); + return rv; +} + +/* + * Description (nsadbRemUserFromGroup) + * + * This function removes a given user from a specified group G. + * The user must be a direct member of the group. However, the + * user may also be a member of one or more of G's descendant + * groups, and this function deals with that. The group entry + * for G is updated in the group database, with the user removed + * from its user member list. The user entry is updated in the + * user database, with an updated list of all groups which now + * contain the user. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to group object + * uoptr - pointer to user object + * + * Returns: + * + * The return value is zero if the user was a direct member of the + * group, and was removed successfully. A return value of +1 + * indicates that the user was not a direct member of the + * group. A negative return value indicates an error. + */ + +NSAPI_PUBLIC int nsadbRemUserFromGroup(NSErr_t * errp, void * authdb, + GroupObj_t * goptr, UserObj_t * uoptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USI_t * idlist; /* pointer to user group id array */ + USI_t tgid; /* test group id */ + int igrp; /* position in user group list */ + int rv; /* result value */ + + /* Is the user a direct member of the group? */ + if (!usiPresent(&goptr->go_users, uoptr->uo_uid)) { + + /* No, nothing to do */ + return 1; + } + + /* Remove the user from the group's user member list */ + rv = usiRemove(&goptr->go_users, uoptr->uo_uid); + if (rv < 0) goto punt; + + /* If the user object is pending deletion, no need to open databases */ + if (!(uoptr->uo_flags & UOF_DELPEND)) { + + /* + * Open user database since the group list of the user + * will be modified. + */ + rv = nsadbOpenUsers(errp, authdb, ADBF_UWRITE); + if (rv < 0) goto punt; + + /* Open group database since group entries will be modified */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GWRITE); + if (rv < 0) goto punt; + } + + /* + * Write out the updated group object. This must be done here + * because nsadbIsUserInGroup() in the loop below will read the + * entry for this group, and it needs to reflect the user's + * removal from being a direct member of the group. This does + * not preclude the possibility that the user will still be an + * indirect member of this group. + */ + goptr->go_flags |= GOF_MODIFIED; + rv = groupStore(errp, adb->adb_groupdb, 0, goptr); + if (rv) goto punt; + + /* If a delete is pending on the user, we're done */ + if (uoptr->uo_flags & UOF_DELPEND) goto punt; + + /* + * Begin loop to check whether user is still a member of each + * of the groups in its group list. Note that the group list + * may shrink during an iteration of the loop. + */ + + for (igrp = 0; igrp < UILCOUNT(&uoptr->uo_groups); ) { + + /* Get pointer to the user's array of group ids */ + idlist = UILLIST(&uoptr->uo_groups); + + /* Get the group id of the next group to consider */ + tgid = idlist[igrp]; + + /* Is the user a member of this group? */ + if (nsadbIsUserInGroup(errp, authdb, + uoptr->uo_uid, tgid, igrp, idlist)) { + + /* Yes, step to next group id */ + ++igrp; + } + else { + + /* + * No, remove it from the user's list of groups. The + * next group id to consider will be shifted into the + * igrp position when the current id is removed. + */ + rv = usiRemove(&uoptr->uo_groups, tgid); + if (rv < 0) goto punt; + } + } + + /* Write out the updated user object */ + uoptr->uo_flags |= UOF_MODIFIED; + rv = userStore(errp, adb->adb_userdb, 0, uoptr); + + punt: + return rv; +} + +/* + * Description (nsadbSuperGroups) + * + * This function builds a list of the group ids for all groups + * which contain, directly or indirectly, a specified group as + * a subgroup. We call these the supergroups of the specified + * group. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle for authentication databases + * goptr - pointer to group object + * gsuper - pointer to list to contain supergroups + * (caller must initialize) + * + * Returns: + * + * Returns the number of elements in gsuper if successful. An + * error is indicated by a negative return value. + */ + +NSAPI_PUBLIC int nsadbSuperGroups(NSErr_t * errp, void * authdb, + GroupObj_t * goptr, USIList_t * gsuper) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + USIList_t aglist; /* ancestor group id list */ + GroupObj_t * aoptr; /* ancestor group object pointer */ + USI_t * idlist; /* pointer to array of group ids */ + USI_t id; /* current group id */ + int rv; /* result value */ + + /* Initialize an empty ancestor group list */ + UILINIT(&aglist); + + /* Enter loop with specified group as first ancestor */ + id = goptr->go_gid; + aoptr = goptr; + + /* Open group database for read access */ + rv = nsadbOpenGroups(errp, authdb, ADBF_GREAD); + if (rv < 0) goto punt; + + /* Loop until the ancestor list is empty */ + for (;;) { + + /* Merge parent groups of current ancestor into ancestor list */ + rv = uilMerge(&aglist, &aoptr->go_pgroups); + if (rv < 0) goto punt; + + /* Also merge parent groups into the result list */ + rv = uilMerge(gsuper, &aoptr->go_pgroups); + if (rv < 0) goto punt; + + /* Free the ancestor group object (but not the original) */ + if (aoptr != goptr) { + groupFree(aoptr); + aoptr = 0; + } + + /* Exit the loop if the ancestor list is empty */ + if (UILCOUNT(&aglist) <= 0) break; + + /* Get pointer to array of ancestor group ids */ + idlist = UILLIST(&aglist); + + /* Remove the first ancestor */ + id = idlist[0]; + rv = usiRemove(&aglist, id); + + /* Get a group object for the ancestor */ + aoptr = groupFindByGid(errp, adb->adb_groupdb, id); + if (aoptr == 0) { + /* Error - group not found, database inconsistent */ + rv = -1; + goto punt; + } + } + + return UILCOUNT(gsuper); + + punt: + /* Handle error */ + + /* Free ancestor list */ + UILFREE(&aglist); + + return rv; +} + +NSPR_END_EXTERN_C + diff --git a/lib/libaccess/nsautherr.cpp b/lib/libaccess/nsautherr.cpp new file mode 100644 index 00000000..308c0698 --- /dev/null +++ b/lib/libaccess/nsautherr.cpp @@ -0,0 +1,126 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsautherr.c) + * + * This module provides facilities for handling authentication + * errors. + */ + +#include <string.h> +#include "base/systems.h" +#include "prprf.h" +#include "libaccess/nserror.h" +#include "libaccess/nsautherr.h" + +/* Error message formats XXX internationalize XXX */ +static char * nsaerrnomem = "insufficient dynamic memory"; +static char * nsaerrinval = "invalid argument"; +static char * nsaerropen = "error opening %s"; +static char * nsaerrmkdir = "error creating %s"; +static char * nsaerrname = "%s not found in database %s"; +static char * unknownerr = "error code %d"; + +/* + * Description (nsadbErrorFmt) + * + * This function formats an authentication error message into a + * buffer provided by the caller. The authentication error + * information is passed in an error list structure. The caller + * can indicate how many error frames should be processed. A + * newline is inserted between messages for different error frames. + * + * Arguments: + * + * errp - error frame list pointer + * msgbuf - pointer to error message buffer + * maxlen - maximum length of generated message + * maxdepth - maximum depth for traceback + */ + +NSAPI_PUBLIC void nsadbErrorFmt(NSErr_t * errp, char * msgbuf, int maxlen, int maxdepth) +{ + NSEFrame_t * efp; /* error frame pointer */ + int len; /* length of error message text */ + int depth = 0; /* current depth */ + + msgbuf[0] = 0; + + for (efp = errp->err_first; efp != 0; efp = efp->ef_next) { + + /* Stop if the message buffer is full */ + if (maxlen <= 0) break; + + if (depth > 0) { + /* Put a newline between error frame messages */ + *msgbuf++ = '\n'; + if (--maxlen <= 0) break; + } + + /* Identify the facility generating the error and the id number */ + len = PR_snprintf(msgbuf, maxlen, + "[%s%d] ", efp->ef_program, efp->ef_errorid); + msgbuf += len; + maxlen -= len; + + if (maxlen <= 0) break; + + len = 0; + + if (!strcmp(efp->ef_program, NSAuth_Program)) { + + switch (efp->ef_retcode) { + case NSAERRNOMEM: + strncpy(msgbuf, nsaerrnomem, maxlen); + len = strlen(nsaerrnomem); + break; + + case NSAERRINVAL: + /* Invalid function argument error: */ + strncpy(msgbuf, nsaerrinval, maxlen); + len = strlen(nsaerrinval); + break; + + case NSAERROPEN: + /* File open error: filename */ + if (efp->ef_errc == 1) { + len = PR_snprintf(msgbuf, maxlen, nsaerropen, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + case NSAERRMKDIR: + /* error creating database directory: database name */ + if (efp->ef_errc == 1) { + len = PR_snprintf(msgbuf, maxlen, nsaerrmkdir, + efp->ef_errv[0]); + } + break; + + case NSAERRNAME: + /* user or group name not found: database, name */ + if (efp->ef_errc == 2) { + len = PR_snprintf(msgbuf, maxlen, nsaerrname, + efp->ef_errv[0], efp->ef_errv[1]); + } + break; + + default: + len = PR_snprintf(msgbuf, maxlen, unknownerr, efp->ef_retcode); + break; + } + } + else { + len = PR_snprintf(msgbuf, maxlen, unknownerr, efp->ef_retcode); + } + + msgbuf += len; + maxlen -= len; + + if (++depth >= maxdepth) break; + } +} diff --git a/lib/libaccess/nscert.cpp b/lib/libaccess/nscert.cpp new file mode 100644 index 00000000..97939b24 --- /dev/null +++ b/lib/libaccess/nscert.cpp @@ -0,0 +1,963 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * Description (nsadb.c) + * + * This module contains routines for accessing and storing information + * in a Netscape client certificate to username database. This + * database is used to associate a username with a client certificate + * that is presented to a server. + */ + +#if defined(CLIENT_AUTH) + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <base/systems.h> +#include <netsite.h> +#include <base/file.h> +#include <base/fsmutex.h> +#include <libaccess/nsdbmgmt.h> +#define __PRIVATE_NSADB +#include <libaccess/nsadb.h> +#include <libaccess/nsamgmt.h> + +static FSMUTEX nscert_lock = 0; + +NSAPI_PUBLIC int nsadbCertInitialize(void) +{ +#ifdef XP_UNIX + nscert_lock = fsmutex_init("NSCERTMAP", geteuid(), + FSMUTEX_VISIBLE|FSMUTEX_NEEDCRIT); +#else /* XP_WIN32 */ + char winuser[128]; + DWORD wulength; + strcpy(winuser, "NSCERTMAP_"); + wulength = 128 - 11; + GetUserName(winuser+10, &wulength); + nscert_lock = fsmutex_init(winuser, 0, + FSMUTEX_VISIBLE|FSMUTEX_NEEDCRIT); +#endif + return (nscert_lock == 0) ? -1 : 0; +} + +NSAPI_PUBLIC int nsadbDecodeCertRec(int reclen, char * recptr, + CertObj_t * coptr) +{ + ATR_t cp = (ATR_t)recptr; /* current pointer into record */ + USI_t tag; /* attribute tag */ + USI_t len; /* attribute value encoding length */ + + /* Parse user DB record */ + while ((cp - (ATR_t)recptr) < reclen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* Process this attribute */ + switch (tag) { + + case CAT_USERNAME: /* username associated with cert */ + cp = NTSDECODE(cp, (NTS_t *)&coptr->co_username); + break; + + case CAT_CERTID: /* certificate-to-user map id */ + cp = USIDECODE(cp, &coptr->co_certid); + break; + + default: /* unrecognized attribute */ + /* Just skip it */ + cp += len; + break; + } + } + + return 0; +} + +/* + * Description (nsadbDecodeCertKey) + * + * This function decodes information from a certificate key. + * Currently a certificate key includes the DER encoding of the + * issuer and subject distinguished names. This is used to + * uniquely identify client certificates, even across certificate + * renewals. SECItems for the issuer and subject are provided + * by the caller. These are updated with the pointers and lengths + * of DER encodings, which can be decoded using nsadbDecodeCertName() + * into SECName structures. The returned SECItems refer to data + * in the provided key buffer. + * + * Arguments: + * + * keylen - length of the certificate key encoding + * keyptr - buffer containing certificate key encoding + * issuer - pointer to SECItem for returning issuer + * subject - pointer to SECItem for returning subject + * + * Returns: + * + * Zero is returned if no errors are encountered. Otherwise -1. + */ + +NSAPI_PUBLIC int nsadbDecodeCertKey(int keylen, char * keyptr, + SECItem * issuer, SECItem * subject) +{ + ATR_t cp = (ATR_t)keyptr; /* current pointer into DB record */ + USI_t len; /* attribute value encoding length */ + USI_t tag; /* attribute tag */ + + /* Parse user DB record */ + while ((cp - (ATR_t)keyptr) < keylen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* Process this attribute */ + switch (tag) { + + case KAT_ISSUER: /* issuer DER encoding */ + issuer->len = len; + issuer->data = cp; + cp += len; + break; + + case KAT_SUBJECT: /* subject name DER encoding */ + subject->len = len; + subject->data = cp; + cp += len; + break; + + default: /* unrecognized attribute */ + /* Just skip it */ + cp += len; + break; + } + } + + return 0; +} + +/* + * Description (nsadbEncodeCertKey) + * + * This function encodes information provided by the caller into + * a certificate key. The certificate key is returned in a + * buffer obtained from MALLOC(). + * + * Arguments: + * + * issuer - pointer to SECItem for issuer DER + * subject - pointer to SECItem for subject DER + * keylen - returned length of certificate key + * keyptr - returned pointer to buffer containing + * certificate key encoding + * + * Returns: + * + * Zero is returned if no errors are encountered. Otherwise -1. + */ + +NSAPI_PUBLIC int nsadbEncodeCertKey(SECItem * issuer, SECItem * subject, + int * keylen, char **keyptr) +{ + ATR_t cp; /* pointer into key buffer */ + ATR_t kptr; /* pointer to key buffer */ + int klen; /* length of key */ + int rv = -1; + + /* Compute length of key encoding */ + klen = 1 + USILENGTH(issuer->len) + issuer->len + + 1 + USILENGTH(subject->len) + subject->len; + + /* Allocate buffer to contain the key */ + kptr = (ATR_t)MALLOC(klen); + if (kptr) { + /* Encode issuer and subject as attributes */ + cp = kptr; + *cp++ = KAT_ISSUER; + cp = USIENCODE(cp, issuer->len); + memcpy(cp, issuer->data, issuer->len); + cp += issuer->len; + *cp++ = KAT_SUBJECT; + cp = USIENCODE(cp, subject->len); + memcpy(cp, subject->data, subject->len); + rv = 0; + } + + /* Return length and buffer pointer */ + if (keylen) *keylen = klen; + *keyptr = (char *)kptr; + + return rv; +} + +/* + * Description (nsadbEnumCertsHelp) + * + * This is a local function that is called by NSDB during certificate + * to user database enumeration. It decodes certificate records into + * CertObj_t structures, and presents them to the caller of + * nsadbEnumerateCerts(), via the specified call-back function. + * The call-back function return value may be a negative error code, + * which will cause enumeration to stop, and the error code will be + * returned from nsadbEnumerateCerts(). If the return value of the + * call-back function is not negative, it can contain one or more of + * the following flags: + * + * ADBF_KEEPOBJ - do not free the CertObj_t structure + * that was passed to the call-back function + * ADBF_STOPENUM - stop the enumeration without an error + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to CertEnumArgs_t structure + * keylen - certificate record key length + * keyptr - certificate record key + * reclen - length of certificate record + * recptr - pointer to certificate record contents + * + * Returns: + * + * If the call-back returns a negative result, that value is + * returned. If the call-back returns ADBF_STOPENUM, then + * -1 is returned, causing the enumeration to stop. Otherwise + * the return value is zero. + */ + +typedef struct CertEnumArgs_s CertEnumArgs_t; +struct CertEnumArgs_s { + int rv; /* just a return value */ + void * client; /* the current key for lookup */ + void * authdb; /* the authentication data base */ + CertEnumCallback func; /* client's callback function */ +}; + +static int nsadbEnumCertsHelp(NSErr_t * errp, void * parg, + int keylen, char * keyptr, + int reclen, char * recptr) +{ + CertEnumArgs_t * ce = (CertEnumArgs_t *)parg; + CertObj_t * coptr; + int rv = NSAERRNOMEM; + + /* Allocate a CertObj_t structure and initialize it */ + coptr = (CertObj_t *)MALLOC(sizeof(CertObj_t)); + if (coptr) { + + coptr->co_issuer.data = 0; + coptr->co_subject.data = 0; + coptr->co_username = 0; + coptr->co_certid = 0; + + /* Decode the certificate key */ + rv = nsadbDecodeCertKey(keylen, keyptr, + &coptr->co_issuer, &coptr->co_subject); + + /* Decode the certificate record */ + rv = nsadbDecodeCertRec(reclen, recptr, coptr); + + /* Pass the CertObj_t to the callback function */ + rv = (*ce->func)(errp, ce->authdb, ce->client, coptr); + if (rv >= 0) { + + /* Count the number of records seen */ + ce->rv += 1; + + /* Free the user object unless the call-back says not to */ + if (!(rv & ADBF_KEEPOBJ)) { + nsadbFreeCertObj(coptr); + } + /* Return either 0 or -1, depending on ADBF_STOPENUM */ + rv = (rv & ADBF_STOPENUM) ? -1 : 0; + } + else { + /* return the error code */ + ce->rv = rv; + } + } + + return rv; +} + +/* + * Description (nsadbEnumerateClients) + * + * (See description for nsadbEnumerateUsers) + */ + +NSAPI_PUBLIC int nsadbEnumerateCerts(NSErr_t * errp, void * authdb, + void * argp, CertEnumCallback func) +{ + AuthDB_t * adb = (AuthDB_t*)authdb; + CertEnumArgs_t helper_data; + int rv; + + /* Open the certificate subdatabase for read access */ + rv = nsadbOpenCerts(errp, authdb, ADBF_CREAD); + if (rv >= 0) { + helper_data.authdb = authdb; + helper_data.func = func; + helper_data.client = argp; + helper_data.rv = 0; + + rv = ndbEnumerate(errp, adb->adb_certdb, NDBF_ENUMNORM, + (void*)&helper_data, nsadbEnumCertsHelp); + } + + return (rv < 0) ? rv: helper_data.rv; +} + +NSAPI_PUBLIC void nsadbFreeCertObj(CertObj_t * coptr) +{ + if (coptr) { + FREE(coptr->co_username); + FREE(coptr); + } +} + +NSAPI_PUBLIC int nsadbGetCertById(NSErr_t * errp, void * authdb, + USI_t certid, CertObj_t **coptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + CertObj_t * cop = 0; + char * keyptr; + char * recptr; + int keylen; + int reclen; + int rv; + + rv = nsadbOpenCerts(errp, authdb, ADBF_CREAD); + if (rv < 0) goto punt; + + /* Get the name corresponding to the id */ + rv = ndbIdToName(errp, adb->adb_certdb, certid, &keylen, &keyptr); + if (rv < 0) goto punt; + + rv = ndbFindName(errp, adb->adb_certdb, + keylen, keyptr, &reclen, &recptr); + if (rv < 0) goto punt; + + /* Allocate a CertObj_t structure and initialize it */ + cop = (CertObj_t *)MALLOC(sizeof(CertObj_t)); + if (cop) { + + cop->co_issuer.data = 0; + cop->co_subject.data = 0; + cop->co_username = 0; + cop->co_certid = 0; + + /* Decode the certificate key */ + rv = nsadbDecodeCertKey(keylen, keyptr, + &cop->co_issuer, &cop->co_subject); + + /* Decode the certificate record */ + rv = nsadbDecodeCertRec(reclen, recptr, cop); + + } + + punt: + if (coptr) *coptr = cop; + return rv; +} + +/* + * Description (nsadbGetUserByCert) + * + * This function looks up a specified client certificate in the + * authentication database. It returns a pointer to the username + * associated with the client certificate, if any. The username + * buffer remains valid until the authentication database is + * closed. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * cert - pointer to client certificate + * username - pointer to returned user name (or null) + * + * Returns: + * + * The return value will be zero if the certificate is found. Also, + * *username will be set to the string value of the associated username + * iff username is not null. + */ + +NSAPI_PUBLIC int nsadbGetUserByCert(NSErr_t * errp, void * authdb, + CERTCertificate * cert, char **username) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + ATR_t cp; /* current pointer into DB record */ + char * user = 0; /* pointer to username */ + char * keyptr = 0; /* pointer to cert key */ + char * recptr; /* pointer to cert db record */ + int keylen; /* length of cert key */ + int reclen; /* length of cert db record */ + USI_t tag; /* attribute tag */ + USI_t len; /* attribute value encoding length */ + int rv; + + /* Construct the record key from the certificate */ + rv = nsadbEncodeCertKey(&cert->derIssuer, &cert->derSubject, + &keylen, &keyptr); + + if (adb->adb_certdb == NULL) { + rv = nsadbOpenCerts(errp, authdb, ADBF_CREAD); + if (rv < 0) goto punt; + } + + rv = ndbFindName(errp, adb->adb_certdb, + keylen, keyptr, &reclen, &recptr); + if (rv < 0) goto punt; + + /* Parse cert DB record */ + cp = (ATR_t)recptr; + + while ((cp - (ATR_t)recptr) < reclen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* We want the CAT_USERNAME attribute */ + if (tag == CAT_USERNAME) { + + /* Get the username associated with the cert */ + user = (char *)cp; + break; + } + + /* Just skip other attributes */ + cp += len; + } + + punt: + if (keyptr) { + FREE(keyptr); + } + if (username) *username = user; + return rv; +} + +/* + * Description (see description for nsadbOpenUsers) + */ + +int nsadbOpenCerts(NSErr_t * errp, void * authdb, int flags) +{ + AuthDB_t *adb = (AuthDB_t*)authdb; + char *dbname = NULL; /* user database name */ + int dblen; /* strlen(adb_dbname) */ + int version; /* database version */ + int eid; /* error id code */ + int rv; /* result value */ + + if (adb == NULL) goto err_inval; + + /* Is the user database already open? */ + if (adb->adb_certdb != 0) { + + /* Yes, is it open for the desired access? */ + if (adb->adb_flags & flags) { + + /* Yes, that was easy */ + return 0; + } + } + else { + /* Allocate space for the user database filename */ + dblen = strlen(adb->adb_dbname); + + dbname = (char *)MALLOC(dblen + strlen(ADBCERTDBNAME) + 2); + if (dbname == 0) goto err_nomem; + + /* Construct user database name */ + strcpy(dbname, adb->adb_dbname); + + /* Put in a '/' (or '\') if it's not there */ + if (dbname[dblen-1] != FILE_PATHSEP) { + dbname[dblen] = FILE_PATHSEP; + dbname[dblen+1] = 0; + ++dblen; + } + + strcpy(&dbname[dblen], ADBCERTDBNAME); + + if (nscert_lock == 0) { + rv = nsadbCertInitialize(); + if (rv < 0) goto err_lock; + } + adb->adb_certlock = nscert_lock; + if (adb->adb_certlock == 0) goto punt; + + fsmutex_lock((FSMUTEX)(adb->adb_certlock)); + + adb->adb_certdb = ndbOpen(errp, + dbname, 0, NDB_TYPE_CLIENTDB, &version); + if (adb->adb_certdb == 0) { + fsmutex_unlock((FSMUTEX)(adb->adb_certlock)); + goto err_open; + } + } + + /* + * We don't really reopen the database to get the desired + * access mode, since that is handled at the nsdb level. + * But we do update the flags, just for the record. + */ + adb->adb_flags &= ~(ADBF_CREAD|ADBF_CWRITE); + if (flags & ADBF_CWRITE) adb->adb_flags |= ADBF_CWRITE; + else adb->adb_flags |= ADBF_CREAD; + rv = 0; + + punt: + if (dbname != NULL) FREE(dbname); + return rv; + + err_inval: + eid = NSAUERR3400; + rv = NSAERRINVAL; + goto err_ret; + + err_nomem: + eid = NSAUERR3420; + rv = NSAERRNOMEM; + goto err_ret; + + err_lock: + eid = NSAUERR3430; + rv = NSAERRLOCK; + goto err_ret; + + err_open: + eid = NSAUERR3440; + rv = NSAERROPEN; + + err_ret: + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, dbname); + goto punt; + +} + +NSAPI_PUBLIC void nsadbCloseCerts(void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + + if (adb->adb_certnm != 0) { + /* Close the username-to-certid database */ + nsadbCloseCertUsers(authdb, flags); + } + + if (adb->adb_certdb != 0) { + + ndbClose(adb->adb_certdb, 0); + adb->adb_certdb = 0; + + /* + * A lock is held for the certificate map DB as long as it is + * open, so release the lock now. + */ + fsmutex_unlock((FSMUTEX)(adb->adb_certlock)); + } +} + +/* + * Description (nsadbOpenCertUsers) + * + * This function opens a database that maps user names to client + * certificates. The database appears as "Certs.nm" in the + * authentication database directory. This function requires + * that the primary certificate database be opened (Certs.db) + * first, and will open it if necessary, acquiring a global + * lock in the process. The lock will not be released until + * nsadbCloseCerts() or nsadbClose() is called. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * flags - same as nsadbOpenCerts() + * + * Returns: + * + * The return value is zero if the operation is successful. + * Otherwise a negative error code is returned. + */ + +NSAPI_PUBLIC int nsadbOpenCertUsers(NSErr_t * errp, void * authdb, int flags) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + char * dbname = 0; + int dblen; + int oflags = O_RDONLY; /* assume read-only access */ + int eid; + int rv; + + /* The primary certificate mapping database must be open first */ + if (adb->adb_certdb != 0) { + + /* It's open, but is it read-only when we need write? */ + if (((flags & adb->adb_flags) == 0) && (flags & ADBF_CWRITE)) { + + /* Yes, close it */ + nsadbCloseCerts(authdb, 0); + } + } + + /* Open it for the desired access if necessary */ + if (adb->adb_certdb == 0) { + /* + * Open it for the desired access. Note that this acquires + * a global lock which is not released until nsadbClose() is + * called for the entire authentication database. + */ + rv = nsadbOpenCerts(errp, authdb, flags); + if (rv < 0) { + /* Go no further if that failed */ + return rv; + } + } + + /* Now look at the username-to-certid database in particular */ + if (adb->adb_certnm && (adb->adb_flags & flags)) { + + /* The database is already open for the desired access */ + return 0; + } + + dblen = strlen(adb->adb_dbname); + dbname = (char *)MALLOC(dblen + strlen(ADBUMAPDBNAME) + 2); + strcpy(dbname, adb->adb_dbname); + if (dbname[dblen-1] != FILE_PATHSEP) { + dbname[dblen] = FILE_PATHSEP; + dbname[++dblen] = 0; + } + strcpy(&dbname[dblen], ADBUMAPDBNAME); + + /* Check for write access and set open flags appropriately if so */ + if (flags & ADBF_CWRITE) { + oflags = O_CREAT|O_RDWR; + } + + /* Open the username-to-certid database */ +// adb->adb_certnm = dbopen(dbname, oflags, 0644, DB_HASH, 0); + adb->adb_certnm = 0; + if (adb->adb_certnm == 0) goto err_open; + + punt: + FREE(dbname); + + return rv; + + err_open: + eid = NSAUERR3600; + rv = NSAERROPEN; + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, dbname); + goto punt; +} + +/* + * Description (nsadbFindCertUser) + * + * This function checks to see whether a client certificate is + * registered for a specified user name. If so, it returns the + * certificate mapping id (for use with nsadbGetCertById()). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * username - pointer to user name string + * id - pointer to returned certificate mapping id + * + * Returns: + * + * If a certificate is registered for the specified user, the return + * value is zero and the certificate mapping id is returned via 'id'. + * Otherwise the return value is a negative error code (nsautherr.h) + * and an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbFindCertUser(NSErr_t * errp, void * authdb, + const char * username, USI_t * id) +{ + int eid; + int rv; + eid = NSAUERR3700; + rv = NSAERRNAME; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + return rv; +} + +/* + * Description (nsadbAddCertUser) + * + * This function adds an entry to the username-to-cert id database, + * with a given username and certificate mapping id. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * username - pointer to user name string + * id - certificate mapping id + * + * Returns: + * + * If the entry is added successfully, the return value is zero. + * Otherwise the return value is a negative error code (nsautherr.h) + * and an error frame is generated if an error frame list is provided. + */ + +NSAPI_PUBLIC int nsadbAddCertUser(NSErr_t * errp, void * authdb, + const char * username, USI_t id) +{ + /* Need to be ported on NSS 3.2 */ + int eid; + int rv; + + eid = NSAUERR3800; + rv = NSAERRPUT; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + return rv; +} + +NSAPI_PUBLIC int nsadbRemoveCertUser(NSErr_t * errp, void * authdb, + char * username) +{ + /* Need to be ported on NSS 3.2 */ + int eid; + int rv; + + eid = NSAUERR3800; + rv = NSAERRPUT; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + return rv; +} + +NSAPI_PUBLIC void nsadbCloseCertUsers(void * authdb, int flags) +{ + /* Need to be ported on NSS 3.2 */ +} + +/* + * Description (nsadbPutUserByCert) + * + * This function looks up a stores a client certificate mapping + * in the authentication database along with the associated username. + * It assumes that a record with the specified certificate key does + * not already exist, and will replace it if it does. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * authdb - handle returned by nsadbOpen() + * certLen - length of the certificate key + * cert - certificate key pointer + * user - username to be associated with the + * certificate + * + * Returns: + * + */ + +NSAPI_PUBLIC int nsadbPutUserByCert(NSErr_t * errp, void * authdb, + CERTCertificate * cert, + const char * username) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + ATR_t cp; /* pointer into cert record contents */ + char * keyptr = 0; /* pointer to cert record key */ + char * recptr = 0; /* pointer to cert record contents */ + int keylen; /* length of cert record key */ + int reclen; /* length of cert record contents */ + USI_t certid; + int usrlen; + int certidlen; + int eid; + int rv; + + /* Construct the record key from the certificate */ + rv = nsadbEncodeCertKey(&cert->derIssuer, &cert->derSubject, + &keylen, &keyptr); + + /* Open the username-to-cert id database for write */ + rv = nsadbOpenCertUsers(errp, authdb, ADBF_CWRITE); + if (rv) goto punt; + + /* If the username is already mapped to a cert, it's an error */ + certid = 0; + rv = nsadbFindCertUser(errp, authdb, username, &certid); + if (rv == 0) goto err_map; + + /* + * Allocate a certificate id and write a record mapping this id + * to the specified certificate key. + */ + certid = 0; + rv = ndbAllocId(errp, adb->adb_certdb, keylen, keyptr, &certid); + if (rv) goto punt; + + /* Record the username as being mapped to the allocated cert id */ + rv = nsadbAddCertUser(errp, authdb, username, certid); + if (rv < 0) goto punt; + + nsadbCloseCertUsers(authdb, 0); + + /* + * First we need to figure out how long the generated record will be. + * This doesn't have to be exact, but it must not be smaller than the + * actual record size. + */ + + /* CAT_USERNAME attribute: tag, length, NTS */ + usrlen = NTSLENGTH(username); + if (usrlen > 127) goto err_user; + reclen = 2 + usrlen; + + /* CAT_CERTID attribute: tag, length, USI */ + certidlen = USILENGTH(certid); + reclen += 2 + certidlen; + + /* Allocate the attribute record buffer */ + recptr = (char *)MALLOC(reclen); + if (recptr) { + + cp = (ATR_t)recptr; + + /* Encode CAT_USERNAME attribute */ + *cp++ = CAT_USERNAME; + *cp++ = usrlen; + cp = NTSENCODE(cp, (NTS_t)username); + + /* Encode CAT_CERTID attribute */ + *cp++ = CAT_CERTID; + *cp++ = certidlen; + cp = USIENCODE(cp, certid); + } + + /* Store the record in the database under the certificate key */ + rv = ndbStoreName(errp, adb->adb_certdb, + 0, keylen, keyptr, reclen, recptr); + + punt: + if (keyptr) { + FREE(keyptr); + } + if (recptr) { + FREE(recptr); + } + + return rv; + + err_user: + eid = NSAUERR3500; + rv = NSAERRINVAL; + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, adb->adb_dbname); + goto punt; + + err_map: + eid = NSAUERR3520; + rv = NSAERRCMAP; + nsadbCloseCertUsers(authdb, 0); + nserrGenerate(errp, rv, eid, NSAuth_Program, 1, adb->adb_dbname); + goto punt; +} + +NSAPI_PUBLIC int nsadbRemoveCert(NSErr_t * errp, void * authdb, + void * username, CertObj_t * coptr) +{ + AuthDB_t * adb = (AuthDB_t *)authdb; + char * keyptr = 0; /* pointer to cert record key */ + int keylen; /* length of cert record key */ + int rv; + int rv2; + + /* If a username is specified, require it to match */ + if (username && strcmp((char *)username, coptr->co_username)) { + return 0; + } + + /* Construct the record key from the certificate */ + rv = nsadbEncodeCertKey(&coptr->co_issuer, &coptr->co_subject, + &keylen, &keyptr); + + if (adb->adb_certdb == NULL) { + rv = nsadbOpenCerts(errp, authdb, ADBF_CWRITE); + if (rv < 0) goto punt; + } + + /* Remove the username-to-cert id entry from Certs.nm */ + rv = nsadbOpenCertUsers(errp, authdb, ADBF_CWRITE); + if (rv < 0) goto punt; + rv = nsadbRemoveCertUser(errp, authdb, coptr->co_username); + nsadbCloseCertUsers(authdb, 0); + + /* Free the cert id value, if any */ + rv = 0; + if (coptr->co_certid != 0) { + rv = ndbFreeId(errp, adb->adb_certdb, + keylen, keyptr, coptr->co_certid); + } + + /* Delete the cert record */ + rv2 = ndbDeleteName(errp, adb->adb_certdb, 0, keylen, keyptr); + + punt: + if (keyptr) { + FREE(keyptr); + } + return (rv) ? rv : rv2; +} + +NSAPI_PUBLIC int nsadbRemoveUserCert(NSErr_t * errp, + void * authdb, char * username) +{ + CertObj_t * coptr = 0; + USI_t certid = 0; + int rv; + + /* + * Open for read access at first. We don't want to create the + * database if it's not already there. This will do nothing + * if the database is already open for write, since that implies + * read access as well. + */ + rv = nsadbOpenCertUsers(errp, authdb, ADBF_CREAD); + if (rv < 0) goto punt; + + /* Find a certificate mapping id for the given username */ + rv = nsadbFindCertUser(errp, authdb, username, &certid); + if (rv < 0) goto punt; + + /* Look up the mapping from the mapping id */ + rv = nsadbGetCertById(errp, authdb, certid, &coptr); + if (rv < 0) goto punt; + + /* It's there, so remove it. This will re-open for write if needed. */ + rv = nsadbRemoveCert(errp, authdb, (void *)username, coptr); + + punt: + + if (coptr != 0) { + nsadbFreeCertObj(coptr); + } + + return rv; +} + +#endif /* defined(CLIENT_AUTH) */ diff --git a/lib/libaccess/nsdb.cpp b/lib/libaccess/nsdb.cpp new file mode 100644 index 00000000..0479b8f3 --- /dev/null +++ b/lib/libaccess/nsdb.cpp @@ -0,0 +1,836 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsdb.c) + * + * This provides access to a Netscape server database. + * A server database is composed of two (libdbm) DB files. One + * of these (<dbname>.db) contains records indexed by a string + * key. These records contain the primary information in the + * database. A second DB file (<dbname>.id) is used to map an + * integer id value to a string key, which can then be used to + * locate a record in the first file. + * + * Normally the records in the primary DB file will contain the + * id values which are used to key the id-to-name DB. When this + * is the case, it is possible to construct the id-to-name DB from + * the primary DB file, and an interface is provided to facilitate + * this. + */ + +#include <stdio.h> +#include <base/systems.h> +#include <netsite.h> +#include <base/file.h> +#define __PRIVATE_NSDB +#include <libaccess/nsdb.h> + +#include <errno.h> + +#define NDBMODE 0644 /* mode for creating files */ + +char * NSDB_Program = "NSDB"; /* NSDB facility name */ + +NSPR_BEGIN_EXTERN_C + +/* + * Description (ndbClose) + * + * This function closes the specified database. This involves + * closing the primary and id-to-name DB files, and freeing the + * NSDB_t object. + * + * Arguments: + * + * ndb - database handle from ndbOpen() + * flags - (currently unused - should be zero) + * + */ + +void ndbClose(void * ndb, int flags) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + + if (ndbp->ndb_flags & (NDBF_WRNAME|NDBF_RDNAME)) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + } + + if (ndbp->ndb_flags & (NDBF_WRID|NDBF_RDID)) { + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + } + + if (ndbp->ndb_pname) { + FREE(ndbp->ndb_pname); + } + + if (ndbp->ndb_iname) { + FREE(ndbp->ndb_iname); + } + + FREE(ndbp); +} + +/* + * Description (ndbEnumerate) + * + * This function is called to enumerate the records of the primary + * DB file to a caller-specified function. The function specified + * by the caller is called with the name (key), length and address + * of each record in the primary DB file. The 'flags' argument can + * be used to select normal data records, metadata records, or both. + * If the 'flags' value is zero, only normal data records are + * enumerated. The function specified by the caller returns -1 to + * terminate the enumeration, 0 to continue it, or +1 to restart + * the enumeration from the beginning. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * flags - bit flags: + * NDBF_ENUMNORM - normal data records + * NDBF_ENUMMETA - metadata records + * func - pointer to caller's enumeration function + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned, and an error frame is generated if an + * error frame list was provided by the caller. + */ + +int ndbEnumerate(NSErr_t * errp, void * ndb, int flags, void * argp, +#ifdef UnixWare + ArgFn_ndbEnum func) /* for ANSI C++ standard, see nsdb.h */ +#else + int (*func)(NSErr_t * ferrp, void * parg, + int namelen, char * name, + int reclen, char * recptr)) +#endif +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + int rv; + int dbflag; + + /* Is the user DB open for reading names? */ + if (!(ndbp->ndb_flags & NDBF_RDNAME)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_RDNAME); + if (rv) goto punt; + } + + if (flags == 0) flags = NDBF_ENUMNORM; + + for (dbflag = R_FIRST; ; dbflag = (rv > 0) ? R_FIRST : R_NEXT) { + + /* Retrieve the next (first) record from the primary DB */ + rv = (*ndbp->ndb_pdb->seq)(ndbp->ndb_pdb, &key, &rec, dbflag); + if (rv) break; + + /* Is this a metadata record? */ + if (*(char *)key.data == NDB_MDPREFIX) { + + /* Yes, skip it if metadata was not requested */ + if (!(flags & NDBF_ENUMMETA)) continue; + } + else { + /* Skip normal data if not requested */ + if (!(flags & NDBF_ENUMNORM)) continue; + } + + /* Pass this record to the caller's function */ + rv = (*func)(errp, argp, + key.size, (char *)key.data, rec.size, (char *)rec.data); + if (rv < 0) break; + } + + /* Indicate success */ + rv = 0; + + punt: + return rv; +} + +/* + * Description (ndbFindName) + * + * This function retrieves from the database a record with the + * specified key. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * namelen - length of the key, including null + * terminator if any + * name - pointer to the key of the desired record + * reclen - pointer to returned record length + * recptr - pointer to returned record pointer + * + * Returns: + * + * If successful, the return value is zero, and the length and + * address of the returned record are returned through reclen and + * recptr. Otherwise the return value is non-zero, and an error + * frame is generated if an error frame list was provided by the + * caller. + * + * Notes: + * + * The record buffer is dynamically allocated and is freed + * automatically when the database is closed. + */ + +int ndbFindName(NSErr_t * errp, void * ndb, int namelen, char * name, + int * reclen, char **recptr) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + int eid; /* error id code */ + int rv; /* result value */ + + /* Is the user DB open for reading names? */ + if (!(ndbp->ndb_flags & NDBF_RDNAME)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_RDNAME); + if (rv) goto punt; + } + + /* Set up record key. Include the terminating null byte. */ + key.data = (void *)name; + key.size = (namelen > 0) ? namelen : (strlen(name) + 1); + + /* Initialize record buffer descriptor */ + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_pget; + + /* Return record length and address */ + if (reclen) *reclen = rec.size; + if (recptr) *recptr = (char *)rec.data; + + /* Indicate success */ + rv = 0; + + punt: + return rv; + + err_pget: + eid = NSDBERR1000; + rv = NDBERRGET; + nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, name); + goto punt; +} + +/* + * Description (ndbIdToName) + * + * This function looks up a specified id in the id-to-name DB + * file, and returns the associated name string. This name + * can be used to retrieve a record using ndbFindName(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * id - id to look up + * plen - pointer to returned length of name + * (may be null, length includes null terminator + * in a string) + * pname - pointer to returned name string pointer + * + * Returns: + * + * The return value is zero if the operation is successful. An + * error is indicated by a negative return value (see nsdberr.h), + * and an error frame is generated if an error frame list was + * provided by the caller. + */ + +int ndbIdToName(NSErr_t * errp, + void * ndb, unsigned int id, int * plen, char **pname) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + char * name = 0; + int namelen = 0; + uint32 myid = id - 1; + int eid; /* error id code */ + int rv; /* result value */ + + /* Is the id-to-name DB open for reading ids? */ + if (!(ndbp->ndb_flags & NDBF_RDID)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_RDID); + if (rv) goto punt; + } + + /* Set up record key */ +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(myid); +#endif + key.data = (void *)&myid; + key.size = sizeof(myid); + + /* Initialize record buffer descriptor */ + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_idb->get)(ndbp->ndb_idb, &key, &rec, 0); + if (rv) goto err_iget; + + /* Get the name pointer (terminating null is part of the name) */ + namelen = rec.size; + name = (char *) rec.data; + + punt: + /* Return name length and size if requested */ + if (plen) *plen = namelen; + if (pname) *pname = name; + + return rv; + + err_iget: + eid = NSDBERR1100; + rv = NDBERRGET; + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_iname, system_errmsg()); + goto punt; +} + +/* + * Description (ndbInitPrimary) + * + * This function creates and initializes the primary DB file. + * Initialization involves writing any required metadata records. + * Currently there is a ?dbtype record, which specifies the nsdb + * version number, and a database type and version number that + * were passed as arguments to ndbOpen(). There is also a + * ?idmap record, which contains an allocation bitmap for id values + * used as keys in the associated id-to-name DB file. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned, and an error frame is generated if an + * error frame list was provided by the caller. + */ + +int ndbInitPrimary(NSErr_t * errp, void * ndb) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; +#if BYTE_ORDER == LITTLE_ENDIAN + uint32 m; + int i; +#endif + int eid; /* error id code */ + int rv; /* result value */ + uint32 dbtype[4]; + + /* Error if the primary DB is marked as existing already */ + if (!(ndbp->ndb_flags & NDBF_NONAME)) goto err_exists; + + /* First create the primary DB file */ + ndbp->ndb_pdb = dbopen(ndbp->ndb_pname, O_RDWR | O_CREAT | O_TRUNC, + NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_pdb) goto err_open; + + /* Generate data for the ?dbtype record */ + dbtype[0] = NDB_VERSION; + dbtype[1] = ndbp->ndb_dbtype; + dbtype[2] = ndbp->ndb_version; + dbtype[3] = 0; +#if BYTE_ORDER == LITTLE_ENDIAN + for (i = 0; i < 4; ++i) { + m = dbtype[i]; + M_32_SWAP(m); + dbtype[i] = m; + } +#endif + + /* Set up descriptors for the ?dbtype record key and data */ + key.data = (void *)NDB_DBTYPE; + key.size = strlen(NDB_DBTYPE) + 1; + + rec.data = (void *)dbtype; + rec.size = sizeof(dbtype); + + /* Write the ?dbtype record out */ + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mput1; + + /* Write out an empty ?idmap record */ + key.data = (void *)NDB_IDMAP; + key.size = strlen(NDB_IDMAP) + 1; + + rec.data = 0; + rec.size = 0; + + /* Write the ?idmap record */ + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mput2; + + /* Close the DB file */ + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + + /* Clear the flag that says the primary DB file does not exist */ + ndbp->ndb_flags &= ~(NDBF_NONAME|NDBF_RDNAME|NDBF_WRNAME); + + /* Indicate success */ + return 0; + + err_exists: + /* Primary database already exists */ + eid = NSDBERR1200; + rv = NDBERREXIST; + nserrGenerate(errp, rv, eid, NSDB_Program, 1, ndbp->ndb_pname); + return rv; + + err_open: + /* Error opening primary database for write */ + eid = NSDBERR1220; + rv = NDBERROPEN; + goto err_dbio; + + err_mput1: + /* Error writing "?dbtype" record */ + eid = NSDBERR1240; + rv = NDBERRMDPUT; + goto err_dbio; + + err_mput2: + /* Error writing "?idmap" record */ + eid = NSDBERR1260; + rv = NDBERRMDPUT; + goto err_dbio; + + err_dbio: + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_pname, system_errmsg()); + + /* Close the primary DB file if it exists */ + if (ndbp->ndb_pdb) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + ndbp->ndb_flags &= ~(NDBF_RDNAME|NDBF_WRNAME); + } + + /* Delete the file */ + system_unlink(ndbp->ndb_pname); + return rv; +} + +/* + * Description (ndbOpen) + * + * This function opens a server database by name. The specified + * name may be the name of the primary DB file, or the name + * without the ".db" suffix. This function will attempt to open + * both the primary and the id-to-name DB files for read access. + * If either of the DB files do not exist, they are not created + * here, but a handle for the database will still be returned. + * The DB files will be created when a subsequent access writes + * to the database. The caller also specifies an application + * database type, which is checked against a value stored in + * in the database metadata, if the primary DB file exists, or + * which is stored in the file metadata when the file is created. + * A type-specific version number is passed and returned. The + * value passed will be stored in the file metadata if it is + * subsequently created. If the file exists, the value in the + * file metadata is returned, and it is the caller's responsibility + * to interpret it. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * dbname - primary DB filename + * flags - (currently unused - should be zero) + * dbtype - application DB type (NDB_TYPE_xxxxx) + * version - (in/out) type-specific version number + * + * Returns: + * + * A handle that can be used for subsequent accesses to the database + * is returned, or 0, if an error occurs, and an error frame is + * generated if an error frame list was provided by the caller. + */ + +void * ndbOpen(NSErr_t * errp, + char * dbname, int flags, int dbtype, int * version) +{ + NSDB_t * ndbp = 0; /* database object pointer */ + char * pname = 0; /* primary DB file name */ + char * iname = 0; /* id-to-name DB file name */ + int namelen; + uint32 dbtrec[4]; + uint32 m; + DBT key; + DBT rec; + int eid; /* error id code */ + int rv; /* result value */ + + /* Get the database name */ + namelen = strlen(dbname); + if (!strcmp(&dbname[namelen-3], ".db")) { + namelen -= 3; + } + + /* Get the primary DB file name */ + pname = (char *)MALLOC(namelen + 4); + if (pname == 0) goto err_nomem1; + strncpy(pname, dbname, namelen); + strcpy(&pname[namelen], ".db"); + + /* Get the id-to-name DB file name */ + iname = (char *)MALLOC(namelen + 4); + if (iname == 0) goto err_nomem2; + strncpy(iname, dbname, namelen); + strcpy(&iname[namelen], ".id"); + + /* Allocate the database object */ + ndbp = (NSDB_t *)MALLOC(sizeof(NSDB_t)); + if (ndbp == 0) goto err_nomem3; + + /* Initialize the database object */ + ndbp->ndb_pname = pname; + ndbp->ndb_pdb = 0; + ndbp->ndb_iname = iname; + ndbp->ndb_idb = 0; + ndbp->ndb_flags = 0; + ndbp->ndb_dbtype = dbtype; + ndbp->ndb_version = (version) ? *version : 0; + + /* Open the primary DB file */ + ndbp->ndb_pdb = dbopen(pname, O_RDONLY, NDBMODE, DB_HASH, 0); + + /* Was it there? */ + if (ndbp->ndb_pdb) { + + /* Retrieve the ?dbtype record */ + key.data = (void *)NDB_DBTYPE; + key.size = strlen(NDB_DBTYPE) + 1; + + rec.data = 0; + rec.size = 0; + + /* Read the ?dbtype record */ + rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mdget; + + /* Check it out */ + if (rec.size < 16) goto err_fmt; + + /* Copy data to an aligned area */ + memcpy((void *)dbtrec, rec.data, sizeof(dbtrec)); + + /* Get the NSDB version number */ + m = dbtrec[0]; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(m); +#endif + /* Assume forward compatibility with versions up to current + 0.5 */ + if (m > (NDB_VERSION + 5)) goto err_vers; + + /* XXX Assume infinite backward compatibility */ + + /* Get the application database type */ + m = dbtrec[1]; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(m); +#endif + /* It's got to match */ + if (m != dbtype) goto err_type; + + /* Get the type-specific version number */ + m = dbtrec[3]; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(m); +#endif + /* Don't check it. Just return it. */ + if (version) *version = m; + + /* The value in dbtrec[3] is currently ignored */ + + /* Mark the primary DB file open for read access */ + ndbp->ndb_flags |= NDBF_RDNAME; + } + else { + /* Indicate that the primary DB file does not exist */ + ndbp->ndb_flags |= NDBF_NONAME; + } + + return (void *)ndbp; + + err_nomem1: + eid = NSDBERR1400; + rv = NDBERRNOMEM; + goto err_nomem; + + err_nomem2: + eid = NSDBERR1420; + rv = NDBERRNOMEM; + goto err_nomem; + + err_nomem3: + eid = NSDBERR1440; + rv = NDBERRNOMEM; + err_nomem: + nserrGenerate(errp, rv, eid, NSDB_Program, 0); + goto punt; + + err_mdget: + eid = NSDBERR1460; + rv = NDBERRMDGET; + nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, + system_errmsg()); + goto err_ret; + + err_fmt: + eid = NSDBERR1480; + rv = NDBERRMDFMT; + goto err_ret; + + err_vers: + { + char vnbuf[16]; + + eid = NSDBERR1500; + rv = NDBERRVERS; + sprintf(vnbuf, "%d", (int)m); + nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, vnbuf); + } + goto punt; + + err_type: + eid = NSDBERR1520; + rv = NDBERRDBTYPE; + goto err_ret; + + err_ret: + nserrGenerate(errp, rv, eid, NSDB_Program, 1, ndbp->ndb_pname); + goto punt; + + punt: + /* Error clean-up */ + if (pname) FREE(pname); + if (iname) FREE(iname); + if (ndbp) { + /* Close the DB files if we got as far as opening them */ + if (ndbp->ndb_pdb) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + } + if (ndbp->ndb_idb) { + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + } + FREE(ndbp); + } + return 0; +} + +/* + * Description (ndbReOpen) + * + * This function is called to ensure that the primary DB file + * and/or the id-to-name DB file are open with specified access + * rights. For example, a file may be open for read, and it needs + * to be open for write. Both the primary and id-to-name DB files + * can be manipulated with a single call. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * flags - (currently unused - should be zero) + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned (NDBERRxxxx - see nsdb.h). If an error + * list is provided, an error frame will be generated when the + * return value is non-zero. + */ + +int ndbReOpen(NSErr_t * errp, void * ndb, int flags) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + char * dbname; /* database name pointer */ + int eid; + int rv; + + /* Want to read or write the primary DB file? */ + if (flags & (NDBF_RDNAME|NDBF_WRNAME)) { + + /* Need to open for write? */ + if ((flags & NDBF_WRNAME) && !(ndbp->ndb_flags & NDBF_WRNAME)) { + + /* If it's already open for read, close it first */ + if (ndbp->ndb_flags & NDBF_RDNAME) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + ndbp->ndb_flags &= ~NDBF_RDNAME; + } + + /* Create it if it doesn't exist */ + if (ndbp->ndb_flags & NDBF_NONAME) { + rv = ndbInitPrimary(errp, ndb); + if (rv) goto err_init; + } + + /* Open primary DB file for write access */ + dbname = ndbp->ndb_pname; + ndbp->ndb_pdb = dbopen(dbname, O_RDWR, NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_pdb) goto err_open1; + + /* Update flags to indicate successful open */ + ndbp->ndb_flags |= (NDBF_RDNAME|NDBF_WRNAME); + } + + /* Need to open for read? */ + if ((flags & NDBF_RDNAME) && !(ndbp->ndb_flags & NDBF_RDNAME)) { + + /* If it's already open for write, close it first */ + if (ndbp->ndb_flags & NDBF_WRNAME) { + (*ndbp->ndb_pdb->close)(ndbp->ndb_pdb); + ndbp->ndb_flags &= ~(NDBF_RDNAME|NDBF_WRNAME); + } + + /* Open primary DB file for read access */ + dbname = ndbp->ndb_pname; + ndbp->ndb_pdb = dbopen(dbname, O_RDONLY, NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_pdb) goto err_open2; + + /* Update flags to indicate successful open */ + ndbp->ndb_flags |= NDBF_RDNAME; + } + } + + /* Want to read or write the id-to-name DB file? */ + if (flags & (NDBF_RDID|NDBF_WRID)) { + + /* Need to open for write? */ + if ((flags & NDBF_WRID) && !(ndbp->ndb_flags & NDBF_WRID)) { + + /* + * If it's not open for read yet, try to open it for read + * in order to find out if it exists. + */ + if (!(ndbp->ndb_flags & NDBF_RDID)) { + + /* Open id-to-name DB file for read access */ + dbname = ndbp->ndb_iname; + ndbp->ndb_idb = dbopen(dbname, O_RDONLY, NDBMODE, DB_HASH,0); + + /* Does it exist? */ + if (ndbp->ndb_idb == 0) { + + /* No, create it */ + dbname = ndbp->ndb_iname; + ndbp->ndb_idb = dbopen(dbname,O_RDWR | O_CREAT | O_TRUNC, + NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_idb) goto err_open3; + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + } + else { + /* Mark it open for read */ + ndbp->ndb_flags |= NDBF_RDID; + } + } + + /* If it's already open for read, close it first */ + if (ndbp->ndb_flags & NDBF_RDID) { + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + ndbp->ndb_flags &= ~NDBF_RDID; + } + + /* Open id-to-name DB file for write access */ + dbname = ndbp->ndb_iname; + ndbp->ndb_idb = dbopen(dbname, O_RDWR, NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_idb) goto err_open4; + + /* Update flags to indicate successful open */ + ndbp->ndb_flags |= (NDBF_RDID|NDBF_WRID); + } + + /* Need to open for read? */ + if ((flags & NDBF_RDID) && !(ndbp->ndb_flags & NDBF_RDID)) { + + /* If it's already open for write, close it first */ + if (ndbp->ndb_flags & NDBF_WRID) { + (*ndbp->ndb_idb->close)(ndbp->ndb_idb); + ndbp->ndb_flags &= ~(NDBF_RDID|NDBF_WRID); + } + + /* Open id-to-name DB file for read access */ + dbname = ndbp->ndb_iname; + ndbp->ndb_idb = dbopen(dbname, O_RDONLY, NDBMODE, DB_HASH, 0); + if (!ndbp->ndb_idb) goto err_open5; + + /* Update flags to indicate successful open */ + ndbp->ndb_flags |= NDBF_RDID; + } + } + + /* Successful completion */ + return 0; + + /* Begin error handlers */ + + err_init: /* failed to create primary DB file */ + (void)nserrGenerate(errp, NDBERRPINIT, NSDBERR1600, NSDB_Program, + 1, + ndbp->ndb_pname /* primary DB filename */ + ); + rv = NDBERRPINIT; + goto punt; + + err_open1: + eid = NSDBERR1620; + goto err_open; + + err_open2: + eid = NSDBERR1640; + goto err_open; + + err_open3: + eid = NSDBERR1660; + goto err_open; + + err_open4: + eid = NSDBERR1680; + goto err_open; + + err_open5: + eid = NSDBERR1700; + goto err_open; + + err_open: /* database open error */ + rv = NDBERROPEN; + (void)nserrGenerate(errp, NDBERROPEN, eid, NSDB_Program, + 2, dbname, system_errmsg()); + + punt: + return rv; +} + +NSPR_END_EXTERN_C + diff --git a/lib/libaccess/nsdbmgmt.cpp b/lib/libaccess/nsdbmgmt.cpp new file mode 100644 index 00000000..c1151e08 --- /dev/null +++ b/lib/libaccess/nsdbmgmt.cpp @@ -0,0 +1,685 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsdbmgmt.h) + * + * The file describes the interface for managing information in + * a Netscape (server) database. A database is composed of + * two (libdbm) DB files. One of these (<dbname>.db) contains + * records indexed by a string key. These records contain the + * primary information in the database. A second DB file + * (<dbname>.id) is used to map an integer id value to a string + * key, which can then be used to locate a record in the first file. + * The interface for retrieving information from a database is + * described in nsdb.h. + */ + +#include <base/systems.h> +#include <netsite.h> +#include <base/file.h> +#define __PRIVATE_NSDB +#include <libaccess/nsdbmgmt.h> +#include <base/util.h> + +/* + * Description (ndbAllocId) + * + * This function allocates a unique id to be associated with a + * name in the primary DB file. An id bitmap is maintained in + * the primary DB file as a metadata record, and an entry is + * created in the id-to-name DB for the assigned id and the + * specified name. An allocated id value is always non-zero. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * namelen - length of key of the desired record, + * including null terminator if any + * name - pointer to the key of the desired record + * id - pointer to returned id value + * + * Returns: + * + * If successful, the return value is zero, and the allocated id + * is returned through 'id'. Otherwise a non-zero error code is + * returned (NDBERRxxxx - see nsdb.h). If an error list is + * provided, an error frame will be generated when the return + * value is non-zero. + */ + +int ndbAllocId(NSErr_t * errp, + void * ndb, int namelen, char * name, unsigned int * id) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + unsigned char * idmap; + unsigned char * newmap = 0; + int m; + int mmsk; + uint32 idval; + int myid; + int i, n; + int rv; + long eid; + + /* + * Ensure that the name does not start with the metadata + * prefix character. + */ + if (!name || (name[0] == NDB_MDPREFIX)) goto err_name; + + /* + * Read the primary DB file metadata record containing the id + * allocation bitmap. + */ + + /* + * We need the primary and the id-to-name DB files open for write + * (and implicitly read) access. + */ + if ((ndbp->ndb_flags & (NDBF_WRNAME|NDBF_WRID)) + != (NDBF_WRNAME|NDBF_WRID)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, (NDBF_WRNAME|NDBF_WRID)); + if (rv < 0) goto punt; + } + + /* Set the key to the id allocation bitmap record name */ + key.data = (void *)NDB_IDMAP; + key.size = strlen(NDB_IDMAP) + 1; + + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mdget; + + /* Search for an available id in the bitmap */ + n = rec.size; + idmap = (unsigned char *)rec.data; + + for (i = 0, m = 0; i < n; ++i) { + + m = idmap[i]; + if (m != 0) break; + } + + /* Did we find a byte with an available bit? */ + if (m == 0) { + + /* No, need to grow the bitmap */ + newmap = (unsigned char *)MALLOC(rec.size + 32); + if (newmap == 0) goto err_nomem1; + + /* Initialize free space at the beginning of the new map */ + for (i = 0; i < 32; ++i) { + newmap[i] = 0xff; + } + + /* Copy the old map after it */ + n += 32; + for ( ; i < n; ++i) { + newmap[i] = idmap[i-32]; + } + + /* Set i and m to allocate the new highest id value */ + i = 0; + m = 0xff; + } + else { + + /* + * It's unfortunate, but it appears to be necessary to copy the + * the ?idmap record into a new buffer before updating it, rather + * than simply updating it in place. The problem is that the + * libdbm put routine deletes the old record and then re-inserts + * it. But once it has deleted the old record, it may take the + * opportunity to move another record into the space that the + * old record occupied, which is the same space that the new + * record occupies. So the new record data is overwritten before + * new record is inserted. :-( + */ + + newmap = (unsigned char *)MALLOC(rec.size); + if (newmap == 0) goto err_nomem2; + + memcpy((void *)newmap, (void *)idmap, rec.size); + } + + /* Calculate the id associated with the low-order bit of byte i */ + myid = (n - i - 1) << 3; + + /* Find the first free (set) bit in that word */ + for (mmsk = 1; !(m & mmsk); mmsk <<= 1, myid += 1) ; + + /* Clear the bit */ + m &= ~mmsk; + newmap[i] = m; + + /* Write the bitmap back out */ + + rec.data = (void *)newmap; + rec.size = n; + + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0); + + /* Check for error on preceding put operation */ + if (rv) goto err_putpdb; + + /* Create the key for the id-to-name record */ + idval = myid; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(idval); +#endif + + key.data = (void *)&idval; + key.size = sizeof(uint32); + + rec.data = (void *)name; + rec.size = (namelen > 0) ? namelen : (strlen(name) + 1); + + /* Write the id-to-name record */ + rv = (*ndbp->ndb_idb->put)(ndbp->ndb_idb, &key, &rec, 0); + if (rv) goto err_putidb; + + /* Return the id value + 1, to avoid returning a zero id */ + if (id) *id = myid + 1; + + punt: + + /* Free the new map space if any */ + if (newmap) { + FREE(newmap); + } + + return rv; + + err_name: /* invalid name parameter */ + eid = NSDBERR2000; + rv = NDBERRNAME; + if (name == 0) { + name = "(null)"; + } + else if ((namelen > 0) && (namelen != strlen(name) + 1)) { + name = "(unprintable)"; + } + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB filename */ + name /* name string */ + ); + goto punt; + + err_mdget: /* error on get from primary DB file */ + eid = NSDBERR2020; + rv = NDBERRMDGET; + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB filename */ + (char *)key.data /* key name string */ + ); + goto punt; + + err_nomem1: + eid = NSDBERR2040; + goto err_nomem; + + err_nomem2: + eid = NSDBERR2060; + err_nomem: /* insufficient memory */ + rv = NDBERRNOMEM; + (void)nserrGenerate(errp, rv, eid, NSDB_Program, 0); + goto punt; + + err_putpdb: /* error on put to primary DB file */ + eid = NSDBERR2080; + rv = NDBERRMDPUT; + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB filename */ + (char *)key.data /* key name string */ + ); + goto punt; + + err_putidb: /* error on put to id-to-name DB */ + { + char idstring[16]; + + eid = NSDBERR2100; + rv = NDBERRIDPUT; + + util_sprintf(idstring, "%d", myid); + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_iname, /* id-to-name DB file */ + idstring /* id value for key */ + ); + } + goto punt; +} + +/* + * Description (ndbDeleteName) + * + * This function deletes a named record from the primary DB file. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * flags - (currently unused - should be zero) + * namelen - length of name key, including null + * terminator if any + * name - pointer to name key + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned (NDBERRxxxx - see nsdberr.h). If an error + * list is provided, an error frame will be generated when the + * return value is non-zero. + */ + +int ndbDeleteName(NSErr_t * errp, + void * ndb, int flags, int namelen, char * name) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + int eid; + int rv; + + /* Is the primary DB open for write access? */ + if (!(ndbp->ndb_flags & NDBF_WRNAME)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_WRNAME); + if (rv) goto punt; + } + + /* Set up the key descriptor */ + key.data = (void *)name; + key.size = (namelen > 0) ? namelen : (strlen(name) + 1); + + /* Delete the record from the primary DB file */ + rv = (*ndbp->ndb_pdb->del)(ndbp->ndb_pdb, &key, 0); + if (rv) goto err_delpdb; + + /* Successful completion */ + return 0; + + /* Begin error handlers */ + + err_delpdb: /* error deleting record from primary DB */ + eid = NSDBERR2200; + rv = NDBERRNMDEL; + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB name */ + (char *)key.data /* primary key */ + ); + punt: + return rv; +} + +/* + * Description (ndbFreeId) + * + * This function frees an id value associated with a name in the + * primary DB file. It is normally called when the named record + * is being deleted from the primary DB file. It deletes the + * record in the id-to-name DB file that is keyed by the id value, + * and updates the id allocation bitmap in the primary DB file. + * The caller may specify the name that is associated with the id + * value, in which case the id-to-name record will be fetched, + * and the name matched, before the record is deleted. Alternatively + * the name parameter can be specified as zero, and id-to-name + * record will be deleted without a check. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * namelen - length of name (including null terminator) + * name - name associated with the id value (optional) + * id - id value to be freed + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned, and an error frame is generated if the + * caller provided an error frame list. + */ + +int ndbFreeId(NSErr_t * errp, + void * ndb, int namelen, char * name, unsigned int id) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + char * recname; + DBT key; + DBT rec; + uint32 idval; + int reclen; + int mmsk; + unsigned char * idmap = 0; + int i; + int eid; + int rv; + + /* + * We need the primary and the id-to-name DB files open for write + * (and implicitly read) access. + */ + if ((ndbp->ndb_flags & (NDBF_WRNAME|NDBF_WRID)) + != (NDBF_WRNAME|NDBF_WRID)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, (NDBF_WRNAME|NDBF_WRID)); + if (rv) goto punt; + } + + /* Was the name for this id value provided by the caller? */ + if (name) { + + /* Get length of name if not provided */ + if (namelen <= 0) namelen = strlen(name) + 1; + + /* Yes, look up the id and check for a match */ + rv = ndbIdToName(errp, ndb, id, &reclen, &recname); + if (rv < 0) goto punt; + + /* Fail if the supplied name doesn't match */ + if ((namelen != reclen) || + strncmp(recname, name, reclen)) goto err_badid1; + } + + /* Caller views the id space as starting at 1, but we start at 0 */ + id -= 1; + + /* Create the key for the id-to-name record */ + idval = id; +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(idval); +#endif + + key.data = (void *)&idval; + key.size = sizeof(uint32); + + /* Delete the id-to-name record */ + rv = (*ndbp->ndb_idb->del)(ndbp->ndb_idb, &key, 0); + if (rv) goto err_del; + + /* Set the key to the id allocation bitmap record name */ + key.data = (void *)NDB_IDMAP; + key.size = strlen(NDB_IDMAP) + 1; + + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mdget; + + /* Make sure the id is in the range of the bitmap */ + i = (rec.size << 3) - id - 1; + if (i < 0) goto err_badid2; + + /* + * See comment in ndbAllocId() about updating ?idmap. Bottom line + * is: we have to copy the record before updating it. + */ + + idmap = (unsigned char *)MALLOC(rec.size); + if (idmap == 0) goto err_nomem; + + memcpy((void *)idmap, rec.data, rec.size); + + /* Calculate the index of the byte with this id's bit */ + i >>= 3; + + /* Calculate the bitmask for the bitmap byte */ + mmsk = 1 << (id & 7); + + /* Set the bit in the bitmap */ + idmap[i] |= mmsk; + + /* Write the bitmap back out */ + + rec.data = (void *)idmap; + + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0); + if (rv) goto err_mdput; + + punt: + + if (idmap) { + FREE(idmap); + } + + return rv; + + err_badid1: + /* Name associated with id doesn't match supplied name */ + eid = NSDBERR2300; + rv = NDBERRBADID; + goto err_id; + + err_del: + /* Error deleting id-to-name record */ + eid = NSDBERR2320; + rv = NDBERRIDDEL; + goto err_dbio; + + err_mdget: + /* Error reading id bitmap from primary DB file */ + eid = NSDBERR2340; + rv = NDBERRMDGET; + goto err_dbio; + + err_badid2: + eid = NSDBERR2360; + rv = NDBERRBADID; + err_id: + { + char idbuf[16]; + + util_sprintf(idbuf, "%d", id); + nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, idbuf); + } + goto punt; + + err_nomem: + eid = NSDBERR2380; + rv = NDBERRNOMEM; + nserrGenerate(errp, rv, eid, NSDB_Program, 0); + goto punt; + + err_mdput: + eid = NSDBERR2400; + rv = NDBERRMDPUT; + goto err_dbio; + + err_dbio: + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_pname, system_errmsg()); + goto punt; +} + +/* + * Description (ndbRenameId) + * + * This function changes the name associated with a specified id + * int the id-to-name DB file. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * namelen - length of new name string, including + * null terminator if any + * newname - pointer to the new name string + * id - id value to be renamed + * + * Returns: + * + * The return value is zero if the operation is successful. An + * error is indicated by a non-zero return value, and an error + * frame is generated if the caller provided an error frame list. + */ + +int ndbRenameId(NSErr_t * errp, + void * ndb, int namelen, char * newname, unsigned int id) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + uint32 idval = id - 1; + int eid; + int rv; + + /* + * Ensure that the name does not start with the metadata + * prefix character. + */ + if (!newname || (newname[0] == NDB_MDPREFIX)) goto err_name; + + /* + * We need the id-to-name DB file open for write + * (and implicitly read) access. + */ + if (!(ndbp->ndb_flags & NDBF_WRID)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_WRID); + if (rv) goto punt; + } + + /* Set up record key */ +#if BYTE_ORDER == LITTLE_ENDIAN + M_32_SWAP(idval); +#endif + key.data = (void *)&idval; + key.size = sizeof(uint32); + + rec.data = 0; + rec.size = 0; + + /* Retrieve the record by its key */ + rv = (*ndbp->ndb_idb->get)(ndbp->ndb_idb, &key, &rec, 0); + if (rv) goto err_idget; + + /* Set up to write the new name */ + rec.data = (void *)newname; + rec.size = (namelen > 0) ? namelen : (strlen(newname) + 1); + + /* Write the id-to-name record */ + rv = (*ndbp->ndb_idb->put)(ndbp->ndb_idb, &key, &rec, 0); + if (rv) goto err_idput; + + punt: + return rv; + + err_name: + eid = NSDBERR2500; + rv = NDBERRNAME; + if (newname == 0) newname = "(null)"; + else if ((namelen > 0) && (namelen != (strlen(newname) + 1))) { + newname = "(unprintable)"; + } + (void)nserrGenerate(errp, rv, eid, NSDB_Program, + 2, + ndbp->ndb_pname, /* primary DB filename */ + newname /* name string */ + ); + goto punt; + + err_idget: + /* Error getting id record from id-to-name database */ + eid = NSDBERR2520; + rv = NDBERRGET; + goto err_dbio; + + err_idput: + /* Error putting id record back to id-to-name database */ + eid = NSDBERR2540; + rv = NDBERRIDPUT; + err_dbio: + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_pname, system_errmsg()); + goto punt; +} + +/* + * Description (ndbStoreName) + * + * This function stores a record, keyed by a specified name, in the + * primary DB file. The record will overwrite any existing record + * with the same key, unless NDBF_NEWNAME, is included in the 'flags' + * argument. If NDBF_NEWNAME is set, and the record already exists, + * it is not overwritten, and an error is returned. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * ndb - database handle from ndbOpen() + * flags - bit flags: + * NDBF_NEWNAME - name is new + * namelen - length of name key, including null + * terminator if any + * name - pointer to name key + * reclen - length of the record data + * recptr - pointer to the record data + * + * Returns: + * + * If successful, the return value is zero. Otherwise a non-zero + * error code is returned, and an error frame is generated if the + * caller provided an error frame list. + */ + +int ndbStoreName(NSErr_t * errp, void * ndb, int flags, + int namelen, char * name, int reclen, char * recptr) +{ + NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */ + DBT key; + DBT rec; + int eid; + int rv; + + /* Is the primary DB open for write access? */ + if (!(ndbp->ndb_flags & NDBF_WRNAME)) { + + /* No, (re)open it */ + rv = ndbReOpen(errp, ndb, NDBF_WRNAME); + if (rv) goto punt; + } + + /* Set up the key and record descriptors */ + key.data = (void *)name; + key.size = (namelen > 0) ? namelen : (strlen(name) + 1); + + rec.data = (void *)recptr; + rec.size = reclen; + + /* Write the record to the primary DB file */ + rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, + (flags & NDBF_NEWNAME) ? R_NOOVERWRITE : 0); + if (rv) goto err_put; + + punt: + return rv; + + err_put: + eid = NSDBERR2700; + rv = NDBERRPUT; + nserrGenerate(errp, rv, eid, NSDB_Program, + 2, ndbp->ndb_pname, system_errmsg()); + goto punt; +} diff --git a/lib/libaccess/nseframe.cpp b/lib/libaccess/nseframe.cpp new file mode 100644 index 00000000..c878940b --- /dev/null +++ b/lib/libaccess/nseframe.cpp @@ -0,0 +1,207 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nseframe.c) + * + * This module is part of the NSACL_RES_ERROR facility. It contains functions + * for allocating, freeing, and managing error frame structures. It + * does not contain routines for generating error messages through + * the use of a message file. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "libaccess/nserror.h" + +/* + * Description (nserrDispose) + * + * This function is used to dispose of an entire list of error + * frames when the error information is no longer needed. It + * does not free the list head, since it is usually not dynamically + * allocated. + * + * Arguments: + * + * errp - error frame list head pointer + */ + +void nserrDispose(NSErr_t * errp) +{ + /* Ignore null list head */ + if (errp == 0) return; + + while (errp->err_first) { + + nserrFFree(errp, errp->err_first); + } +} + +/* + * Description (nserrFAlloc) + * + * This is the default allocator for error frame structures. It + * calls an allocator function indicated by err_falloc, if any, + * or else uses MALLOC(). + * + * Arguments: + * + * errp - error frame list head pointer + * (may be null) + * + * Returns: + * + * The return value is a pointer to a cleared error frame. The + * frame will not have been added to the list referenced by errp. + * An allocation error is indicated by a null return value. + */ + +NSEFrame_t * nserrFAlloc(NSErr_t * errp) +{ + NSEFrame_t * efp; /* return error frame pointer */ + + /* Allocate the error frame */ + efp = (errp && errp->err_falloc) + ? (*errp->err_falloc)(errp) + : (NSEFrame_t *)MALLOC(sizeof(NSEFrame_t)); + + if (efp) { + /* Clear the error frame */ + memset((void *)efp, 0, sizeof(NSEFrame_t)); + } + + return efp; +} + +/* + * Description (nserrFFree) + * + * This function frees an error frame structure. If an error list + * head is specified, it first checks whether the indicated error + * frame is on the list, and removes it if so. If the ef_dispose + * field is non-null, the indicated function is called. The error + * frame is deallocated using either a function indicated by + * err_free in the list head, or FREE() otherwise. + * + * Arguments: + * + * errp - error frame list head pointer + * (may be null) + * efp - error frame pointer + */ + +void nserrFFree(NSErr_t * errp, NSEFrame_t * efp) +{ + NSEFrame_t **lefp; /* pointer to error frame pointer */ + NSEFrame_t * pefp; /* previous error frame on list */ + int i; + + /* Ignore null error frame pointer */ + if (efp == 0) return; + + /* Got a list head? */ + if (errp) { + + /* Yes, see if this frame is on the list */ + pefp = 0; + for (lefp = &errp->err_first; *lefp != 0; lefp = &pefp->ef_next) { + if (*lefp == efp) { + + /* Yes, remove it */ + *lefp = efp->ef_next; + if (errp->err_last == efp) errp->err_last = pefp; + break; + } + pefp = *lefp; + } + } + + /* Free strings referenced by the frame */ + for (i = 0; i < efp->ef_errc; ++i) { + if (efp->ef_errv[i]) { + FREE(efp->ef_errv[i]); + } + } + + /* Free the frame */ + if (errp && errp->err_ffree) { + (*errp->err_ffree)(errp, efp); + } + else { + FREE(efp); + } +} + +/* + * Description (nserrGenerate) + * + * This function is called to generate an error frame and add it + * to a specified error list. + * + * Arguments: + * + * errp - error frame list head pointer + * (may be null) + * retcode - return code (ef_retcode) + * errorid - error id (ef_errorid) + * program - program string pointer (ef_program) + * errc - count of error arguments (ef_errc) + * ... - values for ef_errv[] + * + * Returns: + * + * The return value is a pointer to the generated error frame, filled + * in with the provided information. An allocation error is indicated + * by a null return value. + */ + +NSEFrame_t * nserrGenerate(NSErr_t * errp, long retcode, long errorid, + char * program, int errc, ...) +{ + NSEFrame_t * efp; /* error frame pointer */ + char * esp; /* error string pointer */ + int i; + va_list ap; + + /* Null frame list head pointer means do nothing */ + if (errp == 0) { + return 0; + } + + /* Limit the number of values in ef_errv[] */ + if (errc > NSERRMAXARG) errc = NSERRMAXARG; + + /* Allocate the error frame */ + efp = nserrFAlloc(errp); + + /* Did we get it? */ + if (efp) { + + /* Yes, copy information into it */ + efp->ef_retcode = retcode; + efp->ef_errorid = errorid; + efp->ef_program = program; + efp->ef_errc = errc; + + /* Get the string arguments and copy them */ + va_start(ap, errc); + for (i = 0; i < errc; ++i) { + esp = va_arg(ap, char *); + efp->ef_errv[i] = STRDUP(esp); + } + + /* Add the frame to the list (if any) */ + if (errp) { + efp->ef_next = errp->err_first; + errp->err_first = efp; + if (efp->ef_next == 0) errp->err_last = efp; + } + } + + /* Return the error frame pointer */ + return efp; +} diff --git a/lib/libaccess/nsgmgmt.cpp b/lib/libaccess/nsgmgmt.cpp new file mode 100644 index 00000000..922b21bc --- /dev/null +++ b/lib/libaccess/nsgmgmt.cpp @@ -0,0 +1,434 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsgmgmt.c) + * + * This module contains routines for managing information in a + * Netscape group database. Information for a particular group + * is modified by retrieving the current information in the form + * of a group object (GroupObj_t), calling functions in this module, + * to modify the group object, and then calling groupStore() to + * write the information in the group object back to the database. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#include "libaccess/nsdbmgmt.h" +#define __PRIVATE_NSGROUP +#include "libaccess/nsgmgmt.h" + +/* + * Description (groupAddMember) + * + * This function adds a member to a group object. The member may + * be another group or a user, expressed as a group id or user id, + * respectively. The 'isgid' argument is non-zero if the new + * member is a group, or zero if it is a user. + * + * Arguments: + * + * goptr - group object pointer + * isgid - non-zero if 'id' is a group id + * zero if 'id' is a user id + * id - group or user id to be added + * + * Returns: + * + * Returns zero if the specified member is already a direct member + * of the group. Returns one if the member was added successfully. + */ + +NSAPI_PUBLIC int groupAddMember(GroupObj_t * goptr, int isgid, USI_t id) +{ + USIList_t * uilptr; + int rv = 0; + + /* Point to the relevant uid or gid list */ + uilptr = (isgid) ? &goptr->go_groups : &goptr->go_users; + + /* Add the id to the selected list */ + rv = usiInsert(uilptr, id); + if (rv > 0) { + goptr->go_flags |= GOF_MODIFIED; + } + + return rv; +} + +/* + * Description (groupCreate) + * + * This function creates a group object, using information about + * the group provided by the caller. The strings passed for the + * group name and description may be on the stack. The group id + * is set to zero, but the group object is marked as being new. + * A group id will be assigned when groupStore() is called to add + * the group to a group database. + * + * Arguments: + * + * name - pointer to group name string + * desc - pointer to group description string + * + * Returns: + * + * A pointer to a dynamically allocated GroupObj_t structure is + * returned. + */ + +NSAPI_PUBLIC GroupObj_t * groupCreate(NTS_t name, NTS_t desc) +{ + GroupObj_t * goptr; /* group object pointer */ + + goptr = (GroupObj_t *)MALLOC(sizeof(GroupObj_t)); + if (goptr) { + goptr->go_name = (NTS_t)STRDUP((char *)name); + goptr->go_gid = 0; + goptr->go_flags = (GOF_MODIFIED | GOF_NEW); + if (desc) { + goptr->go_desc = (desc) ? (NTS_t)STRDUP((char *)desc) : 0; + } + UILINIT(&goptr->go_users); + UILINIT(&goptr->go_groups); + UILINIT(&goptr->go_pgroups); + } + + return goptr; +} + +/* + * Description (groupDeleteMember) + * + * This function removes a specified member from a group object's + * list of members. The member to be remove may be a group or a + * user, expressed as a group id or user id, respectively. The + * 'isgid' argument is non-zero if the member being removed is a + * group, or zero if it is a user. + * + * Arguments: + * + * goptr - pointer to group object + * isgid - non-zero if 'id' is a group id + * zero if 'id' is a user id + * id - group or user id to be removed + * + * Returns: + * + * The return value is zero if the specified member was not present + * in the group object, or one if the member was successfully removed. + */ + +NSAPI_PUBLIC int groupDeleteMember(GroupObj_t * goptr, int isgid, USI_t id) +{ + USIList_t * uilptr; /* pointer to list of member users or groups */ + int rv; /* return value */ + + /* Get pointer to appropriate list of ids */ + uilptr = (isgid) ? &goptr->go_groups : &goptr->go_users; + + /* Remove the specified id */ + rv = usiRemove(uilptr, id); + if (rv > 0) { + goptr->go_flags |= GOF_MODIFIED; + } + + return rv; +} + +/* + * Description (groupEncode) + * + * This function encodes a group object into a group DB record. + * + * Arguments: + * + * goptr - pointer to group object + * greclen - pointer to returned record length + * grecptr - pointer to returned record pointer + * + * Returns: + * + * The function return value is zero if successful. The length + * and location of the created attribute record are returned + * through 'greclen' and 'grecptr'. A non-zero function value + * is returned if there's an error. + */ + +NSAPI_PUBLIC int groupEncode(GroupObj_t * goptr, int * greclen, ATR_t * grecptr) +{ + int reclen; /* length of DB record */ + ATR_t rptr; /* DB record pointer */ + ATR_t rstart = 0; /* pointer to beginning of DB record */ + ATR_t glptr; /* saved pointer to UAT_GROUPS length */ + ATR_t gptr; /* saved pointer to after length at glptr */ + int gidlen; /* gid encoding length */ + int fllen; /* flags encoding length */ + USI_t dsclen; /* group description encoding length */ + USI_t nulen; /* member user count encoding length */ + USI_t nglen; /* member group count encoding length */ + int idcnt; /* count of user or group ids */ + USI_t * ids; /* pointer to array of user or group ids */ + int i; /* id index */ + int rv = -1; + + /* + * First we need to figure out how long the generated record will be. + * This doesn't have to be exact, but it must not be smaller than the + * actual record size. + */ + + /* GAT_GID attribute: tag, length, USI */ + gidlen = USILENGTH(goptr->go_gid); + reclen = (1 + 1 + gidlen); + + /* GAT_FLAGS attribute: tag, length, USI */ + fllen = USILENGTH(goptr->go_flags & GOF_DBFLAGS); + reclen += (1 + 1 + fllen); + + /* GAT_DESCRIPT attribute: tag, length, NTS */ + dsclen = NTSLENGTH(goptr->go_desc); + reclen += (1 + USILENGTH(dsclen) + dsclen); + + /* GAT_USERS attribute: tag, length, USI(count), USI(uid)... */ + idcnt = UILCOUNT(&goptr->go_users); + nulen = USILENGTH(idcnt); + reclen += (1 + USIALLOC() + nulen + (5 * idcnt)); + + /* GAT_GROUPS attribute: tag, length, USI(count), USI(gid)... */ + idcnt = UILCOUNT(&goptr->go_groups); + nglen = USILENGTH(idcnt); + reclen += (1 + USIALLOC() + nglen + (5 * idcnt)); + + /* GAT_PGROUPS attribute: tag, length, USI(count), USI(gid)... */ + idcnt = UILCOUNT(&goptr->go_pgroups); + nglen = USILENGTH(idcnt); + reclen += (1 + USIALLOC() + nglen + (5 * idcnt)); + + /* Allocate the attribute record buffer */ + rptr = (ATR_t)MALLOC(reclen); + if (rptr) { + + /* Save pointer to start of record */ + rstart = rptr; + + /* Encode GAT_GID attribute */ + *rptr++ = GAT_GID; + *rptr++ = gidlen; + rptr = USIENCODE(rptr, goptr->go_gid); + + /* Encode GAT_FLAGS attribute */ + *rptr++ = GAT_FLAGS; + *rptr++ = fllen; + rptr = USIENCODE(rptr, (goptr->go_flags & GOF_DBFLAGS)); + + /* Encode GAT_DESCRIPT attribute */ + *rptr++ = GAT_DESCRIPT; + rptr = USIENCODE(rptr, dsclen); + rptr = NTSENCODE(rptr, goptr->go_desc); + + /* Encode GAT_USERS attribute */ + *rptr++ = GAT_USERS; + + /* + * Save a pointer to the attribute encoding length, and reserve + * space for the maximum encoding size of a USI_t value. + */ + glptr = rptr; + rptr += USIALLOC(); + gptr = rptr; + + /* Encode number of user members */ + idcnt = UILCOUNT(&goptr->go_users); + rptr = USIENCODE(rptr, idcnt); + + /* Generate user ids encodings */ + ids = UILLIST(&goptr->go_users); + for (i = 0; i < idcnt; ++i) { + rptr = USIENCODE(rptr, ids[i]); + } + + /* Now fix up the GAT_USERS attribute encoding length */ + glptr = USIINSERT(glptr, (USI_t)(rptr - gptr)); + + /* Encode GAT_GROUPS attribute */ + *rptr++ = GAT_GROUPS; + + /* + * Save a pointer to the attribute encoding length, and reserve + * space for the maximum encoding size of a USI_t value. + */ + glptr = rptr; + rptr += USIALLOC(); + gptr = rptr; + + /* Encode number of groups */ + idcnt = UILCOUNT(&goptr->go_groups); + rptr = USIENCODE(rptr, idcnt); + + /* Generate group ids encodings */ + ids = UILLIST(&goptr->go_groups); + for (i = 0; i < idcnt; ++i) { + rptr = USIENCODE(rptr, ids[i]); + } + + /* Now fix up the GAT_GROUPS attribute encoding length */ + glptr = USIINSERT(glptr, (USI_t)(rptr - gptr)); + + /* Encode GAT_PGROUPS attribute */ + *rptr++ = GAT_PGROUPS; + + /* + * Save a pointer to the attribute encoding length, and reserve + * space for the maximum encoding size of a USI_t value. + */ + glptr = rptr; + rptr += USIALLOC(); + gptr = rptr; + + /* Encode number of groups */ + idcnt = UILCOUNT(&goptr->go_pgroups); + rptr = USIENCODE(rptr, idcnt); + + /* Generate group ids encodings */ + ids = UILLIST(&goptr->go_pgroups); + for (i = 0; i < idcnt; ++i) { + rptr = USIENCODE(rptr, ids[i]); + } + + /* Now fix up the GAT_PGROUPS attribute encoding length */ + glptr = USIINSERT(glptr, (USI_t)(rptr - gptr)); + + /* Return record length and location if requested */ + if (greclen) *greclen = rptr - rstart; + if (grecptr) *grecptr = rstart; + + /* Indicate success */ + rv = 0; + } + + return rv; +} + +/* + * Description (groupRemove) + * + * This function is called to remove a group from a specified group + * database. Both the primary DB file and the id-to-name DB file + * are updated. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * flags - (unused - must be zero) + * name - pointer to group name + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. + */ + +NSAPI_PUBLIC int groupRemove(NSErr_t * errp, void * groupdb, int flags, NTS_t name) +{ + GroupObj_t * goptr; /* group object pointer */ + int rv; + int rv2; + + /* First retrieve the group record */ + goptr = groupFindByName(errp, groupdb, name); + if (!goptr) { + /* Error - specified group not found */ + return NSAERRNAME; + } + + /* Free the group id value, if any */ + rv = 0; + if (goptr->go_gid != 0) { + rv = ndbFreeId(errp, groupdb, 0, (char *)name, goptr->go_gid); + } + + rv2 = ndbDeleteName(errp, groupdb, 0, 0, (char *)name); + + return (rv) ? rv : rv2; +} + +/* + * Description (groupStore) + * + * This function is called to store a group object in the database. + * If the object was created by groupCreate(), it is assumed to be + * a new group, the group account name must not match any existing + * group account names in the database, and a gid is assigned before + * adding the group to the database. If the object was created by + * groupFindByName(), the information in the group object will + * replace the existing database entry for the indicated group + * name. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * flags - (unused - must be zero) + * goptr - group object pointer + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. The group object remains intact in either + * case. + */ + +NSAPI_PUBLIC int groupStore(NSErr_t * errp, void * groupdb, int flags, GroupObj_t * goptr) +{ + ATR_t recptr = 0; + USI_t gid; + int reclen = 0; + int stflags = 0; + int eid; + int rv; + + /* If this is a new group, allocate a uid value */ + if (goptr->go_flags & GOF_NEW) { + + rv = ndbAllocId(errp, groupdb, 0, (char *)goptr->go_name, &gid); + if (rv) goto punt; + + goptr->go_gid = gid; + + /* Let the database manager know that this is a new entry */ + stflags = NDBF_NEWNAME; + } + + /* Convert the information in the group object to a DB record */ + rv = groupEncode(goptr, &reclen, &recptr); + if (rv) goto err_nomem; + + /* + * Store the record in the database under the group name. + * If this is a new entry, a group id to group name mapping + * also will be added to the id-to-name DB file. + */ + rv = ndbStoreName(errp, groupdb, stflags, + 0, (char *)goptr->go_name, reclen, (char *)recptr); + + FREE(recptr); + + if (rv == 0) { + goptr->go_flags &= ~(GOF_NEW | GOF_MODIFIED); + } + + punt: + return rv; + + err_nomem: + eid = NSAUERR2000; + rv = NSAERRNOMEM; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; +} diff --git a/lib/libaccess/nsgroup.cpp b/lib/libaccess/nsgroup.cpp new file mode 100644 index 00000000..c67c85ba --- /dev/null +++ b/lib/libaccess/nsgroup.cpp @@ -0,0 +1,336 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsgroup.c) + * + * This module contains routines for accessing information in a + * Netscape group database. Group information is returned in the + * form of a group object (GroupObj_t), defined in nsauth.h. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#define __PRIVATE_NSGROUP +#include "libaccess/nsgroup.h" + +/* + * Description (groupDecode) + * + * This function decodes an external group DB record into a + * dynamically allocated GroupObj_t structure. The DB record is + * encoded as an attribute record as defined in attrec.h. + * + * Arguments: + * + * name - pointer to group name string + * greclen - length of the group DB record, in octets + * grecptr - pointer to group DB record + * + * Returns: + * + * A pointer to the allocated GroupObj_t structure is returned. + */ + +NSAPI_PUBLIC GroupObj_t * groupDecode(NTS_t name, int greclen, ATR_t grecptr) +{ + ATR_t cp = grecptr; /* current pointer into DB record */ + USI_t tag; /* attribute tag */ + USI_t len; /* attribute value encoding length */ + int i; /* group id index */ + int idcnt; /* count of user or group ids */ + USI_t * ids; /* pointer to array of ids */ + GroupObj_t * goptr; /* group object pointer */ + + /* Allocate a group object structure */ + goptr = (GroupObj_t *)MALLOC(sizeof(GroupObj_t)); + if (goptr) { + + goptr->go_name = (unsigned char *) STRDUP((char *)name); + goptr->go_gid = 0; + goptr->go_flags = GOF_MODIFIED; + goptr->go_desc = 0; + UILINIT(&goptr->go_users); + UILINIT(&goptr->go_groups); + UILINIT(&goptr->go_pgroups); + + /* Parse group DB record */ + while ((cp - grecptr) < greclen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* Process this attribute */ + switch (tag) { + + case GAT_GID: /* group id */ + cp = USIDECODE(cp, &goptr->go_gid); + break; + + case GAT_FLAGS: /* flags */ + cp = USIDECODE(cp, &goptr->go_flags); + break; + + case GAT_DESCRIPT: /* group description */ + cp = NTSDECODE(cp, &goptr->go_desc); + break; + + case GAT_USERS: /* member users of this group */ + + /* First get the number of user ids following */ + cp = USIDECODE(cp, (unsigned *)&idcnt); + + if (idcnt > 0) { + + /* Allocate space for user ids */ + ids = usiAlloc(&goptr->go_users, idcnt); + if (ids) { + for (i = 0; i < idcnt; ++i) { + cp = USIDECODE(cp, ids + i); + } + } + } + break; + + case GAT_GROUPS: /* member groups of this group */ + + /* First get the number of group ids following */ + cp = USIDECODE(cp, (unsigned *)&idcnt); + + if (idcnt > 0) { + + /* Allocate space for group ids */ + ids = usiAlloc(&goptr->go_groups, idcnt); + if (ids) { + for (i = 0; i < idcnt; ++i) { + cp = USIDECODE(cp, ids + i); + } + } + } + break; + + case GAT_PGROUPS: /* parent groups of this group */ + + /* First get the number of group ids following */ + cp = USIDECODE(cp, (USI_t *)&idcnt); + + if (idcnt > 0) { + + /* Allocate space for group ids */ + ids = usiAlloc(&goptr->go_pgroups, idcnt); + if (ids) { + for (i = 0; i < idcnt; ++i) { + cp = USIDECODE(cp, ids + i); + } + } + } + break; + + default: /* unrecognized attribute */ + /* Just skip it */ + cp += len; + break; + } + } + } + + return goptr; +} + +/* + * Description (groupEnumHelp) + * + * This is a local function that is called by NSDB during group + * database enumeration. It decodes group records into group + * objects, and presents them to the caller of groupEnumerate(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to GroupEnumArgs_t structure + * namelen - length of group record key, including null + * terminator + * name - group record key (group account name) + * reclen - length of group record + * recptr - pointer to group record contents + * + * Returns: + * + * Returns whatever value is returned from the upcall to the caller + * of groupEnumerate(). + */ + +static int groupEnumHelp(NSErr_t * errp, void * parg, + int namelen, char * name, int reclen, char * recptr) +{ + GroupEnumArgs_t * ge = (GroupEnumArgs_t *)parg; + GroupObj_t * goptr; /* group object pointer */ + int rv; + + goptr = groupDecode((NTS_t)name, reclen, (ATR_t)recptr); + + rv = (*ge->func)(errp, ge->user, goptr); + + if (!(ge->flags & GOF_ENUMKEEP)) { + FREE(goptr); + } + + return rv; +} + +/* + * Description (groupEnumerate) + * + * This function enumerates all of the groups in a specified group + * database, calling a caller-specified function with a group object + * for each group in the database. A 'flags' value of GOF_ENUMKEEP + * can be specified to keep the group objects around (not free them) + * after the caller's function returns. Otherwise, each group + * object is freed after being presented to the caller's function. + * The 'argp' argument is an opaque pointer, which is passed to + * the caller's function as 'parg' on each call, along with a + * group object pointer. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * flags - bit flags: + * GOF_ENUMKEEP - keep group objects + * argp - passed to 'func' as 'parg' + * func - pointer to caller's enumeration function + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. + */ + +NSAPI_PUBLIC int groupEnumerate(NSErr_t * errp, void * groupdb, int flags, void * argp, + int (*func)(NSErr_t * ferrp, + void * parg, GroupObj_t * goptr)) +{ + int rv; + GroupEnumArgs_t args; + + args.groupdb = groupdb; + args.flags = flags; + args.func = func; + args.user = argp; + + rv = ndbEnumerate(errp, + groupdb, NDBF_ENUMNORM, (void *)&args, groupEnumHelp); + + return rv; +} + +/* + * Description (groupFindByName) + * + * This function looks up a group record for a specified group name, + * converts the group record to the internal group object form, and + * returns a pointer to the group object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * name - group name to find + * + * Returns: + * + * If successful, the return value is a pointer to a group object + * for the specified group. Otherwise it is 0. + */ + +NSAPI_PUBLIC GroupObj_t * groupFindByName(NSErr_t * errp, void * groupdb, NTS_t name) +{ + GroupObj_t * goptr = 0; + ATR_t grecptr; + int greclen; + int rv; + + /* Look up the group name in the database */ + rv = ndbFindName(errp, groupdb, 0, (char *)name, &greclen, (char **)&grecptr); + if (rv == 0) { + + /* Got the group record. Decode into a group object. */ + goptr = groupDecode(name, greclen, grecptr); + } + + return goptr; +} + +/* + * Description (groupFindByGid) + * + * This function looks up a group record for a specified group id, + * converts the group record to the internal group object form, and + * returns a pointer to the group object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * groupdb - handle for group DB access + * gid - group id to find + * + * Returns: + * + * If successful, the return value is a pointer to a group object + * for the specified group. Otherwise it is 0. + */ + +NSAPI_PUBLIC GroupObj_t * groupFindByGid(NSErr_t * errp, void * groupdb, USI_t gid) +{ + GroupObj_t * goptr = 0; + NTS_t name; + ATR_t grecptr; + int greclen; + int rv; + + /* Get the group account name corresponding to the gid */ + rv = ndbIdToName(errp, groupdb, gid, 0, (char **)&name); + if (rv == 0) { + + rv = ndbFindName(errp, groupdb, 0, (char *)name, &greclen, (char **)&grecptr); + if (rv == 0) { + + /* Got the group record. Decode into a group object. */ + goptr = groupDecode(name, greclen, grecptr); + } + } + + return goptr; +} + +/* + * Description (groupFree) + * + * This function is called to free a group object. Group objects + * are not automatically freed when a group database is closed. + * + * Arguments: + * + * goptr - group object pointer + * + */ + +NSAPI_PUBLIC void groupFree(GroupObj_t * goptr) +{ + if (goptr) { + + if (goptr->go_name) FREE(goptr->go_name); + if (goptr->go_desc) FREE(goptr->go_desc); + UILFREE(&goptr->go_users); + UILFREE(&goptr->go_groups); + UILFREE(&goptr->go_pgroups); + FREE(goptr); + } +} diff --git a/lib/libaccess/nslock.cpp b/lib/libaccess/nslock.cpp new file mode 100644 index 00000000..2cf0b00b --- /dev/null +++ b/lib/libaccess/nslock.cpp @@ -0,0 +1,268 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nslock.c) + * + * This modules provides an interprocess locking mechanism, based + * on a named lock. + */ + +#include "netsite.h" +#include "base/file.h" +#define __PRIVATE_NSLOCK +#include "nslock.h" +#include <assert.h> + +char * NSLock_Program = "NSLOCK"; + +#ifdef FILE_UNIX +/* + * The process-wide list of locks, NSLock_List, is protected by the + * critical section, NSLock_Crit. + */ +CRITICAL NSLock_Crit = 0; +NSLock_t * NSLock_List = 0; +#endif /* FILE_UNIX */ + +/* + * Description (nsLockOpen) + * + * This function is used to initialize a handle for a lock. The + * caller specifies a unique name for the lock, and a handle is + * returned. The returned handle should be used by only one + * thread at a time, i.e. if multiple threads in a process are + * using the same lock, they should either have their own handles + * or protect a single handle with a critical section. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * lockname - pointer to name of lock + * plock - pointer to returned handle for lock + * + * Returns: + * + * If successful, a handle for the specified lock is returned via + * 'plock', and the return value is zero. Otherwise the return + * value is a negative error code (see nslock.h), and an error + * frame is generated if an error frame list was provided. + */ + +NSAPI_PUBLIC int nsLockOpen(NSErr_t * errp, char * lockname, void **plock) +{ + NSLock_t * nl = 0; /* pointer to lock structure */ + int len; /* length of lockname */ + int eid; + int rv; + +#ifdef FILE_UNIX + /* Have we created the critical section for NSLock_List yet? */ + if (NSLock_Crit == 0) { + + /* Narrow the window for simultaneous initialization */ + NSLock_Crit = (CRITICAL)(-1); + + /* Create it */ + NSLock_Crit = crit_init(); + } + + /* Lock the list of locks */ + crit_enter(NSLock_Crit); + + /* See if a lock with the specified name exists already */ + for (nl = NSLock_List; nl != 0; nl = nl->nl_next) { + if (!strcmp(nl->nl_name, lockname)) break; + } + + /* Create a new lock if we didn't find it */ + if (nl == 0) { + + len = strlen(lockname); + + nl = (NSLock_t *)PERM_MALLOC(sizeof(NSLock_t) + len + 5); + if (nl == 0) goto err_nomem; + + nl->nl_name = (char *)(nl + 1); + strcpy(nl->nl_name, lockname); + strcpy(&nl->nl_name[len], ".lck"); + nl->nl_cnt = 0; + + nl->nl_fd = open(nl->nl_name, O_RDWR|O_CREAT|O_EXCL, 0644); + if (nl->nl_fd < 0) { + + if (errno != EEXIST) { + crit_exit(NSLock_Crit); + goto err_create; + } + + /* O_RDWR or O_WRONLY is required to use lockf on Solaris */ + nl->nl_fd = open(nl->nl_name, O_RDWR, 0); + if (nl->nl_fd < 0) { + crit_exit(NSLock_Crit); + goto err_open; + } + } + + /* Remove ".lck" from the lock name */ + nl->nl_name[len] = 0; + + /* Create a critical section for this lock (gag!) */ + nl->nl_crit = crit_init(); + + /* Add this lock to NSLock_List */ + nl->nl_next = NSLock_List; + NSLock_List = nl; + } + + crit_exit(NSLock_Crit); + +#else +/* write me */ + nl = (void *)4; +#endif /* FILE_UNIX */ + + *plock = (void *)nl; + return 0; + + err_nomem: + eid = NSLERR1000; + rv = NSLERRNOMEM; + nserrGenerate(errp, rv, eid, NSLock_Program, 0); + goto punt; + + err_create: + eid = NSLERR1020; + rv = NSLERRCREATE; + goto err_file; + + err_open: + eid = NSLERR1040; + rv = NSLERROPEN; + err_file: + nserrGenerate(errp, rv, eid, NSLock_Program, 1, nl->nl_name); + punt: + if (nl) { + FREE(nl); + } + *plock = 0; + return rv; +} + +/* + * Description (nsLockAcquire) + * + * This function is used to acquire exclusive ownership of a lock + * previously accessed via nsLockOpen(). The calling thread will + * be blocked until the lock is acquired. Other threads in the + * process should not be blocked. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * lock - handle for lock from nsLockOpen() + * + * Returns: + * + * If successful, the return value is zero. Otherwise the return + * value is a negative error code (see nslock.h), and an error + * frame is generated if an error frame list was provided. + */ + +NSAPI_PUBLIC int nsLockAcquire(NSErr_t * errp, void * lock) +{ + NSLock_t * nl = (NSLock_t *)lock; + int eid; + int rv; + +#ifdef FILE_UNIX + /* Enter the critical section for the lock */ + crit_enter(nl->nl_crit); + + /* Acquire the file lock if we haven't already */ + if (nl->nl_cnt == 0) { + rv = system_flock(nl->nl_fd); + if (rv) { + crit_exit(nl->nl_crit); + goto err_lock; + } + } + + /* Bump the lock count */ + nl->nl_cnt++; + + crit_exit(nl->nl_crit); +#else + /* write me */ +#endif /* FILE_UNIX */ + + /* Indicate success */ + return 0; + + err_lock: + eid = NSLERR1100; + rv = NSLERRLOCK; + nserrGenerate(errp, rv, eid, NSLock_Program, 1, nl->nl_name); + + return rv; +} + +/* + * Description (nsLockRelease) + * + * This function is used to release exclusive ownership to a lock + * that was previously obtained via nsLockAcquire(). + * + * Arguments: + * + * lock - handle for lock from nsLockOpen() + */ + +NSAPI_PUBLIC void nsLockRelease(void * lock) +{ + NSLock_t * nl = (NSLock_t *)lock; + +#ifdef FILE_UNIX + assert(nl->nl_cnt > 0); + + crit_enter(nl->nl_crit); + + if (--nl->nl_cnt <= 0) { + system_ulock(nl->nl_fd); + nl->nl_cnt = 0; + } + + crit_exit(nl->nl_crit); +#endif /* FILE_UNIX */ +} + +/* + * Description (nsLockClose) + * + * This function is used to close a lock handle that was previously + * acquired via nsLockOpen(). The lock should not be owned. + * + * Arguments: + * + * lock - handle for lock from nsLockOpen() + */ + +NSAPI_PUBLIC void nsLockClose(void * lock) +{ + NSLock_t * nl = (NSLock_t *)lock; + +#ifdef FILE_UNIX + /* Don't do anything with the lock, since it will get used again */ +#if 0 + crit_enter(nl->nl_crit); + close(nl->nl_fd); + crit_exit(nl->nl_crit); + FREE(nl); +#endif +#else + /* write me */ +#endif FILE_UNIX +} diff --git a/lib/libaccess/nsumgmt.cpp b/lib/libaccess/nsumgmt.cpp new file mode 100644 index 00000000..3db677f0 --- /dev/null +++ b/lib/libaccess/nsumgmt.cpp @@ -0,0 +1,456 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsumgmt.c) + * + * This module contains routines for managing information in a + * Netscape user database. Information for a particular user + * is modified by retrieving the current information in the form + * of a user object (UserObj_t), calling functions in this module, + * to modify the user object, and then calling userStore() to + * write the information in the user object back to the database. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#include "libaccess/nsdbmgmt.h" +#define __PRIVATE_NSUSER +#include "libaccess/nsumgmt.h" + +/* + * Description (userAddGroup) + * + * This function adds a group id to the list of group ids associated + * with a user object. + * + * Arguments: + * + * uoptr - user object pointer + * gid - group id to be added + * + * Returns: + * + * Returns zero if the group id is already present in the group id list. + * Returns one if the group id was added successfully. + * Returns a negative value if an error occurs. + */ + +int userAddGroup(UserObj_t * uoptr, USI_t gid) +{ + int rv; + + rv = usiInsert(&uoptr->uo_groups, gid); + + if (rv > 0) { + + uoptr->uo_flags |= UOF_MODIFIED; + } + + return rv; +} + +/* + * Description (userCreate) + * + * This function creates a user object, using information about + * the user provided by the caller. The strings passed for the + * user account name, password, and real user name may be on the + * stack. The user id is set to zero, but the user object is + * marked as being new. A user id will be assigned when + * userStore() is called to add the user to a user database. + * + * Arguments: + * + * name - pointer to user account name string + * pwd - pointer to (encrypted) password string + * rname - real user name (gecos string) + * + * Returns: + * + * A pointer to a dynamically allocated UserObj_t structure is + * returned. + */ + +NSAPI_PUBLIC UserObj_t * userCreate(NTS_t name, NTS_t pwd, NTS_t rname) +{ + UserObj_t * uoptr; /* user object pointer */ + + uoptr = (UserObj_t *)MALLOC(sizeof(UserObj_t)); + if (uoptr) { + uoptr->uo_name = (NTS_t)STRDUP((char *)name); + uoptr->uo_pwd = (pwd) ? (NTS_t)STRDUP((char *)pwd) : 0; + uoptr->uo_uid = 0; + uoptr->uo_flags = (UOF_MODIFIED | UOF_NEW); + uoptr->uo_rname = (rname) ? (NTS_t)STRDUP((char *)rname) : 0; + UILINIT(&uoptr->uo_groups); + } + + return uoptr; +} + +/* + * Description (userDeleteGroup) + * + * This function removes a specified group id from a user object's + * list of groups. + * + * Arguments: + * + * uoptr - pointer to user object + * gid - group id to remove + * + * Returns: + * + * The return value is zero if the specified group id was not present + * in the user object, or one if the group was successfully removed. + */ + +int userDeleteGroup(UserObj_t * uoptr, USI_t gid) +{ + int rv; /* return value */ + + rv = usiRemove(&uoptr->uo_groups, gid); + if (rv > 0) { + uoptr->uo_flags |= UOF_MODIFIED; + } + + return rv; +} + +/* + * Description (userEncode) + * + * This function encodes a user object into a user DB record. + * + * Arguments: + * + * uoptr - pointer to user object + * ureclen - pointer to returned record length + * urecptr - pointer to returned record pointer + * + * Returns: + * + * The function return value is zero if successful. The length + * and location of the created attribute record are returned + * through 'ureclen' and 'urecptr'. A non-zero function value + * is returned if there's an error. + */ + +int userEncode(UserObj_t * uoptr, int * ureclen, ATR_t * urecptr) +{ + int reclen; /* length of DB record */ + ATR_t rptr; /* DB record pointer */ + ATR_t rstart = 0; /* pointer to beginning of DB record */ + ATR_t glptr; /* saved pointer to UAT_GROUPS length */ + ATR_t gptr; /* saved pointer to after length at glptr */ + int pwdlen; /* password encoding length */ + int uidlen; /* uid encoding length */ + int fllen; /* account flags encoding length */ + USI_t rnlen; /* real name encoding length */ + USI_t nglen; /* group count encoding length */ + USI_t gcnt; /* number of group ids */ + USI_t * gids; /* pointer to array of group ids */ + int i; /* group id index */ + int rv = -1; + + /* + * First we need to figure out how long the generated record will be. + * This doesn't have to be exact, but it must not be smaller than the + * actual record size. + */ + + /* UAT_PASSWORD attribute: tag, length, NTS */ + pwdlen = NTSLENGTH(uoptr->uo_pwd); + reclen = 1 + 1 + pwdlen; + if (pwdlen > 127) goto punt; + + /* UAT_UID attribute: tag, length, USI */ + uidlen = USILENGTH(uoptr->uo_uid); + reclen += (1 + 1 + uidlen); + + /* UAT_ACCFLAGS attribute: tag, length, USI */ + fllen = USILENGTH(uoptr->uo_flags & UOF_DBFLAGS); + reclen += (1 + 1 + fllen); + + /* UAT_REALNAME attribute: tag, length, NTS */ + rnlen = NTSLENGTH(uoptr->uo_rname); + reclen += (1 + USILENGTH(rnlen) + rnlen); + + /* UAT_GROUPS attribute: tag, length, USI(count), USI(gid)... */ + gcnt = UILCOUNT(&uoptr->uo_groups); + nglen = USILENGTH(gcnt); + reclen += (1 + USIALLOC() + nglen + (5 * gcnt)); + + /* Allocate the attribute record buffer */ + rptr = (ATR_t)MALLOC(reclen); + if (rptr) { + + /* Save pointer to start of record */ + rstart = rptr; + + /* Encode UAT_PASSWORD attribute */ + *rptr++ = UAT_PASSWORD; + *rptr++ = pwdlen; + rptr = NTSENCODE(rptr, uoptr->uo_pwd); + + /* Encode UAT_UID attribute */ + *rptr++ = UAT_UID; + *rptr++ = uidlen; + rptr = USIENCODE(rptr, uoptr->uo_uid); + + /* Encode UAT_ACCFLAGS attribute */ + *rptr++ = UAT_ACCFLAGS; + *rptr++ = fllen; + rptr = USIENCODE(rptr, (uoptr->uo_flags & UOF_DBFLAGS)); + + /* Encode UAT_REALNAME attribute */ + *rptr++ = UAT_REALNAME; + rptr = USIENCODE(rptr, rnlen); + rptr = NTSENCODE(rptr, uoptr->uo_rname); + + /* Encode UAT_GROUPS attribute */ + *rptr++ = UAT_GROUPS; + + /* + * Save a pointer to the attribute encoding length, and reserve + * space for the maximum encoding size of a USI_t value. + */ + glptr = rptr; + rptr += USIALLOC(); + gptr = rptr; + + /* Encode number of groups */ + rptr = USIENCODE(rptr, gcnt); + + /* Generate group ids encodings */ + gids = UILLIST(&uoptr->uo_groups); + for (i = 0; i < gcnt; ++i) { + rptr = USIENCODE(rptr, gids[i]); + } + + /* Now fix up the UAT_GROUPS attribute encoding length */ + glptr = USIINSERT(glptr, (USI_t)(rptr - gptr)); + + /* Return record length and location if requested */ + if (ureclen) *ureclen = rptr - rstart; + if (urecptr) *urecptr = rstart; + + /* Indicate success */ + rv = 0; + } + + punt: + return rv; +} + +/* + * Description (userRemove) + * + * This function is called to remove a user from a specified user + * database. Both the primary DB file and the id-to-name DB file + * are updated. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * flags - (unused - must be zero) + * name - pointer to user account name + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. + */ + +NSAPI_PUBLIC int userRemove(NSErr_t * errp, void * userdb, int flags, NTS_t name) +{ + UserObj_t * uoptr; /* user object pointer */ + int rv; + int rv2; + + /* First retrieve the user record */ + uoptr = userFindByName(errp, userdb, name); + if (!uoptr) { + /* Error - specified user not found */ + return NSAERRNAME; + } + + /* Free the user id value, if any */ + rv = 0; + if (uoptr->uo_uid != 0) { + rv = ndbFreeId(errp, userdb, 0, (char *)name, uoptr->uo_uid); + } + + rv2 = ndbDeleteName(errp, userdb, 0, 0, (char *)name); + + return (rv) ? rv : rv2; +} + +/* + * Description (userRename) + * + * This function is called to change the account name associated + * with an existing user. The caller provides a pointer to a + * user object for the existing user (with the current user account + * name referenced by uo_name), and the new account name for this + * user. A check is made to ensure the uniqueness of the new name + * in the specified user database. The account name in the user + * object is modified. The user database is not modified until + * userStore() is called. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * uoptr - user object pointer + * newname - pointer to new account name string + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. The user object remains intact in either + * case. + */ + +NSAPI_PUBLIC int userRename(NSErr_t * errp, void * userdb, UserObj_t * uoptr, NTS_t newname) +{ + int reclen; /* user record length */ + ATR_t recptr = 0; /* user record pointer */ + char * oldname; /* old user account name */ + int eid; /* error id code */ + int rv; /* result value */ + + /* Save the current account name and replace it with the new one */ + oldname = (char *)uoptr->uo_name; + uoptr->uo_name = (unsigned char *) STRDUP((char *)newname); + + if ((oldname != 0) && !(uoptr->uo_flags & UOF_NEW)) { + + /* Convert the information in the user object to a DB record */ + rv = userEncode(uoptr, &reclen, &recptr); + if (rv) goto err_nomem; + + /* + * Store the record in the database + * under the new user account name. + */ + rv = ndbStoreName(errp, userdb, NDBF_NEWNAME, + 0, (char *)uoptr->uo_name, reclen, (char *)recptr); + if (rv) goto punt; + + /* Change the mapping of the user id to the new name */ + rv = ndbRenameId(errp, userdb, 0, (char *)uoptr->uo_name, uoptr->uo_uid); + if (rv) goto punt; + + /* Delete the user record with the old account name */ + rv = ndbDeleteName(errp, userdb, 0, 0, oldname); + if (rv) goto punt; + } + else { + /* Set flags in user object for userStore() */ + uoptr->uo_flags |= UOF_MODIFIED; + } + + punt: + if (recptr) { + FREE(recptr); + } + if (oldname) { + FREE(oldname); + } + return rv; + + err_nomem: + eid = NSAUERR1000; + rv = NSAERRNOMEM; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + goto punt; +} + +/* + * Description (userStore) + * + * This function is called to store a user object in the database. + * If the object was created by userCreate(), it is assumed to be + * a new user account, the user account name must not match any + * existing user account names in the database, and a uid is + * assigned before adding the user to the database. If the object + * was created by userFindByName(), the information in the user + * object will replace the existing database entry for the + * indicated user account name. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * flags - (unused - must be zero) + * uoptr - user object pointer + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code. The user object remains intact in either + * case. + */ + +NSAPI_PUBLIC int userStore(NSErr_t * errp, void * userdb, int flags, UserObj_t * uoptr) +{ + ATR_t recptr = 0; + USI_t uid; + int reclen = 0; + int stflags = 0; + int eid; + int rv; + + /* If this is a new user, allocate a uid value */ + if (uoptr->uo_flags & UOF_NEW) { + /* + * Yes, allocate a user id and add a user id to user + * account name mapping to the id-to-name DB file. + */ + uid = 0; + rv = ndbAllocId(errp, userdb, 0, (char *)uoptr->uo_name, &uid); + if (rv) goto punt; + + uoptr->uo_uid = uid; + + /* Let the database manager know that this is a new entry */ + stflags = NDBF_NEWNAME; + } + + /* Convert the information in the user object to a DB record */ + rv = userEncode(uoptr, &reclen, &recptr); + if (rv) goto err_nomem; + + /* Store the record in the database under the user account name. */ + rv = ndbStoreName(errp, userdb, stflags, + 0, (char *)uoptr->uo_name, reclen, (char *)recptr); + if (rv) goto punt; + + FREE(recptr); + recptr = 0; + + uoptr->uo_flags &= ~(UOF_NEW | UOF_MODIFIED); + return 0; + + err_nomem: + eid = NSAUERR1100; + rv = NSAERRNOMEM; + nserrGenerate(errp, rv, eid, NSAuth_Program, 0); + + punt: + if (recptr) { + FREE(recptr); + } + if ((uoptr->uo_flags & UOF_NEW) && (uid != 0)) { + /* Free the user id value if we failed after allocating it */ + ndbFreeId(errp, userdb, 0, (char *)uoptr->uo_name, uid); + } + return rv; +} diff --git a/lib/libaccess/nsuser.cpp b/lib/libaccess/nsuser.cpp new file mode 100644 index 00000000..158235a5 --- /dev/null +++ b/lib/libaccess/nsuser.cpp @@ -0,0 +1,309 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (nsuser.c) + * + * This module contains routines for accessing information in a + * Netscape user database. User information is returned in the + * form of a user object (UserObj_t), defined in nsauth.h. + */ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#define __PRIVATE_NSUSER +#include "libaccess/nsuser.h" + +/* Authentication facility name for error frame generation */ +char * NSAuth_Program = "NSAUTH"; + +/* + * Description (userDecode) + * + * This function decodes an external user DB record into a dynamically + * allocated UserObj_t structure. The DB record is encoded as an + * attribute record as defined in attrec.h. + * + * Arguments: + * + * name - pointer to user account name string + * ureclen - length of the user DB record, in octets + * urecptr - pointer to user DB record + * + * Returns: + * + * A pointer to the allocated UserObj_t structure is returned. + */ + +UserObj_t * userDecode(NTS_t name, int ureclen, ATR_t urecptr) +{ + ATR_t cp = urecptr; /* current pointer into DB record */ + USI_t tag; /* attribute tag */ + USI_t len; /* attribute value encoding length */ + USI_t gcnt; /* number of group ids */ + USI_t * gids; /* pointer to array of group ids */ + int i; /* group id index */ + UserObj_t * uoptr; /* user object pointer */ + + /* Allocate a user object structure */ + uoptr = (UserObj_t *)MALLOC(sizeof(UserObj_t)); + if (uoptr) { + + uoptr->uo_name = (unsigned char *) STRDUP((char *)name); + uoptr->uo_pwd = 0; + uoptr->uo_uid = 0; + uoptr->uo_flags = 0; + uoptr->uo_rname = 0; + UILINIT(&uoptr->uo_groups); + + /* Parse user DB record */ + while ((cp - urecptr) < ureclen) { + + /* Get the attribute tag */ + cp = USIDECODE(cp, &tag); + + /* Get the length of the encoding of the attribute value */ + cp = USIDECODE(cp, &len); + + /* Process this attribute */ + switch (tag) { + + case UAT_PASSWORD: /* encrypted password */ + cp = NTSDECODE(cp, &uoptr->uo_pwd); + break; + + case UAT_UID: /* user id */ + cp = USIDECODE(cp, &uoptr->uo_uid); + break; + + case UAT_ACCFLAGS: /* account flags */ + cp = USIDECODE(cp, &uoptr->uo_flags); + break; + + case UAT_REALNAME: /* real name of user */ + cp = NTSDECODE(cp, &uoptr->uo_rname); + break; + + case UAT_GROUPS: /* groups which include user */ + + /* First get the number of group ids following */ + cp = USIDECODE(cp, &gcnt); + + if (gcnt > 0) { + + /* Allocate space for group ids */ + gids = usiAlloc(&uoptr->uo_groups, gcnt); + if (gids) { + for (i = 0; i < gcnt; ++i) { + cp = USIDECODE(cp, gids + i); + } + } + } + break; + + default: /* unrecognized attribute */ + /* Just skip it */ + cp += len; + break; + } + } + } + + return uoptr; +} + +/* + * Description (userEnumHelp) + * + * This is a local function that is called by NSDB during user + * database enumeration. It decodes user records into user + * objects, and presents them to the caller of userEnumerate(). + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * parg - pointer to UserEnumArgs_t structure + * namelen - user record key length including null + * terminator + * name - user record key (user account name) + * reclen - length of user record + * recptr - pointer to user record contents + * + * Returns: + * + * Returns whatever value is returned from the upcall to the caller + * of userEnumerate(). + */ + +static int userEnumHelp(NSErr_t * errp, void * parg, + int namelen, char * name, int reclen, char * recptr) +{ + UserEnumArgs_t * ue = (UserEnumArgs_t *)parg; + UserObj_t * uoptr; /* user object pointer */ + int rv; + + uoptr = userDecode((NTS_t)name, reclen, (ATR_t)recptr); + + rv = (*ue->func)(errp, ue->user, uoptr); + + if (!(ue->flags & UOF_ENUMKEEP)) { + userFree(uoptr); + } + + return rv; +} + +/* + * Description (userEnumerate) + * + * This function enumerates all of the users in a specified user + * database, calling a caller-specified function with a user object + * for each user in the database. A 'flags' value of UOF_ENUMKEEP + * can be specified to keep the user objects around (not free them) + * after the caller's function returns. Otherwise, each user + * object is freed after being presented to the caller's function. + * The 'argp' argument is an opaque pointer, which is passed to + * the caller's function as 'parg' on each call, along with a + * user object pointer. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * flags - bit flags: + * UOF_ENUMKEEP - keep user objects + * argp - passed to 'func' as 'parg' + * func - pointer to caller's enumeration function + * + * Returns: + * + * If successful, the return value is zero. Otherwise it is a + * non-zero error code, and an error frame is generated if an error + * frame list was provided by the caller. + */ + +int userEnumerate(NSErr_t * errp, void * userdb, int flags, void * argp, + int (*func)(NSErr_t * ferrp, void * parg, UserObj_t * uoptr)) +{ + int rv; + UserEnumArgs_t args; + + args.userdb = userdb; + args.flags = flags; + args.func = func; + args.user = argp; + + rv = ndbEnumerate(errp, + userdb, NDBF_ENUMNORM, (void *)&args, userEnumHelp); + + return rv; +} + +/* + * Description (userFindByName) + * + * This function looks up a user record for a specified user account + * name, converts the user record to the internal user object form, + * and returns a pointer to the user object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * name - user account name to find + * + * Returns: + * + * If successful, the return value is a pointer to a user object + * for the specified user. Otherwise it is 0, and an error frame + * is generated if an error frame list was provided by the caller. + */ + +UserObj_t * userFindByName(NSErr_t * errp, void * userdb, NTS_t name) +{ + UserObj_t * uoptr = 0; + ATR_t urecptr; + int ureclen; + int rv; + + /* Look up the user name in the database */ + rv = ndbFindName(errp, userdb, 0, (char *) name, &ureclen, (char **)&urecptr); + if (rv == 0) { + + /* Got the user record. Decode into a user object. */ + uoptr = userDecode(name, ureclen, urecptr); + } + + return uoptr; +} + +/* + * Description (userFindByUid) + * + * This function looks up a user record for a specified user id, + * converts the user record to the internal user object form, and + * returns a pointer to the user object. + * + * Arguments: + * + * errp - error frame list pointer (may be null) + * userdb - handle for user DB access + * uid - user id to find + * + * Returns: + * + * If successful, the return value is a pointer to a user object + * for the specified user. Otherwise it is 0, and an error frame + * is generated if an error frame list was provided by the caller. + */ + +UserObj_t * userFindByUid(NSErr_t * errp, void * userdb, USI_t uid) +{ + UserObj_t * uoptr = 0; + NTS_t name; + ATR_t urecptr; + int ureclen; + int rv; + + /* Get the user account name corresponding to the uid */ + rv = ndbIdToName(errp, userdb, uid, 0, (char **)&name); + if (rv == 0) { + + rv = ndbFindName(errp, userdb, 0, (char *)name, &ureclen, (char **)&urecptr); + if (rv == 0) { + + /* Got the user record. Decode into a user object. */ + uoptr = userDecode(name, ureclen, urecptr); + } + } + + return uoptr; +} + +/* + * Description (userFree) + * + * This function is called to free a user object. User objects + * are not automatically freed when a user database is closed. + * + * Arguments: + * + * uoptr - user object pointer + * + */ + +NSAPI_PUBLIC void userFree(UserObj_t * uoptr) +{ + if (uoptr) { + + if (uoptr->uo_name) FREE(uoptr->uo_name); + if (uoptr->uo_pwd) FREE(uoptr->uo_pwd); + if (uoptr->uo_rname) FREE(uoptr->uo_rname); + UILFREE(&uoptr->uo_groups); + FREE(uoptr); + } +} diff --git a/lib/libaccess/oneeval.cpp b/lib/libaccess/oneeval.cpp new file mode 100644 index 00000000..be837599 --- /dev/null +++ b/lib/libaccess/oneeval.cpp @@ -0,0 +1,1054 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (acleval.c) + * + * This module provides functions for evaluating Access Control List + * (ACL) structures in memory. + * + */ + +#include <string.h> +#include <sys/types.h> +#include <assert.h> + +#include <netsite.h> +#include <base/systems.h> +#include <base/crit.h> +#include <base/session.h> +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include <libaccess/symbols.h> +#include <libaccess/aclerror.h> +#include <libaccess/aclglobal.h> +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> +#include "access_plhash.h" +#include "aclutil.h" +#include "aclcache.h" +#include "oneeval.h" +#include "permhash.h" + +static int acl_default_result = ACL_RES_DENY; + +static ACLDispatchVector_t __nsacl_vector = { + + /* Error frame stack support */ + + nserrDispose, + nserrFAlloc, + nserrFFree, + nserrGenerate, + + /* Property list support */ + + PListAssignValue, + PListCreate, + PListDefProp, + PListDeleteProp, + PListFindValue, + PListInitProp, + PListNew, + PListDestroy, + PListGetValue, + PListNameProp, + PListSetType, + PListSetValue, + PListEnumerate, + PListDuplicate, + PListGetPool, + + /* ACL attribute handling */ + + ACL_LasRegister, + + /* method/dbtype registration routines */ + + ACL_MethodRegister, + ACL_MethodIsEqual, + ACL_MethodNameIsEqual, + ACL_MethodFind, + ACL_MethodGetDefault, + ACL_MethodSetDefault, + ACL_AuthInfoGetMethod, + + ACL_DbTypeRegister, + ACL_DbTypeIsEqual, + ACL_DbTypeNameIsEqual, + ACL_DbTypeFind, + ACL_DbTypeGetDefault, + ACL_AuthInfoGetDbType, + ACL_DbTypeIsRegistered, + ACL_DbTypeParseFn, + + ACL_AttrGetterRegister, + + ACL_ModuleRegister, + ACL_GetAttribute, + ACL_DatabaseRegister, + ACL_DatabaseFind, + ACL_DatabaseSetDefault, + ACL_LDAPDatabaseHandle, + ACL_AuthInfoGetDbname, + ACL_CacheFlushRegister, + ACL_CacheFlush, + + /* ACL language and file interfaces */ + + ACL_ParseFile, + ACL_ParseString, + ACL_WriteString, + ACL_WriteFile, + ACL_FileRenameAcl, + ACL_FileDeleteAcl, + ACL_FileGetAcl, + ACL_FileSetAcl, + + /* ACL Expression construction interfaces */ + + ACL_ExprNew, + ACL_ExprDestroy, + ACL_ExprSetPFlags, + ACL_ExprClearPFlags, + ACL_ExprTerm, + ACL_ExprNot, + ACL_ExprAnd, + ACL_ExprOr, + ACL_ExprAddAuthInfo, + ACL_ExprAddArg, + ACL_ExprSetDenyWith, + ACL_ExprGetDenyWith, + ACL_ExprAppend, + + /* ACL manipulation */ + + ACL_AclNew, + ACL_AclDestroy, + + /* ACL list manipulation */ + + ACL_ListNew, + ACL_ListConcat, + ACL_ListAppend, + ACL_ListDestroy, + ACL_ListFind, + ACL_ListAclDelete, + ACL_ListGetNameList, + ACL_NameListDestroy, + + /* ACL evaluation */ + + ACL_EvalTestRights, + ACL_EvalNew, + ACL_EvalDestroy, + ACL_EvalSetACL, + ACL_EvalGetSubject, + ACL_EvalSetSubject, + ACL_EvalGetResource, + ACL_EvalSetResource, + + /* Access to critical section for ACL cache */ + + ACL_CritEnter, + ACL_CritExit, + + /* Miscellaneous functions */ + + ACL_AclGetTag, + ACL_ListGetFirst, + ACL_ListGetNext, + + /* Functions added after ES 3.0 release */ + ACL_DatabaseGetDefault, + ACL_SetDefaultResult, + ACL_GetDefaultResult +}; + +NSAPI_PUBLIC ACLDispatchVector_t *__nsacl_table = &__nsacl_vector; + +int ACLEvalAce( + NSErr_t *errp, + ACLEvalHandle_t *acleval, + ACLExprHandle_t *ace, + ACLCachable_t *cachable, + PList_t autharray[], + PList_t global_auth + ) +{ + ACLCachable_t local_cachable; + int result; + ACLExprEntry_t *expr; + int expr_index = 0; + + expr = &ace->expr_arry[0]; + *cachable = ACL_INDEF_CACHABLE; + + while (TRUE) + { + local_cachable = ACL_NOT_CACHABLE; + + /* Call the LAS driver */ + if (!expr->las_eval_func) { + ACL_CritEnter(); + if (!expr->las_eval_func) { /* Must check again after locking */ + ACL_LasFindEval(errp, expr->attr_name, &expr->las_eval_func); + if (!expr->las_eval_func) { /* Couldn't find it */ + ACL_CritExit(); + return LAS_EVAL_INVALID; + } + } + ACL_CritExit(); + } + result = (*expr->las_eval_func)( + errp, + expr->attr_name, + expr->comparator, + expr->attr_pattern, + &local_cachable, + &expr->las_cookie, + acleval->subject, + acleval->resource, + autharray ? autharray[expr_index] : NULL, + global_auth); + + /* Evaluate the cachable value */ + if (local_cachable < *cachable) { + + /* Take the minimum value */ + *cachable = local_cachable; + } + + /* Evaluate the return code */ + switch (result) { + case LAS_EVAL_TRUE: + if (expr->true_idx < 0) + return (expr->true_idx); + else { + expr_index = expr->true_idx; + expr = &ace->expr_arry[expr->true_idx]; + } + break; + + case LAS_EVAL_FALSE: + if (expr->false_idx < 0) + return (expr->false_idx); + else { + expr_index = expr->false_idx; + expr = &ace->expr_arry[expr->false_idx]; + } + break; + + default: + return (result); + } + + } +} + + +int +ACL_EvalDestroyContext(ACLListCache_t *cache) +{ + ACLAceEntry_t *cur_ace, *next_ace; + ACLAceNumEntry_t *cur_num_p, *next_num_p; + ACLExprHandle_t *acep; + + if (!cache) + return 0; + + PR_HashTableDestroy(cache->Table); + cache->Table = NULL; + + cur_ace = cache->acelist; + cache->acelist = NULL; + while (cur_ace) { + if (cur_ace->autharray) + PERM_FREE(cur_ace->autharray); + if ((cur_ace->global_auth) && + (cur_ace->acep->expr_type == ACL_EXPR_TYPE_AUTH)) + PListDestroy(cur_ace->global_auth); + next_ace = cur_ace->next; + acep = cur_ace->acep; /* The ACE structure itself */ + PERM_FREE(cur_ace); + cur_ace = next_ace; + } + + cur_num_p = cache->chain_head; + cache->chain_head = NULL; + while (cur_num_p) { + next_num_p = cur_num_p->chain; + PERM_FREE(cur_num_p); + cur_num_p = next_num_p; + } + + PERM_FREE(cache); + + return 0; +} + + +/* ACLEvalBuildContext + * Builds three structures: + * Table - A hash table of all access rights referenced by any ACE in any + * of the ACLs in this list. Each hash entry then has a list of + * the relevant ACEs, in the form of indexes to the ACE linked + * list. + * ACE List - A linked list of all the ACEs in the proper evaluation order. + * + * For concurrency control, the caller must call ACL_CritEnter() + */ +int +ACLEvalBuildContext( + NSErr_t *errp, + ACLEvalHandle_t *acleval) +{ + ACLHandle_t *acl; + ACLExprHandle_t *ace; + int ace_cnt = -1; + ACLAceEntry_t *acelast, *new_ace; + ACLAceNumEntry_t *entry, *temp_entry; + char **argp; + ACLListCache_t *cache; + ACLWrapper_t *wrapper; + PList_t curauthplist=NULL, absauthplist=NULL; + int i, rv; + ACLExprEntry_t *expr; + PList_t authplist; + + /* Allocate the cache context and link it into the ACLListHandle */ + cache = (ACLListCache_t *)PERM_CALLOC(sizeof(ACLListCache_t)); + if (cache == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4010, ACL_Program, 0); + goto error; + } + + /* Allocate the access rights hash table */ + cache->Table = PR_NewHashTable(0, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + + if (cache->Table == NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4000, ACL_Program, 1, + XP_GetAdminStr(DBT_EvalBuildContextUnableToCreateHash)); + goto error; + } + + wrapper = acleval->acllist->acl_list_head; + + /* Loop through all the ACLs in the list */ + while (wrapper) + { + acl = wrapper->acl; + ace = acl->expr_list_head; + + while (ace) /* Loop through all the ACEs in this ACL */ + { + + /* allocate a new ace list entry and link it in to the ordered + * list. + */ + new_ace = (ACLAceEntry_t *)PERM_CALLOC(sizeof(ACLAceEntry_t)); + if (new_ace == (ACLAceEntry_t *)NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4020, ACL_Program, 1, + XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAceEntry)); + goto error; + } + new_ace->acep = ace; + ace_cnt++; + + if (cache->acelist == NULL) + cache->acelist = acelast = new_ace; + else { + acelast->next = new_ace; + acelast = new_ace; + new_ace->acep = ace; + } + new_ace->next = NULL; + + argp = ace->expr_argv; + + switch (ace->expr_type) + { + case ACL_EXPR_TYPE_ALLOW: + case ACL_EXPR_TYPE_DENY: + + /* Add this ACE to the appropriate entries in the access rights + * hash table + */ + while (*argp) + { + entry = + (ACLAceNumEntry_t *)PERM_CALLOC(sizeof(ACLAceNumEntry_t)); + if (entry == (ACLAceNumEntry_t *)NULL) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4030, ACL_Program, 1, + XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAceEntry)); + goto error; + } + if (cache->chain_head == NULL) + cache->chain_head = cache->chain_tail = entry; + else { + cache->chain_tail->chain = entry; + cache->chain_tail = entry; + } + entry->acenum = ace_cnt; + + /* + * OK to call PL_HasTableLookup() even though it mods + * the Table as this routine is called in critical section. + */ + temp_entry = (ACLAceNumEntry_t *)PL_HashTableLookup(cache->Table, *argp); + /* the first ACE for this right? */ + if (temp_entry) { + /* Link it in at the end */ + while (temp_entry->next) { + temp_entry = temp_entry->next; + } + temp_entry->next = entry; + } else /* just link it in */ + PR_HashTableAdd(cache->Table, *argp, entry); + + argp++; + + } + + /* See if any of the clauses require authentication. */ + if (curauthplist) { + for (i = 0; i < ace->expr_term_index; i++) { + expr = &ace->expr_arry[i]; + rv = PListFindValue(curauthplist, expr->attr_name, + NULL, &authplist); + if (rv > 0) { + /* First one for this ACE? */ + if (!new_ace->autharray) { + new_ace->autharray = (PList_t *)PERM_CALLOC(sizeof(PList_t *) * ace->expr_term_index); + if (!new_ace->autharray) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4040, ACL_Program, 1, XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAuthPointerArray)); + goto error; + } + } + new_ace->autharray[i] = authplist; + } + } + } + break; + + case ACL_EXPR_TYPE_AUTH: + + /* Allocate the running auth tables if none yet */ + if (!curauthplist) { + curauthplist = PListNew(NULL); + if (!curauthplist) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4050, ACL_Program, 1, XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAuthPlist)); + goto error; + } + absauthplist = PListNew(NULL); + if (!absauthplist) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4050, ACL_Program, 1, XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAuthPlist)); + goto error; + } + } else { /* duplicate the existing auth table */ + curauthplist = PListDuplicate(curauthplist, NULL, 0); + if (!curauthplist) { + nserrGenerate(errp, ACLERRNOMEM, ACLERR4050, ACL_Program, 1, XP_GetAdminStr(DBT_EvalBuildContextUnableToAllocAuthPlist)); + goto error; + } + } + + /* For each listed attribute */ + while (*argp) + { + /* skip any attributes that were absoluted */ + if (PListFindValue(absauthplist, *argp, NULL, NULL) < 0) + { + /* Save pointer to the property list */ + PListInitProp(curauthplist, NULL, *argp, ace->expr_auth, + ace->expr_auth); + if (IS_ABSOLUTE(ace->expr_flags)) + PListInitProp(absauthplist, NULL, *argp, NULL, + NULL); + } + + argp++; + } + + break; + + case ACL_EXPR_TYPE_RESPONSE: + (void) ACL_ExprGetDenyWith(NULL, ace, &cache->deny_type, + &cache->deny_response); + break; + + default: + NS_ASSERT(0); + + } /* switch expr_type */ + + new_ace->global_auth = curauthplist; + ace = ace->expr_next; + } + + /* Next ACL please */ + wrapper = wrapper->wrap_next; + } + + if (absauthplist) + PListDestroy(absauthplist); + + /* This must be done last to avoid a race in initialization */ + acleval->acllist->cache = (void *)cache; + + return 0; + +error: + if (absauthplist) + PListDestroy(absauthplist); + if (cache) { + ACL_EvalDestroyContext(cache); + } + acleval->acllist->cache = NULL; + return ACL_RES_ERROR; +} + +/* ACL_InvalidateSubjectPList + * Given a new authentication plist, enumerate the plist and for each + * key in the plist, search for the matching key in the subject plist + * and delete any matches. E.g. "user", "group". + */ +void +ACL_InvalidateSubjectPList(char *attr, const void *value, void *user_data) +{ + PList_t subject = (PList_t)user_data; + + PListDeleteProp(subject, 0, attr); + return; +} + +NSAPI_PUBLIC int ACL_SetDefaultResult (NSErr_t *errp, + ACLEvalHandle_t *acleval, + int result) +{ + int rv; + + switch(result) { + case ACL_RES_ALLOW: + case ACL_RES_DENY: + case ACL_RES_FAIL: + case ACL_RES_INVALID: + acleval->default_result = result; + rv = 0; + break; + default: + rv = -1; + } + + return rv; +} + +NSAPI_PUBLIC int ACL_GetDefaultResult (ACLEvalHandle_t *acleval) +{ + return acleval->default_result; +} + +/* ACL_INTEvalTestRights + * INPUT + * *errp The usual error context stack + * *acleval A list of ACLs + * **rights An array of strings listing the requested rights + * **map_generic An array of strings listing the specific rights + * that map from the generic rights. + * OUTPUT + * **deny_type bong file type passed on the way back out + * **deny_response bong file pathname passed on the way back out + * **acl_tag Name of the ACL that denies access + * *expr_num ACE number within the denying ACL + * *cachable Is the result cachable? + */ +static int +ACL_INTEvalTestRights( + NSErr_t *errp, + ACLEvalHandle_t *acleval, + char **rights, + char **map_generic, + char **deny_type, + char **deny_response, + char **acl_tag, + int *expr_num, + ACLCachable_t *cachable) +{ + struct rights_ent { + char right[64]; /* lowercase-ed rights string */ + int result; /* Interim result value */ + int absolute; /* ACE with absolute keyword */ + int count; /* # specific + generic rights */ + ACLAceNumEntry_t *acelist[ACL_MAX_GENERIC+1]; + /* List of relevant ACEs */ + }; + struct rights_ent *rarray_p; + struct rights_ent rights_arry[ACL_MAX_TEST_RIGHTS]; + ACLAceNumEntry_t *alllist; /* List of ACEs for "all" rights */ + ACLAceEntry_t *cur_ace; + ACLListCache_t *cache; + int rights_cnt = 0; + int prev_acenum, cur_acenum; + int i, j, right_num, delta; + ACLCachable_t ace_cachable; + int result; + int absolute; + int skipflag; + int g_num; /* index into the generic rights array. */ + char **g_rights; + PList_t global_auth=NULL; + int allow_error = 0; + int allow_absolute = 0; + char *allow_tag = NULL; + int allow_num = 0; + int default_result = ACL_GetDefaultResult(acleval); + + *acl_tag = NULL; + *expr_num = 0; + *cachable = ACL_INDEF_CACHABLE; + + /* + * The acleval contains the list of acis we are asking about. + * In our case it's always of length 1. + * The acleval is a per aclpb structure but + * the acllist is a global structure derived from the global + * aci cache--so access to acllist is multi-threaded. + * Hence, for example the use of the "read-only" hash + * lookup routines in this function--ACL_EvalTestRights() + * is called in a "reader only context" so this code is therefore + * thread-safe. + */ + + if (acleval->acllist == ACL_LIST_NO_ACLS) return ACL_RES_ALLOW; + + /* Build up the access right - indexed structures */ + if (acleval->acllist->cache == NULL) { + ACL_CritEnter(); + if (acleval->acllist->cache == NULL) { /* Check again */ + if (ACLEvalBuildContext(errp, acleval) == ACL_RES_ERROR) { + nserrGenerate(errp, ACLERRINTERNAL, ACLERR4110, ACL_Program, + 1, XP_GetAdminStr(DBT_EvalTestRightsEvalBuildContextFailed)); + ACL_CritExit(); + return ACL_RES_ERROR; + } + } + ACL_CritExit(); + } + cache = (ACLListCache_t *)acleval->acllist->cache; + *deny_response = cache->deny_response; + *deny_type = cache->deny_type; + + /* For the list of rights requested, get back the list of relevant + * ACEs. If we want + * to alter the precedence of allow/deny, this would be a good + * place to do it. + */ + + while (*rights) + { + rarray_p = &rights_arry[rights_cnt]; + + /* Initialize the rights array entry */ + strcpy(&rarray_p->right[0], *rights); + makelower(&rarray_p->right[0]); + rarray_p->result = default_result; + rarray_p->absolute = 0; + rarray_p->count = 1; // There's always the specific right + + /* Locate the list of ACEs that apply to the right */ + rarray_p->acelist[0] = + (ACLAceNumEntry_t *)ACL_HashTableLookup_const(cache->Table, rarray_p->right); + + /* See if the requested right also maps back to a generic right and + * if so, locate the acelist for it as well. + */ + if (map_generic) + { + for (g_rights=map_generic, g_num=0; *g_rights; g_rights++, g_num++) + { + if (strstr(*g_rights, rarray_p->right)) { + // Add it to our acelist, but skip 0 'cause that's the + // specific right. + rarray_p->acelist[rarray_p->count++] = + (ACLAceNumEntry_t *)ACL_HashTableLookup_const(cache->Table, + (char *)generic_rights[g_num]); + NS_ASSERT (rarray_p->count < ACL_MAX_GENERIC); + } + } + } + + rights_cnt++; + rights++; + NS_ASSERT (rights_cnt < ACL_MAX_TEST_RIGHTS); + } + + /* Special case - look for an entry that applies to "all" rights */ + alllist = (ACLAceNumEntry_t *)ACL_HashTableLookup_const(cache->Table, "all"); + + /* Ok, we've now got a list of relevant ACEs. Now evaluate things. */ + prev_acenum = -1; + cur_ace = cache->acelist; + + /* Loop through the relevant ACEs for the requested rights */ + while (TRUE) + { + cur_acenum = 10000; /* Pick a really high num so we lose */ + /* Find the lowest ACE among the rights lists */ + for (i=0; i<rights_cnt; i++) { + rarray_p = &rights_arry[i]; + if (rarray_p->absolute) continue; // This right doesn't matter + for (j=0; j<rarray_p->count; j++) { + if ((rarray_p->acelist[j] != NULL) && + (rarray_p->acelist[j]->acenum < cur_acenum)) { + cur_acenum = rarray_p->acelist[j]->acenum; + } + } + } + + /* Special case - look for the "all" rights ace list and see if its + * the lowest of all. + */ + if (alllist && (alllist->acenum < cur_acenum)) + cur_acenum = alllist->acenum; + + /* If no new ACEs then we're done - evaluate the rights list */ + if (cur_acenum == 10000) + break; + + /* Locate that ACE and evaluate it. We have to step through the + * linked list of ACEs to find it. + */ + if (prev_acenum == -1) + delta = cur_acenum; + else + delta = cur_acenum - prev_acenum; + + for (i=0; i<delta; i++) + cur_ace = cur_ace->next; + + if (global_auth && global_auth != cur_ace->global_auth) { + /* We must enumerate the auth_info plist and remove entries for + * each attribute from the subject property list. + */ + PListEnumerate(cur_ace->global_auth, ACL_InvalidateSubjectPList, + acleval->subject); + } + global_auth = cur_ace->global_auth; + + result = ACLEvalAce(errp, acleval, cur_ace->acep, &ace_cachable, + cur_ace->autharray, cur_ace->global_auth); + + /* Evaluate the cachable value */ + if (ace_cachable < *cachable) { + /* Take the minimum value */ + *cachable = ace_cachable; + } + + /* Under certain circumstances, no matter what happens later, + * the current result is not gonna change. + */ + if ((result != LAS_EVAL_TRUE) && (result != LAS_EVAL_FALSE)) { + if (cur_ace->acep->expr_type != ACL_EXPR_TYPE_ALLOW) { + if (allow_error) { + *acl_tag = allow_tag; + *expr_num = allow_num; + return (allow_error); + } else { + *acl_tag = cur_ace->acep->acl_tag; + *expr_num = cur_ace->acep->expr_number; + return (EvalToRes(result)); + } + } else { + /* If the error is on an allow statement, continue processing + * and see if a subsequent allow works. If not, remember the + * error and return it. + */ + if (!allow_error) { + allow_error = EvalToRes(result); + allow_tag = cur_ace->acep->acl_tag; + allow_num = cur_ace->acep->expr_number; + } + if (IS_ABSOLUTE(cur_ace->acep->expr_flags)) { + allow_absolute = 1; + } + } + } + + /* Now apply the result to the rights array. Look to see which rights' + * acelist include the current one, or if the current one is on the + * "all" rights ace list. + */ + for (right_num=0; right_num<rights_cnt; right_num++) + { + rarray_p = &rights_arry[right_num]; + + /* Have we fixated on a prior result? */ + if (rarray_p->absolute) + continue; + + skipflag = 1; + + // Did this ace apply to this right? + for (i=0; i<rarray_p->count; i++) { + if ((rarray_p->acelist[i]) && + (rarray_p->acelist[i]->acenum == cur_acenum)) { + rarray_p->acelist[i] = rarray_p->acelist[i]->next; + skipflag = 0; + } + } + + /* This ace was on the "all" rights queue */ + if ((alllist) && (alllist->acenum == cur_acenum)) { + skipflag = 0; + } + + if (skipflag) + continue; /* doesn't apply to this right */ + + if (IS_ABSOLUTE(cur_ace->acep->expr_flags) && (result == + LAS_EVAL_TRUE)) { + rarray_p->absolute = 1; + absolute = 1; + } else + absolute = 0; + + switch (cur_ace->acep->expr_type) { + case ACL_EXPR_TYPE_ALLOW: + if (result == LAS_EVAL_TRUE) { + rarray_p->result = ACL_RES_ALLOW; + if (!allow_absolute) { + /* A previous ALLOW error was superceded */ + allow_error = 0; + } + } + else if (!*acl_tag) { + *acl_tag = cur_ace->acep->acl_tag; + *expr_num = cur_ace->acep->expr_number; + } + break; + case ACL_EXPR_TYPE_DENY: + if (result == LAS_EVAL_TRUE) { + *acl_tag = cur_ace->acep->acl_tag; + *expr_num = cur_ace->acep->expr_number; + if (absolute) { + if (allow_error) { + *acl_tag = allow_tag; + *expr_num = allow_num; + return (allow_error); + } + return (ACL_RES_DENY); + } + rarray_p->result = ACL_RES_DENY; + } + break; + default: + /* a non-authorization ACE, just ignore */ + break; + } + + } + + /* This ace was on the "all" rights queue */ + if ((alllist) && (alllist->acenum == cur_acenum)) { + alllist = alllist->next; + } + + /* If this is an absolute, check to see if all the rights + * have already been fixed by this or previous absolute + * statements. If so, we can compute the response without + * evaluating any more of the ACL list. + */ + if (absolute) { + for (i=0; i<rights_cnt; i++) { + /* Non absolute right, so skip this section */ + if (rights_arry[i].absolute == 0) + break; + /* This shouldn't be possible, but check anyway. + * Any absolute non-allow result should already + * have been returned earlier. + */ + if (rights_arry[i].result != ACL_RES_ALLOW) { + char result_str[16]; + sprintf(result_str, "%d", rights_arry[i].result); + nserrGenerate(errp, ACLERRINTERNAL, ACLERR4100, ACL_Program, 3, XP_GetAdminStr(DBT_EvalTestRightsInterimAbsoluteNonAllowValue), rights[i], result_str); + break; + } + if (i == (rights_cnt - 1)) + return ACL_RES_ALLOW; + } + } + + prev_acenum = cur_acenum; + + } /* Next ACE */ + + /* Do an AND on the results for the individual rights */ + for (right_num=0; right_num<rights_cnt; right_num++) + if (rights_arry[right_num].result != ACL_RES_ALLOW) { + if (allow_error) { + *acl_tag = allow_tag; + *expr_num = allow_num; + return (allow_error); + } + return (rights_arry[right_num].result); + } + + return (ACL_RES_ALLOW); + +} + + +/* ACL_CachableAclList + * Returns 1 if the ACL list will always evaluate to ALLOW for http_get. + */ +NSAPI_PUBLIC int +ACL_CachableAclList(ACLListHandle_t *acllist) +{ + ACLEvalHandle_t *acleval; + char *bong; + char *bong_type; + char *acl_tag; + int expr_num; + int rv; + static char *rights[] = { "http_get", NULL }; + ACLCachable_t cachable=ACL_INDEF_CACHABLE; + + if (!acllist || acllist == ACL_LIST_NO_ACLS) { + return 1; + } + acleval = ACL_EvalNew(NULL, NULL); + ACL_EvalSetACL(NULL, acleval, acllist); + rv = ACL_INTEvalTestRights(NULL, acleval, rights, http_generic, + &bong_type, &bong, &acl_tag, &expr_num, + &cachable); + + ACL_EvalDestroyNoDecrement(NULL, NULL, acleval); + if (rv == ACL_RES_ALLOW && cachable == ACL_INDEF_CACHABLE) { + return 1; + } + + return 0; +} + + +NSAPI_PUBLIC int +ACL_EvalTestRights( + NSErr_t *errp, + ACLEvalHandle_t *acleval, + char **rights, + char **map_generic, + char **deny_type, + char **deny_response, + char **acl_tag, + int *expr_num) +{ + ACLCachable_t cachable; + + return (ACL_INTEvalTestRights(errp, acleval, rights, map_generic, + deny_type, deny_response, + acl_tag, expr_num, &cachable)); +} + + +NSAPI_PUBLIC ACLEvalHandle_t * +ACL_EvalNew(NSErr_t *errp, pool_handle_t *pool) +{ + ACLEvalHandle_t *rv = ((ACLEvalHandle_t *)pool_calloc(pool, sizeof(ACLEvalHandle_t), 1)); + rv->default_result = ACL_RES_DENY; + return rv; +} + +NSAPI_PUBLIC void +ACL_EvalDestroy(NSErr_t *errp, pool_handle_t *pool, ACLEvalHandle_t *acleval) +{ + if (!acleval->acllist || acleval->acllist == ACL_LIST_NO_ACLS) + return; + NS_ASSERT(acleval->acllist->ref_count > 0); + + ACL_CritEnter(); + NS_ASSERT(ACL_CritHeld()); + if (--acleval->acllist->ref_count == 0) { + if (ACL_LIST_IS_STALE(acleval->acllist)) { + ACL_ListDestroy(errp, acleval->acllist); + } + } + ACL_CritExit(); + pool_free(pool, acleval); +} + +NSAPI_PUBLIC void +ACL_EvalDestroyNoDecrement(NSErr_t *errp, pool_handle_t *pool, ACLEvalHandle_t *acleval) +{ + /*if (!acleval->acllist || acleval->acllist == ACL_LIST_NO_ACLS) + return; */ + + /* olga: we need to free acleval unconditionally to avoid memory leaks */ + if (acleval) + pool_free(pool, acleval); +} + +NSAPI_PUBLIC int +ACL_ListDecrement(NSErr_t *errp, ACLListHandle_t *acllist) +{ + if (!acllist || acllist == ACL_LIST_NO_ACLS) + return 0; + + NS_ASSERT(ACL_AssertAcllist(acllist)); + + ACL_CritEnter(); + NS_ASSERT(ACL_CritHeld()); + if (--acllist->ref_count == 0) { + if (ACL_LIST_IS_STALE(acllist)) { + ACL_ListDestroy(errp, acllist); + } + } + ACL_CritExit(); + + return 0; +} + +NSAPI_PUBLIC int +ACL_EvalSetACL(NSErr_t *errp, ACLEvalHandle_t *acleval, ACLListHandle_t *acllist) +{ + NS_ASSERT(ACL_AssertAcllist(acllist)); + + acleval->acllist = acllist; + return(0); +} + +NSAPI_PUBLIC int +ACL_EvalSetSubject(NSErr_t *errp, ACLEvalHandle_t *acleval, PList_t subject) +{ + acleval->subject = subject; + return 0; +} + +NSAPI_PUBLIC PList_t +ACL_EvalGetSubject(NSErr_t *errp, ACLEvalHandle_t *acleval) +{ + return (acleval->subject); +} + +NSAPI_PUBLIC int +ACL_EvalSetResource(NSErr_t *errp, ACLEvalHandle_t *acleval, PList_t resource) +{ + acleval->resource = resource; + return 0; +} + +NSAPI_PUBLIC PList_t +ACL_EvalGetResource(NSErr_t *errp, ACLEvalHandle_t *acleval) +{ + return (acleval->resource); +} diff --git a/lib/libaccess/oneeval.h b/lib/libaccess/oneeval.h new file mode 100644 index 00000000..022570dd --- /dev/null +++ b/lib/libaccess/oneeval.h @@ -0,0 +1,17 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef LEVAL_H +#define LEVAL_H + +NSPR_BEGIN_EXTERN_C + +int +freeLAS(NSErr_t *errp, char *attribute, void **las_cookie); + +NSPR_END_EXTERN_C + +#endif + diff --git a/lib/libaccess/parse.h b/lib/libaccess/parse.h new file mode 100644 index 00000000..420bd913 --- /dev/null +++ b/lib/libaccess/parse.h @@ -0,0 +1,21 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * module private routines for handling the yacc based + * ACL Parser. + */ + +#ifndef PARSE_H +#define PARSE_H + +NSPR_BEGIN_EXTERN_C + +extern int acl_PushListHandle(ACLListHandle_t *handle); +extern int acl_Parse(void); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/permhash.h b/lib/libaccess/permhash.h new file mode 100644 index 00000000..f072be2b --- /dev/null +++ b/lib/libaccess/permhash.h @@ -0,0 +1,79 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#ifndef _PERMHASH_H_ +#define _PERMHASH_H_ + +#include <string.h> +#include <plhash.h> +#include <base/pool.h> +#include <base/util.h> + +static void * +ACL_PermAllocTable(void *pool, PRSize size) +{ + return pool_malloc((pool_handle_t *)pool, size); +} + +static void +ACL_PermFreeTable(void *pool, void *item) +{ + pool_free((pool_handle_t *)pool, item); +} + +static PLHashEntry * +ACL_PermAllocEntry(void *pool, const void *unused) +{ + return ((PLHashEntry *)pool_malloc((pool_handle_t *)pool, sizeof(PLHashEntry))); +} + +static void +ACL_PermFreeEntry(void *pool, PLHashEntry *he, PRUintn flag) +{ + if (flag == HT_FREE_ENTRY) + pool_free((pool_handle_t *)pool, he); +} + +static PLHashAllocOps ACLPermAllocOps = { + ACL_PermAllocTable, + ACL_PermFreeTable, + ACL_PermAllocEntry, + ACL_PermFreeEntry +}; + +static int +PR_StringFree(PLHashEntry *he, int i, void *arg) +{ + PERM_FREE(he->key); + return 0; +} + +static PLHashNumber +PR_HashCaseString(const void *key) +{ + PLHashNumber h; + const unsigned char *s; + + h = 0; + for (s = (const unsigned char *)key; *s; s++) + h = (h >> 28) ^ (h << 4) ^ tolower(*s); + return h; +} + +static int +PR_CompareCaseStrings(const void *v1, const void *v2) +{ + const char *s1 = (const char *)v1; + const char *s2 = (const char *)v2; + +#ifdef XP_WIN32 + return (util_strcasecmp(s1, s2) == 0); +#else + return (strcasecmp(s1, s2) == 0); +#endif +} + + +#endif /* _PERMHASH_H */ diff --git a/lib/libaccess/register.cpp b/lib/libaccess/register.cpp new file mode 100644 index 00000000..2973e1d5 --- /dev/null +++ b/lib/libaccess/register.cpp @@ -0,0 +1,821 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * LAS registration interface + */ + +#include <netsite.h> +#include <plhash.h> +#include <base/systems.h> +#include <base/util.h> +#include <base/nsassert.h> +#include "permhash.h" +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/aclglobal.h> +#include <libaccess/ldapacl.h> +#include "aclcache.h" +#include <libaccess/dbtlibaccess.h> +#include <libaccess/aclerror.h> + +/* This is to force aclspace.o into ns-httpd30.dll */ +static ACLGlobal_p *link_ACLGlobal = &ACLGlobal; + +/* This forces oneeval.o into ns-httpd30.dll */ +static ACLDispatchVector_t **link_nsacl_table = &__nsacl_table; + +ACLMethod_t ACLMethodDefault = ACL_METHOD_INVALID; +ACLDbType_t ACLDbTypeDefault = ACL_DBTYPE_INVALID; +static char *ACLDatabaseDefault = 0; + +ACLDbType_t ACL_DbTypeLdap = ACL_DBTYPE_INVALID; + +DbParseFn_t ACLDbParseFnTable[ACL_MAX_DBTYPE]; + +void +ACL_LasHashInit() +{ + int i; + + ACLLasEvalHash = PR_NewHashTable(0, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLLasEvalHash); + + ACLLasFlushHash = PR_NewHashTable(0, + PR_HashString, + PR_CompareStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLLasFlushHash); + + ACLMethodHash = PR_NewHashTable(ACL_MAX_METHOD, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLMethodHash); + + ACLDbTypeHash = PR_NewHashTable(ACL_MAX_DBTYPE, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLDbTypeHash); + + for (i = 0; i < ACL_MAX_DBTYPE; i++) + ACLDbParseFnTable[i] = 0; + + ACLAttrGetterHash = PR_NewHashTable(256, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLDbTypeHash); + + ACLDbNameHash = PR_NewHashTable(0, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + ACL_DATABASE_POOL); + NS_ASSERT(ACLDbNameHash); + + ACLUserLdbHash = PR_NewHashTable(0, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + NULL); + NS_ASSERT(ACLUserLdbHash); + + return; +} + +void +ACL_LasHashDestroy() +{ + if (ACLLasEvalHash) { + PR_HashTableDestroy(ACLLasEvalHash); + ACLLasEvalHash=NULL; + } + if (ACLLasFlushHash) { + PR_HashTableDestroy(ACLLasFlushHash); + ACLLasFlushHash=NULL; + } +} + +/* ACL_LasRegister + * INPUT + * errp NSError structure + * attr_name E.g. "ip" or "dns" etc. + * eval_func E.g. LASIpEval + * flush_func Optional - E.g. LASIpFlush or NULL + * OUTPUT + * 0 on success, non-zero on failure + */ +NSAPI_PUBLIC int +ACL_LasRegister(NSErr_t *errp, char *attr_name, LASEvalFunc_t eval_func, +LASFlushFunc_t flush_func) +{ + + if ((!attr_name) || (!eval_func)) return -1; + + ACL_CritEnter(); + + /* See if the function is already registered. If so, report and + * error, but go ahead and replace it. + */ + if (PR_HashTableLookup(ACLLasEvalHash, attr_name) != NULL) { + nserrGenerate(errp, ACLERRDUPSYM, ACLERR3900, ACL_Program, 1, + attr_name); + } + + /* Put it in the hash tables */ + PR_HashTableAdd(ACLLasEvalHash, attr_name, (void *)eval_func); + PR_HashTableAdd(ACLLasFlushHash, attr_name, (void *)flush_func); + + ACL_CritExit(); + return 0; +} + +/* ACL_LasFindEval + * INPUT + * errp NSError pointer + * attr_name E.g. "ip" or "user" etc. + * eval_funcp Where the function pointer is returned. NULL if the + * function isn't registered. + * Must be called in a critical section as ACLEvalHash is a global + * variable. + * OUTPUT + * 0 on success, non-zero on failure + */ +NSAPI_PUBLIC int +ACL_LasFindEval(NSErr_t *errp, char *attr_name, LASEvalFunc_t *eval_funcp) +{ + + NS_ASSERT(attr_name); + if (!attr_name) return -1; + + *eval_funcp = (LASEvalFunc_t)PR_HashTableLookup(ACLLasEvalHash, attr_name); + return 0; +} + + +/* ACL_LasFindFlush + * INPUT + * errp NSError pointer + * attr_name E.g. "ip" or "user" etc. + * eval_funcp Where the function pointer is returned. NULL if the + * function isn't registered. + * OUTPUT + * 0 on success, non-zero on failure + */ +NSAPI_PUBLIC int +ACL_LasFindFlush(NSErr_t *errp, char *attr_name, LASFlushFunc_t *flush_funcp) +{ + + NS_ASSERT(attr_name); + if (!attr_name) return -1; + + *flush_funcp = (LASFlushFunc_t)PR_HashTableLookup(ACLLasFlushHash, attr_name); + return 0; +} + + +/* ACL_MethodRegister + * INPUT + * name Method name string. Can be freed after return. + * OUTPUT + * &t Place to return the Method_t (>0) + * retcode 0 on success, non-zero otherwise + */ + +int cur_method = 0; /* Use a static counter to generate the numbers */ + +NSAPI_PUBLIC int +ACL_MethodRegister(NSErr_t *errp, const char *name, ACLMethod_t *t) +{ + ACLMethod_t rv; + + ACL_CritEnter(); + + /* See if this is already registered */ + rv = (ACLMethod_t) PR_HashTableLookup(ACLMethodHash, name); + if (rv != NULL) { + *t = rv; + ACL_CritExit(); + return 0; + } + + /* To prevent the hash table from resizing, don't get to 32 entries */ + if (cur_method >= (ACL_MAX_METHOD-1)) { + ACL_CritExit(); + return -1; + } + + /* Put it in the hash table */ + rv = PR_HashTableAdd(ACLMethodHash, name, (void *)++cur_method); + *t = (ACLMethod_t) cur_method; + + ACL_CritExit(); + return 0; +} + +NSAPI_PUBLIC int +ACL_MethodFind(NSErr_t *errp, const char *name, ACLMethod_t *t) +{ + ACLMethod_t rv; + + /* Don't have to get the Critical Section lock 'cause the only danger + * would be if the hash table had to be resized. We created it with + * room for 32 entries before that happens. + */ + rv = (ACLMethod_t) PR_HashTableLookup(ACLMethodHash, name); + if (rv != NULL) { + *t = rv; + return 0; + } + + return -1; +} + +typedef struct HashEnumArg_s { + char **names; + int count; +} HashEnumArg_t; + +typedef HashEnumArg_t *HashEnumArg_p; + +static int acl_hash_enumerator (PLHashEntry *he, PRIntn i, void *arg) +{ + HashEnumArg_t *info = (HashEnumArg_t *)arg; + char **names = info->names; + + names[info->count++] = STRDUP((const char *)he->key); + + return names[info->count-1] ? 0 : -1; +} + +int acl_registered_names(PLHashTable *ht, int count, char ***names) +{ + HashEnumArg_t arg; + int rv; + + if (count == 0) { + *names = 0; + return 0; + } + + arg.names = (char **)MALLOC(count * sizeof(char *)); + arg.count = 0; + + if (!arg.names) return -1; + + rv = PR_HashTableEnumerateEntries(ht, acl_hash_enumerator, &arg); + + if (rv >= 0) { + /* success */ + *names = arg.names; + } + else { + *names = 0; + } + + return rv; +} + +NSAPI_PUBLIC int +ACL_MethodNamesGet(NSErr_t *errp, char ***names, int *count) +{ + *count = cur_method; + return acl_registered_names (ACLMethodHash, *count, names); +} + +NSAPI_PUBLIC int +ACL_MethodNamesFree(NSErr_t *errp, char **names, int count) +{ + int i; + + if (!names) return 0; + + for (i = count-1; i; i--) FREE(names[i]); + + FREE(names); + return 0; +} + +NSAPI_PUBLIC int +ACL_DbTypeFind(NSErr_t *errp, const char *name, ACLDbType_t *t) +{ + ACLDbType_t rv; + + /* Don't have to get the Critical Section lock 'cause the only danger + * would be if the hash table had to be resized. We created it with + * room for 32 entries before that happens. + */ + rv = (ACLDbType_t) PR_HashTableLookup(ACLDbTypeHash, name); + if (rv != NULL) { + *t = rv; + return 0; + } + + return -1; +} + +/* ACL_DbTypeRegister + * INPUT + * name DbType name string. Can be freed after return. + * OUTPUT + * &t Place to return the DbType (>0) + * retcode 0 on success, non-zero otherwise + */ + +int cur_dbtype = 0; /* Use a static counter to generate the numbers */ + +NSAPI_PUBLIC int +ACL_DbTypeRegister(NSErr_t *errp, const char *name, DbParseFn_t func, ACLDbType_t *t) +{ + ACLDbType_t rv; + + ACL_CritEnter(); + + /* See if this is already registered */ + rv = (ACLDbType_t) PR_HashTableLookup(ACLDbTypeHash, name); + if (rv != NULL) { + *t = rv; + ACLDbParseFnTable[(int)(PRSize)rv] = func; + ACL_CritExit(); + return 0; + } + + /* To prevent the hash table from resizing, don't get to 32 entries */ + if (cur_dbtype >= (ACL_MAX_DBTYPE-1)) { + ACL_CritExit(); + return -1; + } + + /* Put it in the hash table */ + rv = PR_HashTableAdd(ACLDbTypeHash, name, (void *)++cur_dbtype); + *t = (ACLDbType_t) cur_dbtype; + ACLDbParseFnTable[cur_dbtype] = func; + + ACL_CritExit(); + return 0; +} + + +NSAPI_PUBLIC int +ACL_DbTypeIsRegistered (NSErr_t *errp, const ACLDbType_t t) +{ + return (0 < ((int)(PRSize)t) && ((int)(PRSize)t) <= cur_dbtype); +} + + +/* ACL_MethodIsEqual + * RETURNS non-zero if equal. + */ +NSAPI_PUBLIC int +ACL_MethodIsEqual(NSErr_t *errp, const ACLMethod_t t1, const ACLMethod_t t2) +{ + return (t1 == t2); +} + + +/* ACL_DbTypeIsEqual + * RETURNS non-zero if equal. + */ +NSAPI_PUBLIC int +ACL_DbTypeIsEqual(NSErr_t *errp, const ACLDbType_t t1, const ACLDbType_t t2) +{ + return (t1 == t2); +} + + +/* ACL_MethodNameIsEqual + * Takes a method type and a method name and sees if they match. + * Returns non-zero on match. + */ +NSAPI_PUBLIC int +ACL_MethodNameIsEqual(NSErr_t *errp, const ACLMethod_t t1, const char *name) +{ + int rv; + ACLMethod_t t2; + + rv = ACL_MethodFind(errp, name, &t2); + if (rv) + return (rv); + else + return (t1 == t2); +} + +/* ACL_DbTypeNameIsEqual + * Takes a dbtype type and a dbtype name and sees if they match. + * Returns non-zero on match. + */ +NSAPI_PUBLIC int +ACL_DbTypeNameIsEqual(NSErr_t *errp, const ACLDbType_t t1, const char *name) +{ + int rv; + ACLDbType_t t2; + + rv = ACL_DbTypeFind(errp, name, &t2); + if (rv) + return (rv); + else + return (t1 == t2); +} + +/* ACL_MethodGetDefault + */ +NSAPI_PUBLIC ACLMethod_t +ACL_MethodGetDefault(NSErr_t *errp) +{ + return (ACLMethodDefault); +} + +/* ACL_MethodSetDefault + */ +NSAPI_PUBLIC int +ACL_MethodSetDefault(NSErr_t *errp, const ACLMethod_t t) +{ + ACLMethodDefault = t; + return 0; +} + + +/* ACL_DbTypeGetDefault + */ +NSAPI_PUBLIC ACLDbType_t +ACL_DbTypeGetDefault(NSErr_t *errp) +{ + return (ACLDbTypeDefault); +} + +/* ACL_DbTypeSetDefault + */ +NSAPI_PUBLIC int +ACL_DbTypeSetDefault(NSErr_t *errp, ACLDbType_t t) +{ + ACLDbTypeDefault = t; + return 0; +} + + +/* ACL_DatabaseGetDefault + */ +NSAPI_PUBLIC const char * +ACL_DatabaseGetDefault(NSErr_t *errp) +{ + return (ACLDatabaseDefault); +} + +/* ACL_DatabaseSetDefault + */ +NSAPI_PUBLIC int +ACL_DatabaseSetDefault(NSErr_t *errp, const char *dbname) +{ + ACLDbType_t dbtype; + int rv; + void *db; + + if (!dbname || !*dbname) return LAS_EVAL_FAIL; + + rv = ACL_DatabaseFind(errp, dbname, &dbtype, &db); + + if (rv != LAS_EVAL_TRUE) return -1; + + if (ACLDatabaseDefault) pool_free(ACL_DATABASE_POOL, ACLDatabaseDefault); + + ACL_DbTypeSetDefault(errp, dbtype); + ACLDatabaseDefault = pool_strdup(ACL_DATABASE_POOL, dbname); + + return ACLDatabaseDefault ? 0 : -1; +} + + +/* ACL_AuthInfoGetMethod + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * OUTPUT + * *t The Method number. This can be the default method + number if the auth_info PList doesn't explicitly have a Method entry. + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoGetMethod(NSErr_t *errp, PList_t auth_info, ACLMethod_t *t) +{ + ACLMethod_t *methodp; + + if (!auth_info || + PListGetValue(auth_info, ACL_ATTR_METHOD_INDEX, (void **)&methodp, NULL) < 0) + { + /* No entry for "method" */ + *t = ACLMethodDefault; + } else { + *t = *methodp; + } + + return 0; +} + + +/* ACL_AuthInfoSetMethod + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * t The Method number. + * OUTPUT + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoSetMethod(NSErr_t *errp, PList_t auth_info, ACLMethod_t t) +{ + ACLMethod_t *methodp; + int rv; + + if (auth_info) { + rv = PListGetValue(auth_info, ACL_ATTR_METHOD_INDEX, (void **)&methodp, + NULL); + + if (rv < 0) { + /* No entry for "method" */ + methodp = (ACLMethod_t *)PERM_MALLOC(sizeof(ACLMethod_t)); + if (!methodp) return -1; + *methodp = t; + PListInitProp(auth_info, ACL_ATTR_METHOD_INDEX, ACL_ATTR_METHOD, methodp, 0); + } + else { + /* replace the old entry */ + if (!methodp) return -1; + *methodp = t; + } + } + else { + return -1; + } + + return 0; +} + + +/* ACL_AuthInfoSetDbname + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * dbname Name of the new auth_info database. + * OUTPUT + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoSetDbname(NSErr_t *errp, PList_t auth_info, const char *dbname) +{ + ACLDbType_t *dbtype = (ACLDbType_t *)PERM_MALLOC(sizeof(ACLDbType_t)); + ACLDbType_t *t2; + char *copy; + char *n2; + void *db; + int old1; + int old2; + int rv; + + if (!dbtype) { + /* out of memory */ + return -1; + } + + if (auth_info) { + rv = ACL_DatabaseFind(errp, dbname, dbtype, (void **)&db); + + if (rv != LAS_EVAL_TRUE) { + PERM_FREE(dbtype); + return -1; + } + + /* Check the existing entry */ + old1 = PListGetValue(auth_info, ACL_ATTR_DBTYPE_INDEX, (void **)&t2, + NULL); + old2 = PListGetValue(auth_info, ACL_ATTR_DATABASE_INDEX, (void **)&n2, + NULL); + + if (old1 >= 0 && old2 >= 0) { + /* check if the old entry is same */ + if (ACL_DbTypeIsEqual(errp, *dbtype, *t2)) { + /* Nothing to do */ + PERM_FREE(dbtype); + return 0; + } + } + /* free the old entries */ + if (old1 >= 0) { + PListDeleteProp(auth_info, ACL_ATTR_DBTYPE_INDEX, ACL_ATTR_DBTYPE); + PERM_FREE(t2); + } + if (old2 >= 0) { + PListDeleteProp(auth_info, ACL_ATTR_DATABASE_INDEX, ACL_ATTR_DATABASE); + PERM_FREE(n2); + } + + /* Create new entries for "dbtype" & "dbname" */ + copy = (char *)PERM_STRDUP(dbname); + if (!copy) return -1; + PListInitProp(auth_info, ACL_ATTR_DATABASE_INDEX, + ACL_ATTR_DATABASE, copy, 0); + PListInitProp(auth_info, ACL_ATTR_DBTYPE_INDEX, ACL_ATTR_DBTYPE, + dbtype, 0); + } + else { + return -1; + } + + return 0; +} + + +/* ACL_AuthInfoGetDbType + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * OUTPUT + * *t The DbType number. This can be the default dbtype + * number if the auth_info PList doesn't explicitly + * have a DbType entry. + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoGetDbType(NSErr_t *errp, PList_t auth_info, ACLDbType_t *t) +{ + ACLDbType_t *dbtypep; + + if (!auth_info || + PListGetValue(auth_info, ACL_ATTR_DBTYPE_INDEX, (void **)&dbtypep, NULL) < 0) + { + /* No entry for "dbtype" */ + *t = ACLDbTypeDefault; + } else { + *t = *dbtypep; + } + + return 0; +} + +/* ACL_AuthInfoGetDbname + * INPUT + * auth_info A PList of the authentication name/value pairs as + * provided by EvalTestRights to the LAS. + * OUTPUT + * dbname The database name. This can be the default database + * name if the auth_info PList doesn't explicitly + * have a database entry. + * retcode 0 on success. + */ +NSAPI_PUBLIC int +ACL_AuthInfoGetDbname(PList_t auth_info, char **dbname) +{ + char *dbstr; + + if (!auth_info || + PListGetValue(auth_info, ACL_ATTR_DATABASE_INDEX, (void **)&dbstr, NULL) < 0) + { + /* No entry for "database" */ + dbstr = ACLDatabaseDefault; + } + + /* else the value was already set by the PListGetValue call */ + *dbname = dbstr; + return 0; +} + +NSAPI_PUBLIC DbParseFn_t +ACL_DbTypeParseFn(NSErr_t *errp, const ACLDbType_t dbtype) +{ + if (ACL_DbTypeIsRegistered(errp, dbtype)) + return ACLDbParseFnTable[(int)(PRSize)dbtype]; + else + return 0; +} + +/* The hash table is keyed by attribute name, and contains pointers to the + * PRCList headers. These in turn, circularly link a set of AttrGetter_s + * structures. + */ +NSAPI_PUBLIC int +ACL_AttrGetterRegister(NSErr_t *errp, const char *attr, ACLAttrGetterFn_t fn, + ACLMethod_t m, ACLDbType_t d, int position, void *arg) +{ + ACLAttrGetter_t *getter; + PLHashEntry **hep; + + if (position != ACL_AT_FRONT && position != ACL_AT_END) { + return -1; + } + + ACL_CritEnter(); + + hep = PR_HashTableRawLookup(ACLAttrGetterHash, PR_HashCaseString(attr), attr); + + /* Now, allocate the current entry */ + getter = (ACLAttrGetter_t *)CALLOC(sizeof(ACLAttrGetter_t)); + if (getter == NULL) { + ACL_CritExit(); + return -1; + } + getter->method = m; + getter->dbtype = d; + getter->fn = fn; + getter->arg = arg; + + if (*hep == 0) { /* New entry */ + + PR_INIT_CLIST(&getter->list); + PR_HashTableAdd(ACLAttrGetterHash, attr, (void *)getter); + } + else { + + ACLAttrGetter_t *head = (ACLAttrGetter_t *)((*hep)->value); + + PR_INSERT_BEFORE(&getter->list, &head->list); + + if (position == ACL_AT_FRONT) { + + /* Set new head of list */ + (*hep)->value = (void *)getter; + } + } + + ACL_CritExit(); + return 0; +} + +NSAPI_PUBLIC int +ACL_AttrGetterFind(NSErr_t *errp, const char *attr, + ACLAttrGetterList_t *getters) +{ + *getters = PR_HashTableLookup(ACLAttrGetterHash, attr); + if (*getters) + return 0; + else + return -1; +} + +NSAPI_PUBLIC +ACLAttrGetter_t * ACL_AttrGetterFirst(ACLAttrGetterList_t *getters) +{ + ACLAttrGetter_t * first = 0; + + if (getters && *getters) { + + first = (ACLAttrGetter_t *)(*getters); + } + + return first; +} + +NSAPI_PUBLIC ACLAttrGetter_t * +ACL_AttrGetterNext(ACLAttrGetterList_t *getters, ACLAttrGetter_t *last) +{ + ACLAttrGetter_t *head; + ACLAttrGetter_t *next = 0; + + if (getters && *getters && last) { + + head = (ACLAttrGetter_t *)(*getters); + if (head) { + + /* End of list? */ + if (last != (ACLAttrGetter_t *)PR_LIST_TAIL(&head->list)) { + + /* No, get next entry */ + next = (ACLAttrGetter_t *)PR_NEXT_LINK(&last->list); + } + } + } + + return next; +} + +int +ACL_RegisterInit () +{ + NSErr_t *errp = 0; + int rv; + + /* Register the ldap database */ + rv = ACL_DbTypeRegister(errp, ACL_DBTYPE_LDAP, parse_ldap_url, &ACL_DbTypeLdap); + + return rv; +} + diff --git a/lib/libaccess/register.h b/lib/libaccess/register.h new file mode 100644 index 00000000..981e2cb8 --- /dev/null +++ b/lib/libaccess/register.h @@ -0,0 +1,98 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifndef ACL_REGISTER_HEADER +#define ACL_REGISTER_HEADER + +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include <libaccess/las.h> + +typedef void * ACLMethod_t; +#define ACL_METHOD_ANY (ACLMethod_t)-1 +#define ACL_METHOD_INVALID (ACLMethod_t)-2 +typedef void * ACLDbType_t; +#define ACL_DBTYPE_ANY (ACLDbType_t)-1 +#define ACL_DBTYPE_INVALID (ACLDbType_t)-2 + +typedef struct ACLGetter_s { + ACLMethod_t method; + ACLDbType_t db; + AttrGetterFn fn; +} ACLGetter_t; +typedef ACLGetter_s * ACLGetter_p; + +/* + * Command values for the "position" argument to ACL_RegisterGetter + * Any positive >0 value is the specific position in the list to insert + * the new function. + */ +#define ACL_AT_FRONT 0 +#define ACL_AT_END -1 +#define ACL_REPLACE_ALL -2 +#define ACL_REPLACE_MATCHING -3 + +#ifdef ACL_LIB_INTERNAL +#define ACL_MAX_METHOD 32 +#define ACL_MAX_DBTYPE 32 +#endif + +NSPR_BEGIN_EXTERN_C + +NSAPI_PUBLIC extern int + ACL_LasRegister( NSErr_t *errp, char *attr_name, LASEvalFunc_t + eval_func, LASFlushFunc_t flush_func ); +NSAPI_PUBLIC extern int + ACL_LasFindEval( NSErr_t *errp, char *attr_name, LASEvalFunc_t + *eval_funcp ); +NSAPI_PUBLIC extern int + ACL_LasFindFlush( NSErr_t *errp, char *attr_name, LASFlushFunc_t + *flush_funcp ); +extern void + ACL_LasHashInit( void ); +extern void + ACL_LasHashDestroy( void ); + +/* + * Revised, normalized method/dbtype registration routines + */ +NSAPI_PUBLIC extern int + ACL_MethodRegister(const char *name, ACLMethod_t *t); +NSAPI_PUBLIC extern int + ACL_MethodIsEqual(ACLMethod_t t1, ACLMethod_t t2); +NSAPI_PUBLIC extern int + ACL_MethodNameIsEqual(ACLMethod_t t, const char *name); +NSAPI_PUBLIC extern int + ACL_MethodFind(const char *name, ACLMethod_t *t); +NSAPI_PUBLIC extern ACLMethod_t + ACL_MethodGetDefault(); +NSAPI_PUBLIC extern void + ACL_MethodSetDefault(); +NSAPI_PUBLIC extern int + ACL_AuthInfoGetMethod(PList_t auth_info, ACLMethod_t *t); + +NSAPI_PUBLIC extern int + ACL_DbTypeRegister(const char *name, DbParseFn_t func, ACLDbType_t *t); +NSAPI_PUBLIC extern int + ACL_DbTypeIsEqual(ACLDbType_t t1, ACLDbType_t t2); +NSAPI_PUBLIC extern int + ACL_DbTypeNameIsEqual(ACLDbType_t t, const char *name); +NSAPI_PUBLIC extern int + ACL_DbTypeFind(const char *name, ACLDbType_t *t); +NSAPI_PUBLIC extern ACLDbType_t + ACL_DbTypeGetDefault(); +NSAPI_PUBLIC extern void + ACL_DbTypeSetDefault(); +NSAPI_PUBLIC extern int + ACL_AuthInfoGetDbType(PList_t auth_info, ACLDbType_t *t); + +NSAPI_PUBLIC extern int + ACL_RegisterGetter(AttrGetterFn fn, ACLMethod_t m, ACLDbType_t d, int + position, void *arg); + +NSPR_END_EXTERN_C + +#endif diff --git a/lib/libaccess/symbols.cpp b/lib/libaccess/symbols.cpp new file mode 100644 index 00000000..ca1e18b1 --- /dev/null +++ b/lib/libaccess/symbols.cpp @@ -0,0 +1,350 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* + * Description (symbols.c) + * + * This module implements a symbol table for ACL-related structures. + * The symbol table associates string names and types with pointers + * to various kinds of structures. + */ +/* +#include <base/systems.h> +*/ +#include <plhash.h> +#include <base/util.h> +#include <netsite.h> +#define __PRIVATE_SYMBOLS +#include "libaccess/symbols.h" +#include <ctype.h> + +static PLHashEntry * symAllocEntry(void * pool, const void *unused); +static void * symAllocTable(void * pool, PRSize size); +static int symCmpName(const void * name1, const void * name2); +static int symCmpValue(const void * value1, const void * value2); +static PLHashNumber symHash(const void * symkey); +static void symFreeEntry(void * pool, PLHashEntry * he, PRUintn flag); +static void symFreeTable(void * pool, void * item); + +/* Table of pointers to functions associated with the hash table */ +static PLHashAllocOps SymAllocOps = { + symAllocTable, /* allocate the hash table */ + symFreeTable, /* free the hash table */ + symAllocEntry, /* allocate a table entry */ + symFreeEntry, /* free a table entry */ +}; + +static void * symAllocTable(void * pool, PRSize size) +{ + return (void *)PERM_MALLOC(size); +} + +static void symFreeTable(void * pool, void * item) +{ + PERM_FREE(item); +} + + + +static PLHashEntry * symAllocEntry(void * pool, const void *ignored) +{ + PLHashEntry * he; + + he = (PLHashEntry *) PERM_MALLOC(sizeof(PLHashEntry)); + + return he; +} + +static void symFreeEntry(void * pool, PLHashEntry * he, PRUintn flag) +{ + if (flag == HT_FREE_ENTRY) { + /* Just free the hash entry, not anything it references */ + PERM_FREE(he); + } +} + + +static int symCmpName(const void * name1, const void * name2) +{ + Symbol_t * sym1 = (Symbol_t *)name1; + Symbol_t * sym2 = (Symbol_t *)name2; + + return ((sym1->sym_type == sym2->sym_type) && + !strcasecmp(sym1->sym_name, sym2->sym_name)); +} + +static int symCmpValue(const void * value1, const void * value2) +{ + return (value1 == value2); +} + +static PLHashNumber symHash(const void * symkey) +{ + Symbol_t * sym = (Symbol_t *)symkey; + char * cp; + PLHashNumber h; + + h = sym->sym_type; + cp = sym->sym_name; + if (cp) { + while (*cp) { + h = (h << 3) ^ tolower(*cp); + ++cp; + } + } + + return h; +} + +/* Helper function for symTableEnumerate() */ +typedef struct { + int (*func)(Symbol_t * sym, void * parg); + void * argp; +} SymTableEnum_t; + +static int symTableEnumHelp(PLHashEntry * he, int n, void * step) +{ + SymTableEnum_t * ste = (SymTableEnum_t *)step; + int ret = 0; + int rv; + + rv = (*ste->func)((Symbol_t *)(he->key), ste->argp); + if (rv != 0) { + if (rv & SYMENUMREMOVE) ret = HT_ENUMERATE_REMOVE; + if (rv & SYMENUMSTOP) ret |= HT_ENUMERATE_STOP; + } + + return ret; +} + +NSPR_BEGIN_EXTERN_C + +/* + * Description (symTableAddSym) + * + * This function adds a symbol definition to the symbol table. + * The symbol definition includes a name string, a type, and a + * reference to a structure. + * + * Arguments: + * + * table - handle for symbol table + * newsym - pointer to new symbol name and type + * symref - pointer to structure named by symbol + * + * Returns: + * + * If successful, the return code is zero. An error is indicated + * by a negative return code (SYMERRxxxx - see symbols.h). + */ + +int symTableAddSym(void * table, Symbol_t * newsym, void * symref) +{ + SymTable_t * st = (SymTable_t *)table; + PLHashEntry * he; + PLHashEntry **hep; + PLHashNumber keyhash; + int rv = 0; + + /* Compute the hash value for this symbol */ + keyhash = symHash((const void *)newsym); + + crit_enter(st->stb_crit); + + /* See if another symbol already has the same name and type */ + hep = PL_HashTableRawLookup(st->stb_ht, keyhash, (void *)newsym); + if (*hep == 0) { + + /* Expand the hash table if necessary and allocate an entry */ + he = PL_HashTableRawAdd(st->stb_ht, + hep, keyhash, (void *)newsym, symref); + } + else { + /* The symbol is already there. It's an error */ + rv = SYMERRDUPSYM; + } + + crit_exit(st->stb_crit); + return rv; +} + +/* + * Description (symTableRemoveSym) + * + * This function removes an entry from a symbol table. It does + * not free the entry itself, just the hash entry that references + * it. + * + * Arguments: + * + * table - symbol table handle + * sym - pointer to symbol structure + */ + +void symTableRemoveSym(void * table, Symbol_t * sym) +{ + SymTable_t * st = (SymTable_t *)table; + + if (sym->sym_name != 0) { + crit_enter(st->stb_crit); + PL_HashTableRemove(st->stb_ht, (void *)sym); + crit_exit(st->stb_crit); + } +} + +/* + * Description (symTableEnumerate) + * + * This function enumerates all of the entries in a symbol table, + * calling a specified function for each entry. The function + * specified by the caller may return flags indicating actions + * to be taken for each entry or whether to terminate the + * enumeration. These flags are defined in symbols.h as + * SYMENUMxxxx. + * + * Arguments: + * + * table - symbol table handle + * argp - argument for caller function + * func - function to be called for each entry + */ + +void symTableEnumerate(void * table, void * argp, +#ifdef UnixWare /* Fix bug in UnixWare compiler for name mangeling - nedh@sco.com */ + ArgFn_symTableEnum func) +#else + int (*func)(Symbol_t * sym, void * parg)) +#endif +{ + SymTable_t * st = (SymTable_t *)table; + SymTableEnum_t ste; /* enumeration arguments */ + + ste.func = func; + ste.argp = argp; + + crit_enter(st->stb_crit); + (void)PL_HashTableEnumerateEntries(st->stb_ht, + symTableEnumHelp, (void *)&ste); + crit_exit(st->stb_crit); +} + +/* + * Description (symTableFindSym) + * + * This function locates a symbol with a specified name and type + * in a given symbol table. It returns a pointer to the structure + * named by the symbol. + * + * Arguments: + * + * table - symbol table handle + * symname - symbol name string pointer + * symtype - symbol type code + * psymref - pointer to returned structure pointer + * + * Returns: + * + * If successful, the return code is zero and the structure pointer + * associated with the symbol name and type is returned in the + * location specified by 'psymref'. An error is indicated by a + * negative return code (SYMERRxxxx - see symbols.h). + */ + +int symTableFindSym(void * table, char * symname, + int symtype, void **psymref) +{ + SymTable_t * st = (SymTable_t *)table; + Symbol_t sym; + void * symref; + + /* Create temporary entry with fields needed by symHash() */ + sym.sym_name = symname; + sym.sym_type = symtype; + + crit_enter(st->stb_crit); + + symref = PL_HashTableLookup(st->stb_ht, (void *)&sym); + + crit_exit(st->stb_crit); + + *psymref = symref; + + return (symref) ? 0 : SYMERRNOSYM; +} + +/* + * Description (symTableDestroy) + * + * This function destroys a symbol table created by symTableNew(). + * + * Arguments: + * + * table - symbol table handle from symTableNew() + * flags - bit flags (unused - must be zero) + */ + +void symTableDestroy(void * table, int flags) +{ + SymTable_t * st = (SymTable_t *)table; + + if (st) { + if (st->stb_crit) { + crit_terminate(st->stb_crit); + } + + if (st->stb_ht) { + PL_HashTableDestroy(st->stb_ht); + } + + PERM_FREE(st); + } +} + +/* + * Description (symTableNew) + * + * This function creates a new symbol table, and returns a handle + * for it. + * + * Arguments: + * + * ptable - pointer to returned symbol table handle + * + * Returns: + * + * If successful, the return code is zero and a handle for the new + * symbol table is returned in the location specified by 'ptable'. + * An error is indicated by a negative return code (SYMERRxxxx + * - see symbols.h). + */ + +int symTableNew(void **ptable) +{ + SymTable_t * st; + + /* Allocate the symbol table object */ + st = (SymTable_t *)PERM_MALLOC(sizeof(SymTable_t)); + if (st == 0) goto err_nomem; + + /* Get a monitor for it */ + st->stb_crit = crit_init(); + + st->stb_ht = PL_NewHashTable(0, symHash, symCmpName, symCmpValue, + &SymAllocOps, 0); + if (st->stb_ht == 0) goto err_nomem; + + *ptable = st; + return 0; + + err_nomem: + if (st) { + symTableDestroy(st, 0); + } + return SYMERRNOMEM; +} + +NSPR_END_EXTERN_C + diff --git a/lib/libaccess/userauth.cpp b/lib/libaccess/userauth.cpp new file mode 100644 index 00000000..3413fbd0 --- /dev/null +++ b/lib/libaccess/userauth.cpp @@ -0,0 +1,12 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* userauth.c + * This file contain code to authenticate user. + */ + + + diff --git a/lib/libaccess/usi.cpp b/lib/libaccess/usi.cpp new file mode 100644 index 00000000..677f3bd7 --- /dev/null +++ b/lib/libaccess/usi.cpp @@ -0,0 +1,371 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#include "base/systems.h" +#include "netsite.h" +#include "assert.h" +#include "libaccess/usi.h" + +/* + * Description (usiAlloc) + * + * This function is used to initialize a USIList_t structure to + * reference an array of unsigned integers, where the size of the + * array is specified. The caller is responsible for initializing + * the specified number of values in the array. + * + * Arguments: + * + * uilptr - pointer to list head + * count - number of entries to allocate + * + * Returns: + * + * If successful, a pointer to the USI_t array now referenced by the + * list head (uilptr) is returned. An error is indicated by a null + * return value. + */ + +USI_t * usiAlloc(USIList_t * uilptr, int count) +{ + /* Is there space already allocated for this list? */ + if (uilptr->uil_size > 0) { + + /* If it's not big enough to hold the desired size, free it */ + if (count > uilptr->uil_size) { + FREE(uilptr->uil_list); + UILINIT(uilptr); + } + } + + /* Do we need space? */ + if (uilptr->uil_size < count) { + + /* Yes, allocate space for the specified number of values */ + uilptr->uil_list = (USI_t *)MALLOC(sizeof(USI_t) * count); + if (uilptr->uil_list == 0) { + + /* Error - no memory available */ + uilptr->uil_count = 0; + return 0; + } + + uilptr->uil_size = count; + } + + uilptr->uil_count = count; + + return uilptr->uil_list; +} + +/* + * Description (usiInsert) + * + * This function is called to insert a specified USI_t value into + * a given list of USI_t values. The values are maintained in an + * array, where they are kept in ascending order. Duplicate values + * are rejected. + * + * Arguments: + * + * uilptr - pointer to list head + * usi - value to be inserted + * + * Returns: + * + * If the specified value is already in the list, zero is returned. + * If the value is successfully inserted into the list, +1 is + * returned. An error is indicated by a negative return value. + */ + +int usiInsert(USIList_t * uilptr, USI_t usi) +{ + int ilow, ihigh, i; + USI_t * ids; + + ids = uilptr->uil_list; + + /* Binary search for specified group id */ + i = 0; + for (ilow = 0, ihigh = uilptr->uil_count; ilow != ihigh; ) { + + i = (ilow + ihigh) >> 1; + if (usi == ids[i]) { + /* The value is already in the list */ + return 0; + } + + if (usi > ids[i]) { + ilow = i + 1; + } + else { + ihigh = i; + } + } + + /* Check for empty list */ + if (uilptr->uil_count <= 0) { + + /* Any space allocated for the list yet? */ + if (uilptr->uil_size <= 0) { + + /* No, allocate some initial space */ + ids = (USI_t *) MALLOC(sizeof(USI_t) * 4); + if (ids == 0) { + /* Error - no memory available */ + return -1; + } + + uilptr->uil_size = 4; + uilptr->uil_list = ids; + } + + /* Value will be inserted at ilow, which is zero */ + } + else { + + /* + * Set ilow to the index at which the specified value + * should be inserted. + */ + if (usi > ids[i]) ++i; + ilow = i; + + /* Is there space for another value? */ + if (uilptr->uil_count >= uilptr->uil_size) { + + /* No, expand the array to hold more values */ + ids = (USI_t *)REALLOC(ids, + (uilptr->uil_size + 4) * sizeof(USI_t)); + if (ids == 0) { + /* Error - no memory available */ + return -1; + } + + uilptr->uil_size += 4; + uilptr->uil_list = ids; + } + + /* Copy higher values up */ + for (i = uilptr->uil_count; i > ilow; --i) { + ids[i] = ids[i-1]; + } + } + + /* Add the new value */ + ids[ilow] = usi; + uilptr->uil_count += 1; + + return 1; +} + +/* + * Description (usiPresent) + * + * This function is called to check whether a specified USI_t value + * is present in a given list. + * + * Arguments: + * + * uilptr - pointer to list head + * usi - value to check for + * + * Returns: + * + * The return value is the index of the value in the list, plus one, + * if the value is present in the list, 0 if it is not. + */ + +int usiPresent(USIList_t * uilptr, USI_t usi) +{ + int ilow, ihigh, i; + USI_t * ids; + + ids = uilptr->uil_list; + + /* Binary search for specified group id */ + i = 0; + for (ilow = 0, ihigh = uilptr->uil_count; ilow != ihigh; ) { + + i = (ilow + ihigh) >> 1; + if (usi == ids[i]) { + /* The value is in the list */ + return i + 1; + } + + if (usi > ids[i]) { + ilow = i + 1; + } + else { + ihigh = i; + } + } + + /* The value was not found */ + return 0; +} + +/* + * Description (usiRemove) + * + * This function is called to remove a specified USI_t value from + * a given list. The list is compressed when the value is removed. + * + * Arguments: + * + * uilptr - pointer to list head + * usi - value to be removed + * + * Returns: + * + * Returns the value returned by usiPresent(uilptr, usi). + */ + +int usiRemove(USIList_t * uilptr, USI_t usi) +{ + USI_t * ids; + int i, j; + + i = usiPresent(uilptr, usi); + if (i > 0) { + + /* Compress the remaining values */ + ids = uilptr->uil_list; + for (j = i ; j < uilptr->uil_count; ++j) { + ids[j-1] = ids[j]; + } + + /* Decrement the number of values and free space if none left */ + if (--uilptr->uil_count <= 0) { + FREE(uilptr->uil_list); + UILINIT(uilptr); + } + } + + return i; +} + +/* + * Description (uilDuplicate) + * + * This function is called to make a duplicate of a specified + * source list, in a given destination list. Any existing list + * referenced by the destination list head is either overwritten + * or replaced with a newly allocated list. The values in the + * source list are copied to the destination. Note that the + * destination list area may be larger than the source list area + * on return, i.e. their uil_size values may differ. + * + * Arguments: + * + * dstptr - pointer to destination list head + * srcptr - pointer to source list head + * + * Returns: + * + * The number of elements in the source and destination lists is + * returned if successful. An error is indicated by a negative + * return value. + */ + +int uilDuplicate(USIList_t * dstptr, USIList_t * srcptr) +{ + USI_t * idlist; + USI_t * srclist; + int count; + int i; + + count = srcptr->uil_count; + srclist = srcptr->uil_list; + + /* Allocate enough space in the destination list */ + idlist = usiAlloc(dstptr, count); + if ((idlist == 0) && (count > 0)) { + /* Error - insufficient memory */ + return -1; + } + + /* Copy source values to destination */ + for (i = 0; i < count; ++i) { + idlist[i] = srclist[i]; + } + + /* Return number of entries in destination list */ + return count; +} + +/* + * Description (uilMerge) + * + * This function is called to merge the values in a source list + * into a destination list. That is, any values in the source + * list which are not in the destination list will be inserted + * in it. + * + * Arguments: + * + * dstptr - pointer to destination list head + * srcptr - pointer to source list head + * + * Returns: + * + * The resulting number of elements in the destination list is + * returned if successful. An error is indicated by a negative + * return value. + */ + +int uilMerge(USIList_t * dstptr, USIList_t * srcptr) +{ + USIList_t mglist; /* list head for merged list */ + USI_t * srclist = srcptr->uil_list; + USI_t * dstlist = dstptr->uil_list; + int isrc, idst; + int scnt, dcnt; + int rv; + + UILINIT(&mglist); + + scnt = srcptr->uil_count; + dcnt = dstptr->uil_count; + isrc = 0; + idst = 0; + + while ((isrc < scnt) && (idst < dcnt)) { + + if (srclist[isrc] >= dstlist[idst]) { + rv = usiInsert(&mglist, dstlist[idst]); + if (rv < 0) goto punt; + if (srclist[isrc] == dstlist[idst]) ++isrc; + ++idst; + } + else if (srclist[isrc] < dstlist[idst]) { + rv = usiInsert(&mglist, srclist[isrc]); + if (rv < 0) goto punt; + ++isrc; + } + } + + while (isrc < scnt) { + rv = usiInsert(&mglist, srclist[isrc]); + if (rv < 0) goto punt; + ++isrc; + } + + while (idst < dcnt) { + rv = usiInsert(&mglist, dstlist[idst]); + if (rv < 0) goto punt; + ++idst; + } + + UILREPLACE(dstptr, &mglist); + + return dstptr->uil_count; + + punt: + UILFREE(&mglist); + return rv; +} + diff --git a/lib/libaccess/usrcache.cpp b/lib/libaccess/usrcache.cpp new file mode 100644 index 00000000..2f79d2cc --- /dev/null +++ b/lib/libaccess/usrcache.cpp @@ -0,0 +1,657 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* #define DBG_PRINT */ + + +#include <netsite.h> +extern "C" { +#include <secitem.h> +} +#include <base/crit.h> +#include <ldaputil/errors.h> +#include <libaccess/usrcache.h> +#include <libaccess/las.h> +#include <libaccess/authdb.h> +#include "permhash.h" + +/* uid is unique within a database. The user cache tables are stored per + * database. The following table maps a database name to the corresponding + * user cache table. The user cache table is another hash table which stores + * the UserCacheObj instances. + */ +static PRHashTable *databaseUserCacheTable = 0; +static time_t acl_usr_cache_lifetime = (time_t)120; +static PRCList *usrobj_list = 0; +static const int num_usrobj = 200; +static CRITICAL usr_hash_crit = NULL; /* Controls user cache hash tables & */ + /* usrobj link list */ +static pool_handle_t *usrcache_pool = NULL; +static PRHashTable *singleDbTable = 0; + +#define USEROBJ_PTR(l) \ + ((UserCacheObj*) ((char*) (l) - offsetof(UserCacheObj, list))) + +static void user_hash_crit_enter (void) +{ + /* Caching may be disabled (usr_hash_crit will be NULL) */ + if (usr_hash_crit) crit_enter(usr_hash_crit); +} + +static void user_hash_crit_exit (void) +{ + /* Caching may be disabled (usr_hash_crit will be NULL) */ + if (usr_hash_crit) crit_exit(usr_hash_crit); +} + +static void user_hash_crit_init (void) +{ + usr_hash_crit = crit_init(); +} + +static PRHashNumber +usr_cache_hash_cert(const void *key) +{ + PRHashNumber h; + const unsigned char *s; + unsigned int i = 0; + SECItem *derCert = (SECItem *)key; + unsigned int len = derCert->len; + + h = 0; + for (s = (const unsigned char *)derCert->data; i < len; s++, i++) + h = (h >> 28) ^ (h << 4) ^ *s; + return h; +} + +static PRHashNumber +usr_cache_hash_fn (const void *key) +{ + UserCacheObj *usrObj = (UserCacheObj *)key; + + if (usrObj->derCert) + return usr_cache_hash_cert(usrObj->derCert); + else + return PR_HashCaseString(usrObj->uid); +} + +static int +usr_cache_compare_certs(const void *v1, const void *v2) +{ + const SECItem *c1 = (const SECItem *)v1; + const SECItem *c2 = (const SECItem *)v2; + + return (c1->len == c2 ->len && !memcmp(c1->data, c2->data, c1->len)); +} + +static int +usr_cache_compare_fn(const void *v1, const void *v2) +{ + UserCacheObj *usrObj1 = (UserCacheObj *)v1; + UserCacheObj *usrObj2 = (UserCacheObj *)v2; + + if (usrObj1->derCert && usrObj2->derCert) + return usr_cache_compare_certs(usrObj1->derCert, usrObj2->derCert); + else if (!usrObj1->derCert && !usrObj2->derCert) + return PR_CompareCaseStrings(usrObj1->uid, usrObj1->uid); + else + return 0; +} + +static PRHashTable *alloc_db2uid_table () +{ + return PR_NewHashTable(0, + usr_cache_hash_fn, + usr_cache_compare_fn, + PR_CompareValues, + &ACLPermAllocOps, + usrcache_pool); +} + + +int acl_usr_cache_set_timeout (const int nsec) +{ + acl_usr_cache_lifetime = (time_t)nsec; + return 0; +} + + +int acl_usr_cache_enabled () +{ + return (acl_usr_cache_lifetime > 0); +} + + +int acl_usr_cache_init () +{ + UserCacheObj *usrobj; + int i; + + if (acl_usr_cache_lifetime <= 0) { + /* Caching is disabled */ + DBG_PRINT1("usrcache is disabled"); + return 0; + } + + usrcache_pool = pool_create(); + user_hash_crit_init(); + + if (acl_num_databases() == 0) { + /* Something wrong -- No databases registered yet! */ + return -1; + } + else if (acl_num_databases() == 1) { + /* Optimize for single database */ + DBG_PRINT1("Optimizing usrcache for single db"); + singleDbTable = alloc_db2uid_table(); + } + else { + singleDbTable = 0; + databaseUserCacheTable = PR_NewHashTable(0, + PR_HashCaseString, + PR_CompareCaseStrings, + PR_CompareValues, + &ACLPermAllocOps, + usrcache_pool); + } + + /* Allocate first UserCacheObj and initialize the circular link list */ + usrobj = (UserCacheObj *)pool_malloc(usrcache_pool, sizeof(UserCacheObj)); + if (!usrobj) return -1; + memset((void *)usrobj, 0, sizeof(UserCacheObj)); + usrobj_list = &usrobj->list; + PR_INIT_CLIST(usrobj_list); + + /* Allocate rest of the UserCacheObj and put them in the link list */ + for(i = 0; i < num_usrobj; i++){ + usrobj = (UserCacheObj *)pool_malloc(usrcache_pool, + sizeof(UserCacheObj)); + + if (!usrobj) return -1; + memset((void *)usrobj, 0, sizeof(UserCacheObj)); + PR_INSERT_AFTER(&usrobj->list, usrobj_list); + } + + return (singleDbTable || databaseUserCacheTable) ? 0 : -1; +} + +/* If the user hash table exists in the databaseUserCacheTable then return it. + * Otherwise, create a new hash table, insert it in the databaseUserCacheTable + * and then return it. + */ +static int usr_cache_table_get (const char *dbname, PRHashTable **usrTable) +{ + PRHashTable *table; + + if (singleDbTable) { + *usrTable = singleDbTable; + return LAS_EVAL_TRUE; + } + + user_hash_crit_enter(); + + table = (PRHashTable *)PR_HashTableLookup(databaseUserCacheTable, + dbname); + + if (!table) { + /* create a new table and insert it in the databaseUserCacheTable */ + table = alloc_db2uid_table(); + + if (table) { + PR_HashTableAdd(databaseUserCacheTable, + pool_strdup(usrcache_pool, dbname), + table); + } + } + + *usrTable = table; + + user_hash_crit_exit(); + + return table ? LAS_EVAL_TRUE : LAS_EVAL_FAIL; +} + +int acl_usr_cache_insert (const char *uid, const char *dbname, + const char *userdn, const char *passwd, + const char *group, + const SECItem *derCert, const time_t time) +{ + PRHashTable *usrTable; + UserCacheObj *usrobj; + UserCacheObj key; + int rv; + + if (acl_usr_cache_lifetime <= 0) { + /* Caching is disabled */ + return LAS_EVAL_TRUE; + } + + rv = usr_cache_table_get (dbname, &usrTable); + + if (rv != LAS_EVAL_TRUE) return rv; + + user_hash_crit_enter(); + + key.uid = (char *)uid; + key.derCert = (SECItem *)derCert; + + usrobj = (UserCacheObj *)PR_HashTableLookup(usrTable, &key); + + if (usrobj) { + time_t elapsed = time - usrobj->time; + int expired = (elapsed >= acl_usr_cache_lifetime); + + /* Free & reset the old values in usrobj if -- there is an old value + * and if the new value is given then it is different or the usrobj + * has expired */ + /* Set the field if the new value is given and the field is not set */ + /* If the usrobj has not expired then we only want to update the field + * whose new value is non-NULL and different */ + + /* Work on the 'uid' field */ + if (usrobj->uid && + (uid ? strcmp(usrobj->uid, uid) : expired)) + { + pool_free(usrcache_pool, usrobj->uid); + usrobj->uid = 0; + } + if (uid && !usrobj->uid) { + usrobj->uid = pool_strdup(usrcache_pool, uid); + } + + /* Work on the 'userdn' field */ + if (usrobj->userdn && + (userdn ? strcmp(usrobj->userdn, userdn) : expired)) + { + pool_free(usrcache_pool, usrobj->userdn); + usrobj->userdn = 0; + } + if (userdn && !usrobj->userdn) { + usrobj->userdn = pool_strdup(usrcache_pool, userdn); + } + + /* Work on the 'passwd' field */ + if (usrobj->passwd && + (passwd ? strcmp(usrobj->passwd, passwd) : expired)) + { + pool_free(usrcache_pool, usrobj->passwd); + usrobj->passwd = 0; + } + if (passwd && !usrobj->passwd) { + usrobj->passwd = pool_strdup(usrcache_pool, passwd); + } + + /* Work on the 'group' field -- not replace a valid group */ + if (!expired && usrobj->group && + (group ? strcmp(usrobj->group, group) : expired)) + { + pool_free(usrcache_pool, usrobj->group); + usrobj->group = 0; + } + if (group && !usrobj->group) { + usrobj->group = pool_strdup(usrcache_pool, group); + } + + /* Work on the 'derCert' field */ + if (usrobj->derCert && + (derCert ? (derCert->len != usrobj->derCert->len || + memcmp(usrobj->derCert->data, derCert->data, + derCert->len)) + : expired)) + { + SECITEM_FreeItem(usrobj->derCert, PR_TRUE); + usrobj->derCert = 0; + } + if (derCert && !usrobj->derCert) { + usrobj->derCert = SECITEM_DupItem((SECItem *)derCert); + } + + /* Reset the time only if the usrobj has expired */ + if (expired) { + DBG_PRINT1("Replace "); + usrobj->time = time; + } + else { + DBG_PRINT1("Update "); + } + } + else { + /* Get the last usrobj from the link list, erase it and use it */ + /* Maybe the last usrobj is not invalid yet but we don't want to grow + * the list of usrobjs. The last obj is the best candidate for being + * not valid. We don't want to compare the time -- just use it. + */ + PRCList *tail = PR_LIST_TAIL(usrobj_list); + usrobj = USEROBJ_PTR(tail); + + /* If the removed usrobj is in the hashtable, remove it from there */ + if (usrobj->hashtable) { + PR_HashTableRemove(usrobj->hashtable, usrobj); + } + + /* Free the memory associated with the usrobj */ + if (usrobj->userdn) pool_free(usrcache_pool, usrobj->userdn); + if (usrobj->passwd) pool_free(usrcache_pool, usrobj->passwd); + if (usrobj->group) pool_free(usrcache_pool, usrobj->group); + if (usrobj->derCert) SECITEM_FreeItem(usrobj->derCert, PR_TRUE); + if (usrobj->uid) pool_free(usrcache_pool, usrobj->uid); + + /* Fill in the usrobj with the current data */ + usrobj->uid = pool_strdup(usrcache_pool, uid); + usrobj->userdn = userdn ? pool_strdup(usrcache_pool, userdn) : 0; + usrobj->passwd = passwd ? pool_strdup(usrcache_pool, passwd) : 0; + usrobj->derCert = derCert ? SECITEM_DupItem((SECItem *)derCert) : 0; + usrobj->group = group ? pool_strdup(usrcache_pool, group) : 0; + usrobj->time = time; + + /* Add the usrobj to the user hash table */ + PR_HashTableAdd(usrTable, usrobj, usrobj); + usrobj->hashtable = usrTable; + DBG_PRINT1("Insert "); + } + + /* Move the usrobj to the head of the list */ + PR_REMOVE_LINK(&usrobj->list); + PR_INSERT_AFTER(&usrobj->list, usrobj_list); + + /* Set the time in the UserCacheObj */ + if (usrobj) { + rv = LAS_EVAL_TRUE; + } + else { + rv = LAS_EVAL_FAIL; + } + + DBG_PRINT4("acl_usr_cache_insert: derCert = \"%s\" uid = \"%s\" at time = %ld\n", + usrobj->derCert ? (char *)usrobj->derCert->data : "<NONE>", + uid, time); + + user_hash_crit_exit(); + return rv; +} + +static int acl_usr_cache_get_usrobj (const char *uid, const SECItem *derCert, + const char *dbname, const time_t time, + UserCacheObj **usrobj_out) +{ + PRHashTable *usrtable; + UserCacheObj *usrobj; + UserCacheObj key; + time_t elapsed; + int rv; + + *usrobj_out = 0; + + if (acl_usr_cache_lifetime <= 0) { + /* Caching is disabled */ + return LAS_EVAL_FALSE; + } + + rv = usr_cache_table_get(dbname, &usrtable); + if (!usrtable) return LAS_EVAL_FALSE; + + key.uid = (char *)uid; + key.derCert = (SECItem *)derCert; + + usrobj = (UserCacheObj *)PR_HashTableLookup(usrtable, &key); + + if (!usrobj) return LAS_EVAL_FALSE; + + rv = LAS_EVAL_FALSE; + + elapsed = time - usrobj->time; + + /* If the cache is valid, return the usrobj */ + if (elapsed < acl_usr_cache_lifetime) { + rv = LAS_EVAL_TRUE; + *usrobj_out = usrobj; + DBG_PRINT4("usr_cache found: derCert = \"%s\" uid = \"%s\" at time = %ld\n", + usrobj->derCert ? (char *)usrobj->derCert->data : "<NONE>", + usrobj->uid, time); + } + else { + DBG_PRINT4("usr_cache expired: derCert = \"%s\" uid = \"%s\" at time = %ld\n", + usrobj->derCert ? (char *)usrobj->derCert->data : "<NONE>", + usrobj->uid, time); + } + + return rv; +} + +int acl_usr_cache_passwd_check (const char *uid, const char *dbname, + const char *passwd, + const time_t time, char **dn, + pool_handle_t *pool) +{ + UserCacheObj *usrobj; + int rv; + + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj->passwd && passwd && + !strcmp(usrobj->passwd, passwd)) + { + /* extract dn from the usrobj */ + *dn = usrobj->userdn ? pool_strdup(pool, usrobj->userdn) : 0; + rv = LAS_EVAL_TRUE; + DBG_PRINT1("Success "); + } + else { + rv = LAS_EVAL_FALSE; + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_passwd_check: uid = \"%s\" at time = %ld\n", + uid, time); + user_hash_crit_exit(); + + return rv; +} + + +static int group_check_helper (UserCacheObj *usrobj, void *val) +{ + if (usrobj->group && !strcmp(usrobj->group, (char *)val)) + return LAS_EVAL_TRUE; + else + return LAS_EVAL_FALSE; +} + +int acl_usr_cache_group_check (const char *uid, const char *dbname, + const char *group, const time_t time) +{ + UserCacheObj *usrobj; + int rv; + + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj->group && group && + !strcmp(usrobj->group, group)) + { + DBG_PRINT1("Success "); + } + else { + rv = LAS_EVAL_FALSE; + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_group_check: uid = \"%s\" group = \"%s\"\n", + uid, group ? group : "<NONE>"); + user_hash_crit_exit(); + + return rv; +} + +int acl_usr_cache_group_len_check (const char *uid, const char *dbname, + const char *group, const int len, + const time_t time) +{ + UserCacheObj *usrobj; + int rv; + + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj->group && group && + !strncmp(usrobj->group, group, len)) + { + rv = LAS_EVAL_TRUE; + DBG_PRINT1("Success "); + } + else { + rv = LAS_EVAL_FALSE; + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_group_check: uid = \"%s\" group = \"%s\"\n", + uid, group); + user_hash_crit_exit(); + + return rv; +} + + +int acl_usr_cache_get_userdn (const char *uid, const char *dbname, + const time_t time, char **userdn, + pool_handle_t *pool) +{ + UserCacheObj *usrobj; + int rv; + + *userdn = 0; + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE) { + *userdn = usrobj->userdn ? pool_strdup(pool, usrobj->userdn) : 0; + DBG_PRINT1("Success "); + } + else { + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_get_userdn: uid = \"%s\" userdn = \"%s\"\n", + uid, *userdn ? *userdn : "<NONE>"); + user_hash_crit_exit(); + + return *userdn ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; +} + +int acl_usr_cache_userdn_check (const char *uid, const char *dbname, + const char *userdn, const time_t time) +{ + UserCacheObj *usrobj; + int rv; + + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj->userdn && userdn && + !strcmp(usrobj->userdn, userdn)) + { + DBG_PRINT1("Success "); + } + else { + rv = LAS_EVAL_FALSE; + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_userdn_check: uid = \"%s\" userdn = \"%s\"\n", + uid, userdn ? userdn : "<NONE>"); + user_hash_crit_exit(); + + return rv; +} + +int acl_usr_cache_set_userdn (const char *uid, const char *dbname, + const char *userdn, const time_t time) +{ + int rv; + + /* acl_usr_cache_insert updates the existing un-expired entry or creates a + * new one */ + rv = acl_usr_cache_insert(uid, dbname, userdn, 0, 0, 0, time); + + return rv; +} + +int acl_usr_cache_get_group (const char *uid, const char *dbname, + const time_t time, char **group, + pool_handle_t *pool) +{ + UserCacheObj *usrobj; + int rv; + + *group = 0; + user_hash_crit_enter(); + rv = acl_usr_cache_get_usrobj(uid, 0, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE) { + *group = usrobj->group ? pool_strdup(pool, usrobj->group) : 0; + DBG_PRINT1("Success "); + } + else { + DBG_PRINT1("Failed "); + } + + DBG_PRINT3("acl_usr_cache_get_group: uid = \"%s\" group = \"%s\"\n", + uid, *group ? *group : "<NONE>"); + user_hash_crit_exit(); + + return *group ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; +} + +int acl_usr_cache_set_group (const char *uid, const char *dbname, + const char *group, const time_t time) +{ + int rv; + + /* acl_usr_cache_insert updates the existing un-expired entry or creates a + * new one */ + rv = acl_usr_cache_insert(uid, dbname, 0, 0, group, 0, time); + + return rv; +} + +int acl_cert_cache_insert (void *cert_in, const char *dbname, + const char *uid, const char *dn, + const time_t time) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + SECItem derCert = cert->derCert; + int rv; + + rv = acl_usr_cache_insert(uid, dbname, dn, 0, 0, &derCert, time); + + return rv; +} + +/* Returns LAS_EVAL_TRUE if the user's cache is valid and returns uid */ +int acl_cert_cache_get_uid (void *cert_in, const char *dbname, + const time_t time, char **uid, char **dn, + pool_handle_t *pool) +{ + CERTCertificate *cert = (CERTCertificate *)cert_in; + SECItem derCert = cert->derCert; + UserCacheObj *usrobj = 0; + int rv; + + rv = acl_usr_cache_get_usrobj(0, &derCert, dbname, time, &usrobj); + + if (rv == LAS_EVAL_TRUE && usrobj && usrobj->uid) { + *uid = pool_strdup(pool, usrobj->uid); + *dn = usrobj->userdn ? pool_strdup(pool, usrobj->userdn) : 0; + } + else { + *uid = 0; + *dn = 0; + rv = LAS_EVAL_FALSE; + } + + return rv; +} + diff --git a/lib/libaccess/utest.mk b/lib/libaccess/utest.mk new file mode 100644 index 00000000..f33e6e6d --- /dev/null +++ b/lib/libaccess/utest.mk @@ -0,0 +1,61 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# + +#CFLAGS = -g -DDEBUG -I. +CFLAGS = -g -I. -I../../../include $(TESTFLAGS) +#LEX = flex +CC=gcc + +HEAD = aclparse.h acltools.h lparse.h acl.h acleval.h lasdns.h lasip.h mthash.h stubs.h aclscan.h acl.tab.h +XSRC = aclparse.y aclscan.l +CSRC = acleval.c aclutil.c lasdns.c lasip.c lastod.c mthash.c testmain.c acltools.c space.c acl.tab.c acl.yy.c +SRC = $(HEAD) $(XSRC) $(CSRC) + +XOBJ = acl.tab.o acl.yy.o testmain.o acltools.o +COBJ = $(CSRC:%.c=%.o) +OBJ = $(XOBJ) $(COBJ) + +always: $(OBJ) + +acleval.o: stubs.h aclparse.h acl.h acleval.h mthash.h + +aclutil.o: acl.h aclparse.h + +lasdns.o: acl.h aclparse.h lasdns.h mthash.h + +lasip.o: acl.h aclparse.h lasip.h + +lastod.o: acl.h aclparse.h + +acltools.o: aclparse.h aclscan.h lparse.h aclparse.y + +testmain.o: aclparse.h acltools.h + +acl.yy.o: acl.yy.c acl.tab.h + +acl.yy.o acl.tab.o acltools.o: aclparse.h acltools.h lparse.h + +yacc: aclparse.y + $(YACC) -dv aclparse.y + mv y.tab.h acl.tab.h + mv y.tab.c acl.tab.c +#sed -f yy-sed y.tab.h > acl.tab.h +#sed -f yy-sed y.tab.c > acl.tab.c + +# Should only run this on an SGI, where flex() is present +flex: aclscan.l + $(LEX) aclscan.l + mv lex.yy.c acl.yy.c +#sed -f yy-sed lex.yy.c > acl.yy.c + +clean: + rm -f aclparse aclparse.pure y.output acl.tab.c acl.tab.h acl.yy.c lex.yy.c y.tab.c y.tab.h aclparse.c $(OBJ) + +# Check it out from the RCS directory +$(SRC): RCS/$$@,v + co $@ diff --git a/lib/libaccess/utest/.purify b/lib/libaccess/utest/.purify new file mode 100644 index 00000000..56b9983e --- /dev/null +++ b/lib/libaccess/utest/.purify @@ -0,0 +1,19 @@ +suppress umr process_gethost +suppress umr _door_gethostbyname_r +suppress umr _get_hostserv_inetnetdir_byname +suppress umr _get_hostserv_inetnetdir_byaddr +suppress umr gethostbyname_r +suppress umr _nsc_trydoorcall +suppress umr LASDnsBuild +suppress umr PR_HashString +suppress umr mthsearch +suppress umr Hash +suppress umr strcmp +suppress umr mthsearch +suppress umr strlen +suppress umr strdup +suppress umr strcpy +suppress umr PListFindValue +suppress umr LASIpEval +suppress umr LASDnsEval +suppress mlk system_strdup_perm diff --git a/lib/libaccess/utest/Makefile b/lib/libaccess/utest/Makefile new file mode 100644 index 00000000..2acedff0 --- /dev/null +++ b/lib/libaccess/utest/Makefile @@ -0,0 +1,119 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +# +# +MCOM_ROOT=../../../.. +MODULE=LibAcl +include ../../../nsdefs.mk + +OBJDEST=. +UTESTDEST=utest + +include ../../../nsconfig.mk + +MODULE_CFLAGS=-I$(NSROOT)/include/libaccess -I$(NSROOT)/include -I$(NSROOT)/include/public -I. -DACL_LIB_INTERNAL + +include $(INCLUDE_DEPENDS) + +#TESTFLAGS = -DUTEST -DDEBUG_LEVEL_2 +TESTFLAGS = -DUTEST +CC = $(CCC) + +CSRC = acltest.cpp onetest.cpp ustubs.cpp twotest.cpp +TSRC = aclfile0 aclfile1 aclfile2 aclfile3 aclfile4 aclfile5 aclfile6 aclfile7 aclfile8 aclfile9 aclfile10 aclfile11 aclfile12 aclfile13 aclfile14 aclfile15 aclfile16 aclfile17 aclfile18 aclfile19 test.ref +SRC = $(CSRC) $(TSRC) +XSRC = \ + ../oneeval.cpp \ + ../lastod.cpp \ + ../lasip.cpp \ + ../aclutil.cpp \ + ../lasdns.cpp \ + ../acl.tab.cpp \ + ../acl.yy.cpp \ + ../acltools.cpp \ + ../aclspace.cpp \ + ../lasgroup.cpp \ + ../lasuser.cpp \ + ../lasprogram.cpp \ + ../nseframe.cpp \ + ../aclcache.cpp \ + ../register.cpp \ + ../symbols.cpp \ + ../method.cpp \ + ../authdb.cpp + +COBJ = $(CSRC:%.cpp=%.o) +XOBJ = $(XSRC:../%.cpp=%.o) + +# This may be needed for other platforms too +ifeq ($(ARCH), IRIX) +XLIBS = -rpath $(LDAP_LIBPATH) +endif + +ifeq ($(ARCH), HPUX) +XLIBS = -lpthread +endif +ifeq ($(ARCH), SOLARIS) +XLIBS = -lsocket -lnsl -ldl -lposix4 +endif + +XLIBS+= $(OBJDIR)/lib/base/plist.o \ + $(OBJDIR)/lib/base/pool.o \ + $(OBJDIR)/lib/base/util.o \ + $(OBJDIR)/lib/base/ereport.o \ + $(OBJDIR)/lib/base/system.o \ + $(OBJDIR)/lib/base/shexp.o \ + $(OBJDIR)/lib/base/pblock.o \ + $(OBJDIR)/lib/base/file.o \ + $(OBJDIR)/lib/base/systhr.o \ + $(OBJDIR)/lib/base/nscperror.o \ + $(OBJDIR)/lib/libldapu.a \ + $(LIBNSPR) + +all: $(COBJ) $(TSRC) acltest + ./acltest > test.out + diff test.ref test.out + @echo + @echo "The unit test is passed if there is no diff output, and the" + @echo "Purify window shows no errors and 0 bytes leaked." + @echo + @echo "Run - gmake coverage - manually to get code coverage analysis." + @echo + +aclparse: ustubs.o testmain.o $(XOBJ) + purify $(CC) -o aclparse testmain.o $(XOBJ) ustubs.o $(XLIBS) + +aclparse.pure: acl.tab.o acl.yy.o testmain.o acltools.o ustubs.o + purify -user-path=.. $(CC) -o aclparse.pure $(XOBJ) ustubs.o $(XLIBS) + +onetest: onetest.o ustubs.o $(XOBJ) + $(CC) -o onetest onetest.o $(XOBJ) ustubs.o $(XLIBS) + +twotest: twotest.o ustubs.o $(XOBJ) + $(CC) -o twotest twotest.o $(XOBJ) ustubs.o $(XLIBS) + +acltest: acltest.o ustubs.o $(XOBJ) +# purify $(CC) -o acltest acltest.o $(XOBJ) ustubs.o $(XLIBS) + $(CC) -o acltest acltest.o $(XOBJ) ustubs.o $(XLIBS) + +coverage: acltest.o ustubs.o $(XOBJ) + purecov $(CC) -o acltestcov acltest.o $(XOBJ) ustubs.o $(XLIBS) + rm -f *.pcv + acltestcov + +lasemail: lasemail.o + $(LD) -G -h lasemail.so -o lasemail.so lasemail.o + +#$(XOBJ): $(XSRC) +# cd ..; gmake OBJDEST=$(UTESTDEST) CC=$(OCC) TESTFLAGS=$(TESTFLAGS) + +%.o:../%.c + $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) -I.. $< -o $(OBJDEST)/$*.o + +%.o:../%.cpp + $(CC) -c $(CFLAGS) $(TESTFLAGS) $(MCC_INCLUDE) -I.. $< -o $(OBJDEST)/$*.o diff --git a/lib/libaccess/utest/acl.dat b/lib/libaccess/utest/acl.dat new file mode 100644 index 00000000..d640adca --- /dev/null +++ b/lib/libaccess/utest/acl.dat @@ -0,0 +1,12 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; + +acl davids; + +deny (all) dns=aruba.mcom.com; diff --git a/lib/libaccess/utest/aclfile0 b/lib/libaccess/utest/aclfile0 new file mode 100644 index 00000000..8bb2a428 --- /dev/null +++ b/lib/libaccess/utest/aclfile0 @@ -0,0 +1,55 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile0; +deny with uri="test"; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); + +acl aclfile0.0; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.1; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.2; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.3; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.4; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.5; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.6; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.7; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.8; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.9; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.10; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); +acl aclfile0.11; +allow (read, write, execute) (timeofday<2100); +acl aclfile0.12; +authenticate (user, group) { + database=franco; + method=basic; +}; +allow (read, write, execute) (timeofday<2100); +allow (read, write, execute) (dayofweek!=sun or ip="255.255.255.255+*.*.*.*, 255.255.0.0+190.198.162.1"); diff --git a/lib/libaccess/utest/aclfile1 b/lib/libaccess/utest/aclfile1 new file mode 100644 index 00000000..e148f1a1 --- /dev/null +++ b/lib/libaccess/utest/aclfile1 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile1; +deny (read, write, execute) (timeofday<2100); +deny (read, write, execute) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile10 b/lib/libaccess/utest/aclfile10 new file mode 100644 index 00000000..f0f5a223 --- /dev/null +++ b/lib/libaccess/utest/aclfile10 @@ -0,0 +1,13 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile10; +deny absolute (read) ip="17.34.*"; +allow (read,write) timeofday>1700; +deny (read) dns="*.mcom.com"; +allow (read,write) dayofweek=mon; diff --git a/lib/libaccess/utest/aclfile11 b/lib/libaccess/utest/aclfile11 new file mode 100644 index 00000000..9fe73cb2 --- /dev/null +++ b/lib/libaccess/utest/aclfile11 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile11; +allow (read) (timeofday<2100); +allow (html_write, execute) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile12 b/lib/libaccess/utest/aclfile12 new file mode 100644 index 00000000..ac154d7a --- /dev/null +++ b/lib/libaccess/utest/aclfile12 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile12; +allow (read) (timeofday<2100); +allow (read, html_write, execute) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile13 b/lib/libaccess/utest/aclfile13 new file mode 100644 index 00000000..7334d03d --- /dev/null +++ b/lib/libaccess/utest/aclfile13 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile13; +allow (read) (ip="17.34.1.1+255.255.0.0"); +allow (html_write) (dns!="*.microsoft.com"); diff --git a/lib/libaccess/utest/aclfile14 b/lib/libaccess/utest/aclfile14 new file mode 100644 index 00000000..5fc5c706 --- /dev/null +++ b/lib/libaccess/utest/aclfile14 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile14; +allow (read, write) (ip="17.34.*"); +deny (write) (dns!="*.mcom.com"); diff --git a/lib/libaccess/utest/aclfile15 b/lib/libaccess/utest/aclfile15 new file mode 100644 index 00000000..2d8701ec --- /dev/null +++ b/lib/libaccess/utest/aclfile15 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile15; +allow (html_read, write) (dns="*.mcom.com"); +deny (read) (ip="17.34.*"); diff --git a/lib/libaccess/utest/aclfile16 b/lib/libaccess/utest/aclfile16 new file mode 100644 index 00000000..54ce99f8 --- /dev/null +++ b/lib/libaccess/utest/aclfile16 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile16; +allow (html_read, write) (dns="*.mcom.com"); +deny (read) (ip="17.34.1.1 + 255.255.0.0"); diff --git a/lib/libaccess/utest/aclfile17 b/lib/libaccess/utest/aclfile17 new file mode 100644 index 00000000..128076f3 --- /dev/null +++ b/lib/libaccess/utest/aclfile17 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile17; +allow absolute (all) (dns="*.mcom.com"); +deny (read) (ip="17.34.1.1+255.255.0.0"); diff --git a/lib/libaccess/utest/aclfile18 b/lib/libaccess/utest/aclfile18 new file mode 100644 index 00000000..4a80bc27 --- /dev/null +++ b/lib/libaccess/utest/aclfile18 @@ -0,0 +1,19 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile18; +authenticate (user) { + method=SSL; + database=LDAP; +}; +allow (read, write, execute, create) dns="*.mcom.com"; +authenticate (user) { + method=basic; + database=20; +}; +allow (read, write, execute, create) (timeofday>1700 or timeofday<0800); diff --git a/lib/libaccess/utest/aclfile19 b/lib/libaccess/utest/aclfile19 new file mode 100644 index 00000000..4433f4c7 --- /dev/null +++ b/lib/libaccess/utest/aclfile19 @@ -0,0 +1,14 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile19A; +deny (read, write, execute, create) dns!="*.mcom.com"; +allow absolute (read) ((timeofday>1700 or timeofday<0800) or dayofweek=satsunmon); + +acl aclfile19B; +deny (write) dns="*.mcom.com"; diff --git a/lib/libaccess/utest/aclfile2 b/lib/libaccess/utest/aclfile2 new file mode 100644 index 00000000..eee5c30c --- /dev/null +++ b/lib/libaccess/utest/aclfile2 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile2; +deny (read) (timeofday<2100); +deny (read) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile3 b/lib/libaccess/utest/aclfile3 new file mode 100644 index 00000000..094c1abe --- /dev/null +++ b/lib/libaccess/utest/aclfile3 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile3; +allow (read) (timeofday<2100); +allow (read) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile4 b/lib/libaccess/utest/aclfile4 new file mode 100644 index 00000000..befc7b4b --- /dev/null +++ b/lib/libaccess/utest/aclfile4 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile4; +allow (read) (timeofday>0700); +allow (write) (dayofweek!=sun); diff --git a/lib/libaccess/utest/aclfile5 b/lib/libaccess/utest/aclfile5 new file mode 100644 index 00000000..8b0e1e8d --- /dev/null +++ b/lib/libaccess/utest/aclfile5 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile5; +allow (read) (ip="17.34.*"); +allow (write) (dns!="*.microsoft.com"); diff --git a/lib/libaccess/utest/aclfile6 b/lib/libaccess/utest/aclfile6 new file mode 100644 index 00000000..9646b548 --- /dev/null +++ b/lib/libaccess/utest/aclfile6 @@ -0,0 +1,23 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile6; +allow (read, +write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +allow (read, write) (ip="17.34.*"); +deny (write) +(dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); +deny (write) (dns!="*.mcom.com"); diff --git a/lib/libaccess/utest/aclfile7 b/lib/libaccess/utest/aclfile7 new file mode 100644 index 00000000..d8f9aa13 --- /dev/null +++ b/lib/libaccess/utest/aclfile7 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile7; +allow (read,write) (dns="*.mcom.com"); +deny (read) (ip="17.34.*"); diff --git a/lib/libaccess/utest/aclfile8 b/lib/libaccess/utest/aclfile8 new file mode 100644 index 00000000..b11cfe7e --- /dev/null +++ b/lib/libaccess/utest/aclfile8 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile8; +allow (read, write, execute, create) dns="*.mcom.com"; +allow (read, write, execute, create) (timeofday>1700 or timeofday<0800); diff --git a/lib/libaccess/utest/aclfile9 b/lib/libaccess/utest/aclfile9 new file mode 100644 index 00000000..2a0ab35e --- /dev/null +++ b/lib/libaccess/utest/aclfile9 @@ -0,0 +1,11 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclfile9; +deny (read) ip="*.34.*+*.128.*.0"; +allow (read, write, execute, create) ((timeofday>1700 or timeofday<0800) or dayofweek=satsunmon); diff --git a/lib/libaccess/utest/aclgrp0 b/lib/libaccess/utest/aclgrp0 new file mode 100644 index 00000000..ba08fbf1 --- /dev/null +++ b/lib/libaccess/utest/aclgrp0 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp0; +allow (read, write, execute) (group = marketing or group!="Directory Administrators"); diff --git a/lib/libaccess/utest/aclgrp1 b/lib/libaccess/utest/aclgrp1 new file mode 100644 index 00000000..7a804404 --- /dev/null +++ b/lib/libaccess/utest/aclgrp1 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp1; +allow (read, write, execute) (group!="Directory Administrators"); diff --git a/lib/libaccess/utest/aclgrp2 b/lib/libaccess/utest/aclgrp2 new file mode 100644 index 00000000..13938c19 --- /dev/null +++ b/lib/libaccess/utest/aclgrp2 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp2; +allow (read, write, execute) (group=marketing); diff --git a/lib/libaccess/utest/aclgrp3 b/lib/libaccess/utest/aclgrp3 new file mode 100644 index 00000000..30b44d5c --- /dev/null +++ b/lib/libaccess/utest/aclgrp3 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp3; +allow (read, write, execute) (group>"Directory Admin,marketing"); diff --git a/lib/libaccess/utest/aclgrp4 b/lib/libaccess/utest/aclgrp4 new file mode 100644 index 00000000..5b07d6c2 --- /dev/null +++ b/lib/libaccess/utest/aclgrp4 @@ -0,0 +1,10 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +version 3.0; +acl aclgrp4; +allow (read, write, execute) (group = " marketing ,, Directory Administrators ,, "); diff --git a/lib/libaccess/utest/acltest.cpp b/lib/libaccess/utest/acltest.cpp new file mode 100644 index 00000000..c643f873 --- /dev/null +++ b/lib/libaccess/utest/acltest.cpp @@ -0,0 +1,796 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <netsite.h> +#include <base/session.h> +#include <base/daemon.h> +#include <base/systhr.h> +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> +#include "../aclcache.h" +#include <libaccess/las.h> + + +extern ACLListHandle_t *ACL_ParseFile(NSErr_t *errp, char *filename); + +int +TestEvalFunc(NSErr_t *errp, char *attr, CmpOp_t comparator, + char *attr_pattern, ACLCachable_t *cachable, + void **las_cookie, PList_t subject, PList_t resource, + PList_t auth_info, PList_t global_auth) +{ + return 0; +} + +void +TestFlushFunc(void **cookie) +{ + return; +} + +static int parse_dburl (NSErr_t *errp, ACLDbType_t dbtype, + const char *dbname, const char *url, + PList_t plist, void **db) +{ + *db = strdup(url); + return 0; +} + + +main() +{ + ACLListHandle_t *acl_list; + int result; + ACLCachable_t cachable = 0; + void *las_cookie=NULL; + ACLEvalHandle_t eval; + char *rights[3]; + char filename[20]; + char newfilename[25]; + int i; + char *map_generic[7]; + LASEvalFunc_t Eval_funcp; + LASFlushFunc_t Flush_funcp; + char *bong; + char *bong_type; + char *acl_tag; + int expr_num; + int ii; + char **name_list; + ACLMethod_t method=NULL; + ACLDbType_t dbtype=NULL; + int rv; + ACLAttrGetterList_t aglist; + ACLAttrGetter_t *agptr; + char **names; + int cnt; + + systhread_init("acl_utest"); + + char *acl_file_list[3] = {"aclfile6", "aclfile7", NULL}; + char *new_filename = "merge6_7"; + char *acl_name_list[3] = {"aclfile6", "aclfile7", NULL}; + char *new_aclname = "merge6_7"; + char *bad_acl_file_list[3] = {"bad_aclfile6", "bad_aclfile7", NULL}; + + if ( ACL_FileMergeFile(NULL, new_filename, bad_acl_file_list, 0) < 0 ) { + printf("Failed ACL_FileMergeFile() test.\n"); + } + + if ( ACL_FileMergeFile(NULL, new_filename, acl_file_list, 0) < 0 ) { + printf("Failed ACL_FileMergeFile() test.\n"); + } + + if ( ACL_FileMergeAcl(NULL, new_filename, acl_name_list, new_aclname, 0) < 0 ) { + printf("Failed ACL_FileMergeAcl() test.\n"); + } + + /* LAS Registration Unit Tests */ + + ACL_Init(); + + rv = ACL_MethodRegister(NULL, "one", &method); + printf("Method one is #%d, rv=%d\n", (int)method, rv); + + rv = ACL_MethodRegister(NULL, "two", &method); + printf("Method two is #%d, rv=%d\n", (int)method, rv); + + rv = ACL_MethodRegister(NULL, "one", &method); + printf("Method one repeated is #%d, rv=%d\n", (int)method, rv); + + rv = ACL_MethodRegister(NULL, "three", &method); + printf("Method three is #%d, rv=%d\n", (int)method, rv); + + rv = ACL_MethodNamesGet(NULL, &names, &cnt); + + for(i = 0; i < cnt; i++) { + printf("\tMethod[%d] = \"%s\"\n", i, names[i]); + } + + ACL_MethodNamesFree(NULL, names, cnt); + + if (!ACL_MethodIsEqual(NULL, method, method)) { + printf("Error comparing methods"); + } + + if (!ACL_MethodNameIsEqual(NULL, method, "three")) { + printf("Error comparing method by name"); + } + + /* Since LDAP is already registered by ACL_Init, the first number + * we'll get is actually 2. + */ + rv = ACL_DbTypeRegister(NULL, "two", parse_dburl, &dbtype); + printf("DbType two is #%d, rv=%d\n", (int)dbtype, rv); + + rv = ACL_DbTypeRegister(NULL, "three", parse_dburl, &dbtype); + printf("DbType three is #%d, rv=%d\n", (int)dbtype, rv); + + rv = ACL_DbTypeRegister(NULL, "two", parse_dburl, &dbtype); + printf("DbType two repeated is #%d, rv=%d\n", (int)dbtype, rv); + + rv = ACL_DbTypeRegister(NULL, "four", parse_dburl, &dbtype); + printf("DbType four is #%d, rv=%d\n", (int)dbtype, rv); + + if (!ACL_DbTypeIsEqual(NULL, dbtype, dbtype)) { + printf("Error comparing dbtypes\n"); + } + + if (!ACL_DbTypeNameIsEqual(NULL, dbtype, "four")) { + printf("Error comparing dbtype by name\n"); + } + + rv = ACL_DatabaseRegister(NULL, dbtype, "db1", "url for db1", NULL); + if (rv < 0) { + printf("ACL_DatabaseRegister failed for db1\n"); + } + + rv = ACL_DatabaseRegister(NULL, dbtype, "db2", "url for db2", NULL); + if (rv < 0) { + printf("ACL_DatabaseRegister failed for db2\n"); + } + + rv = ACL_DatabaseRegister(NULL, dbtype, "db3", "url for db3", NULL); + if (rv < 0) { + printf("ACL_DatabaseRegister failed for db3\n"); + } + + rv = ACL_DatabaseNamesGet(NULL, &names, &cnt); + + for(i = 0; i < cnt; i++) { + printf("\tDatabase[%d] = \"%s\"\n", i, names[i]); + } + + if (ACL_AttrGetterRegister(NULL, "attr", (ACLAttrGetterFn_t)2, (ACLMethod_t)10, (ACLDbType_t)20, ACL_AT_FRONT, NULL)) { + printf("Error registering attr getter\n"); + } + + if (ACL_AttrGetterRegister(NULL, "attr", (ACLAttrGetterFn_t)3, (ACLMethod_t)10, (ACLDbType_t)20, ACL_AT_END, NULL)) { + printf("Error registering attr getter\n"); + } + + if (ACL_AttrGetterRegister(NULL, "attr", (ACLAttrGetterFn_t)1, (ACLMethod_t)10, (ACLDbType_t)20, ACL_AT_FRONT, NULL)) { + printf("Error registering attr getter\n"); + } + + if (ACL_AttrGetterRegister(NULL, "attr", (ACLAttrGetterFn_t)4, (ACLMethod_t)10, (ACLDbType_t)20, ACL_AT_END, NULL)) { + printf("Error registering attr getter\n"); + } + + if (ACL_AttrGetterFind(NULL, "attr", &aglist)) { + printf("Error finding attr getter\n"); + } + + for (i = 0, agptr = ACL_AttrGetterFirst(&aglist); + i < 4; + i++, agptr = ACL_AttrGetterNext(&aglist, agptr)) { + + if (agptr) { + printf("position %d\n", (int)(agptr->fn)); + } + else { + printf("***Error: missing getter ***\n"); + } + } + +#ifndef XP_WIN32 + if (ACL_LasRegister(NULL, "test_attr", TestEvalFunc, TestFlushFunc)) { + printf("Error registering Test LAS functions\n"); + } + ACL_LasFindEval(NULL, "test_attr", &Eval_funcp); + if (Eval_funcp != TestEvalFunc) { + printf("Error finding Eval function - expecting %x, got %x\n", + TestEvalFunc, Eval_funcp); + } + ACL_LasFindFlush(NULL, "test_attr", &Flush_funcp); + if (Flush_funcp != TestFlushFunc) { + printf("Error finding Flush function - expecting %x, got %x\n", + TestFlushFunc, Flush_funcp); + } + ACL_LasFindEval(NULL, "wrong_attr", &Eval_funcp); + if (Eval_funcp != NULL) { + printf("Error finding Eval function - expecting NULL, got %x\n", + Eval_funcp); + } + ACL_LasFindFlush(NULL, "wrong_attr", &Flush_funcp); + if (Flush_funcp != NULL) { + printf("Error finding Flush function - expecting NULL, got %x\n", + Flush_funcp); + } +#endif /* !XP_WIN32 */ + + /* ACL Eval Unit Tests + */ + rights[0] = "http_get"; + rights[1] = "http_post"; + rights[2] = NULL; + + eval.subject = NULL; + eval.resource = NULL; + + for (i=0; i<10; i++) { + sprintf(filename, "aclfile%d", i); + eval.acllist = ACL_ParseFile((NSErr_t *)NULL, filename); + if ( eval.acllist == NULL ) { + printf("Couldn't parse.\n"); + continue; + } + + sprintf(newfilename, "%s.v30", filename); + if ( ACL_WriteFile(NULL, newfilename, eval.acllist) < 0) { + printf("Couldn't write %s.\n", newfilename); + } + result = ACL_EvalTestRights(NULL, &eval, &rights[0], + http_generic, &bong, &bong_type, &acl_tag, &expr_num); + ACL_ListDestroy(NULL, eval.acllist); + printf("%s = %d\n\n", filename, result); + } + +/******************************************************************** + + TEST #1 + + TEST ACL_ParseString() + TEST ACL_WriteFile() + TEST ACL_ParseFile() + TEST ACL_ListFind() + +*********************************************************************/ + acl_list = ACL_ParseString((NSErr_t *)NULL, + "version 3.0; acl > franco;"); + if ( acl_list != NULL ) { + ACL_ListDestroy(NULL, acl_list); + printf("Test #1a fails parsed invalid ACL\n"); + goto skip_test; + } + + acl_list = ACL_ParseString((NSErr_t *)NULL, + "version 3.0; acl franco; \nallow (read) user=franco;"); + if ( acl_list == NULL ) { + printf("Test #1b fails couldn't parse valid ACL\n"); + goto skip_test; + } else { + if ( ACL_WriteFile(NULL, "buffer", acl_list) < 0) { + printf("Test #1b, couldn't write %s.\n", "buffer"); + } + ACL_ListDestroy(NULL, acl_list); + } + + acl_list = ACL_ParseString((NSErr_t *)NULL, + "version 3.0; acl franco; \njunk (read) user=franco;"); + + if ( acl_list != NULL ) { + printf("Test #1c failed missed syntax error\n"); + ACL_ListDestroy(NULL, acl_list); + goto skip_test; + } + + acl_list = ACL_ParseString((NSErr_t *)NULL, + "version 3.0; acl franco; \nallow (read) user=franco;"); + + if ( acl_list == NULL ) { + printf("Test #1d couldn't parse valid ACL\n"); + } else { + ACL_ListDestroy(NULL, acl_list); + goto skip_test; + } + + acl_list= ACL_ParseFile((NSErr_t *)NULL, "buffer"); + if ( acl_list == NULL ) { + printf("Test #1e, couldn't perform ACL_ParseFile(buffer)\n"); + goto skip_test; + } else { + if ( ACL_ListFind(NULL, acl_list, "franco", ACL_CASE_INSENSITIVE) == NULL ) { + printf("Test #1e, couldn't find %s in %s.\n", "franco", "buffer"); + } + ACL_ListDestroy(NULL, acl_list); + } + +/******************************************************************** + + TEST #2 + + TEST ACL_FileDeleteAcl() + TEST ACL_ParseFile() + TEST ACL_ListFind() + +*********************************************************************/ + if ( ACL_FileDeleteAcl(NULL, "buffer", "franco", ACL_CASE_INSENSITIVE) < 0) { + printf("Test #2, couldn't write %s.\n", "buffer"); + } + acl_list= ACL_ParseFile((NSErr_t *)NULL, "buffer"); + if ( acl_list == NULL ) { + printf("Test #2, couldn't perform ACL_ParseFile(buffer)\n"); + goto skip_test; + } else { + if ( ACL_ListFind(NULL, acl_list, "franco", ACL_CASE_INSENSITIVE) ) { + printf("Couldn't delete %s from %s.\n", "franco", "buffer"); + } + ACL_ListDestroy(NULL, acl_list); + } + +/******************************************************************** + + TEST #3 + + TEST ACL_FileSetAcl() + TEST ACL_ParseFile() + TEST ACL_ListFind() + +*********************************************************************/ + if ( ACL_FileSetAcl(NULL, "buffer", + "version 3.0; acl FileSetAcl; \nallow (read) user=franco;", + ACL_CASE_INSENSITIVE)< 0) { + printf("Test #3, couldn't ACL_FileSetACL(%s).\n", "FileSetAcl"); + } + if ( ACL_FileSetAcl(NULL, "buffer", + "version 3.0; acl franco; \nallow (read) user=franco;", + ACL_CASE_INSENSITIVE)< 0) { + printf("Test #3, couldn't ACL_FileSetACL(%s).\n", "franco"); + } + acl_list= ACL_ParseFile((NSErr_t *)NULL, "buffer"); + if ( acl_list == NULL ) { + printf("Test #3, couldn't perform ACL_ParseFile(buffer)\n"); + goto skip_test; + } else { + if ( ACL_ListFind(NULL, acl_list, "franco", ACL_CASE_INSENSITIVE) == NULL) { + printf("Test #3, couldn't set %s in %s.\n", "franco", "buffer"); + } + if ( ACL_ListFind(NULL, acl_list, "filesetacl", ACL_CASE_INSENSITIVE) == NULL) { + printf("Test #3, couldn't set %s in %s.\n", "filesetacl", "buffer"); + } + ACL_ListDestroy(NULL, acl_list); + } + +/******************************************************************** + + TEST #4 + + TEST ACL_FileRenameAcl() + TEST ACL_ParseFile() + TEST ACL_ListFind() + +*********************************************************************/ + if ( ACL_FileRenameAcl(NULL, "buffer", "FileSetAcl", "loser", ACL_CASE_INSENSITIVE)< 0) { + printf("Test #4, fail ACL_FileRenameACL(filesetacl, loser).\n"); + } + if ( ACL_FileRenameAcl(NULL, "buffer", "franco", "bigdogs", + ACL_CASE_INSENSITIVE)< 0) { + printf("Test #4, fail ACL_FileRenameACL(franco, bigdogs).\n"); + } + acl_list= ACL_ParseFile((NSErr_t *)NULL, "buffer"); + if ( acl_list == NULL ) { + printf("Test #3, couldn't perform ACL_ParseFile(buffer)\n"); + goto skip_test; + } else { + if ( ACL_ListFind(NULL, acl_list, "loser", ACL_CASE_INSENSITIVE) == NULL) { + printf("Test #4, fail rename %s in %s.\n", "loser", "buffer"); + } + if ( ACL_ListFind(NULL, acl_list, "bigdogs", ACL_CASE_INSENSITIVE) == NULL) { + printf("Test #4, fail rename %s in %s.\n", "bigdogs", "buffer"); + } + if ( ACL_ListGetNameList(NULL, acl_list, &name_list) < 0 ) { + printf("Test #4, yikes, the GetNameList failed.\n"); + } else { + for (ii = 0; name_list[ii]; ii++) + printf("ACL %s\n", name_list[ii]); + ACL_NameListDestroy(NULL, name_list); + } + ACL_ListDestroy(NULL, acl_list); + } + + + + +skip_test: +/******************************************************************** + + END + +*********************************************************************/ + + rights[0] = "html_read"; + rights[1] = "html_write"; + + map_generic[0] = "html_read"; + map_generic[1] = "html_write"; + map_generic[2] = "N/A"; + map_generic[3] = "html_create"; + map_generic[4] = "html_delete"; + map_generic[5] = "N/A"; + map_generic[6] = NULL; + + for (i=10; i<20; i++) { + sprintf(filename, "aclfile%d", i); + eval.acllist = ACL_ParseFile((NSErr_t *)NULL, filename); + if ( eval.acllist == NULL ) { + printf("Parse failed.\n"); + continue; + } + result = ACL_EvalTestRights(NULL, &eval, &rights[0], map_generic, &bong, &bong_type, &acl_tag, &expr_num); + ACL_ListDestroy(NULL, eval.acllist); + printf("%s = %d\n\n", filename, result); + } + + /* + * Program LAS Unit Tests + */ + char *groups[32] = { + "http-foo", + "http-bar", + "http-grog", + NULL + }; + char *programs[32] = { + "foo, fubar, frobozz", + "bar, shoo, fly", + "grog, beer", + NULL + }; + struct program_groups program_groups; + program_groups.groups = groups; + program_groups.programs = programs; + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo, http-bar,http-grog", &cachable, &las_cookie, (PList_t)"foo", (PList_t)&program_groups, NULL, NULL); + printf("program = foo %d\n\n", result); + + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo, http-bar,http-grog", &cachable, &las_cookie, (PList_t)"nomatch", (PList_t)&program_groups, NULL, NULL); + printf("program = nomatch %d\n\n", result); + + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo, http-bar,http-grog", &cachable, &las_cookie, (PList_t)"beer", (PList_t)&program_groups, NULL, NULL); + printf("program = beer %d\n\n", result); + + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo, http-bar, http-grog", &cachable, &las_cookie, (PList_t)"http-grog", (PList_t)&program_groups, NULL, NULL); + printf("program = http-grog %d\n\n", result); + + result = LASProgramEval(NULL, "program", CMP_OP_EQ, "http-foo", &cachable, &las_cookie, (PList_t)"ubar", (PList_t)&program_groups, NULL, NULL); + printf("program = ubar %d\n\n", result); + + + /* + * DNS LAS Unit Tests + */ + + result = LASDnsEval(NULL, "dnsalias", CMP_OP_EQ, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dnsalias = *? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dnsalias", CMP_OP_EQ, "aruba.mcom.com brain251.mcom.com", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dnsalias = aruba.mcom.com brain251.mcom.com? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = *? %d\n\n", result); + + result = LASDnsEval(NULL, "dns", CMP_OP_NE, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns != *? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "aruba.mcom.com", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = aruba.mcom.com? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "ai.mit.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = ai.mit.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "*.ai.mit.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = *.ai.mit.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "*.mit.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = *.mit.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_EQ, "*.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns = *.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_NE, "*.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns != *.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "mistake", CMP_OP_NE, "*.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("mistake != *.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + result = LASDnsEval(NULL, "dns", CMP_OP_GT, "*.edu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("dns > *.edu? %d\n\n", result); + + LASDnsFlush(&las_cookie); + + + /* + * IP LAS Unit Tests + */ + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = *? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_NE, "*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip != *? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "*.*.*.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = *.*.*.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.*.*.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.*.*.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.*.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.*.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.*", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.*? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.*+255.255.255.255", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.*+255.255.255.255? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.69+255.255.255.254, 123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.69+255.255.255.254, 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_NE, "17.34.51.69+255.255.255.254, 123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip != 17.34.51.69+255.255.255.254, 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.68, 17.34.51.69", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.68, 17.34.51.69? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.68, 17.34.51.69, 123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.68, 17.34.51.69, 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_NE, "17.34.51.68, 17.34.51.69, 123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip != 17.34.51.68, 17.34.51.69, 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.68", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.68? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.69", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.69? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.51.69+255.255.255.254", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.51.69+255.255.255.254? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.34.50.69+255.255.254.0", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.34.50.69+255.255.254.0? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "17.35.50.69+255.254.0.0", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 17.35.50.69+255.254.0.0? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "16.35.50.69+254.0.0.0", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 16.35.50.69+254.0.0.0? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "ip", CMP_OP_EQ, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip = 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_NE, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip != 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_GT, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip > 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_LT, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip < 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_GE, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip >= 123.45.67.89? %d\n\n", result); + + result = LASIpEval(NULL, "ip", CMP_OP_LE, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("ip <= 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + result = LASIpEval(NULL, "mistake", CMP_OP_LE, "123.45.67.89", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf ("mistake <= 123.45.67.89? %d\n\n", result); + + LASIpFlush(&las_cookie); + + + /* + * Time of Day unit tests. + */ + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GT, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time > 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LT, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time < 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GT, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time > 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LT, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time < 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GT, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time > 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LT, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time < 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GE, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time >= 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LE, "2120", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time <= 2120? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GE, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time >= 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LE, "0700", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time <= 0700? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_GE, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time >= 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LE, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time <= 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "mistake", CMP_OP_LE, "2400", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("mistake <= 2400? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "0800-2200", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 0800-2200? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "0800-2200", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 0800-2200? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_EQ, "2200-0800", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time = 2200-0800? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_NE, "2200-0800", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time != 2200-0800? %d\n\n", result); + + result = LASTimeOfDayEval(NULL, "timeofday", CMP_OP_LE, "2200-0800", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("time <= 2200-0800? %d\n\n", result); + + + /* + * Day Of Week Unit Tests + */ + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "Mon", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= mon? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "tUe", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= tUe? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "weD", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= weD? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "THu", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= THu? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "FrI", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= FrI? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "sAT", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= tUe? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_EQ, "mon,tuewed,thu,frisatsun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("= mon,tuewed,thu,frisatsun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_NE, "mon,tuewed,thu,frisatsun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("!= mon,tuewed,thu,frisatsun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_GT, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("> Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_LT, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("< Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_GE, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf(">= Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "dayofweek", CMP_OP_LE, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("<= Sun? %d\n\n", result); + + result = LASDayOfWeekEval(NULL, "mistake", CMP_OP_LE, "Sun", &cachable, &las_cookie, NULL, NULL, NULL, NULL); + printf("mistake <= Sun? %d\n\n", result); + + + ACL_Destroy(); + + exit(0); + +} diff --git a/lib/libaccess/utest/lasemail.cpp b/lib/libaccess/utest/lasemail.cpp new file mode 100644 index 00000000..469a315f --- /dev/null +++ b/lib/libaccess/utest/lasemail.cpp @@ -0,0 +1,180 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +/* lasemail.cpp + * This file contains the Email LAS code. + */ + +#include <ldap.h> +#include <nsacl/aclapi.h> + +#define ACL_ATTR_EMAIL "email" + +extern "C" { +extern int LASEmailEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, ACLCachable_t *cachable, void **LAS_cookie, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth); +extern void LASEmailFlush(void **las_cookie); +extern int LASEmailModuleInit(); +} + + +/* + * LASEmailEval + * INPUT + * attr_name The string "email" - in lower case. + * comparator CMP_OP_EQ or CMP_OP_NE only + * attr_pattern A comma-separated list of emails + * (we currently support only one e-mail addr) + * *cachable Always set to ACL_NOT_CACHABLE. + * subject Subject property list + * resource Resource property list + * auth_info Authentication info, if any + * RETURNS + * retcode The usual LAS return codes. + */ +int LASEmailEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, + char *attr_pattern, ACLCachable_t *cachable, + void **LAS_cookie, PList_t subject, PList_t resource, + PList_t auth_info, PList_t global_auth) +{ + char *uid; + char *email; + int rv; + LDAP *ld; + char *basedn; + LDAPMessage *res; + int numEntries; + char filter[1024]; + int matched; + + *cachable = ACL_NOT_CACHABLE; + *LAS_cookie = (void *)0; + + if (strcmp(attr_name, ACL_ATTR_EMAIL) != 0) { + fprintf(stderr, "LASEmailEval called for incorrect attr \"%s\"\n", + attr_name); + return LAS_EVAL_INVALID; + } + + if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + fprintf(stderr, "LASEmailEval called with incorrect comparator %d\n", + comparator); + return LAS_EVAL_INVALID; + } + + if (!strcmp(attr_pattern, "anyone")) { + *cachable = ACL_INDEF_CACHABLE; + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + /* get the authenticated user name */ + rv = ACL_GetAttribute(errp, ACL_ATTR_USER, (void **)&uid, + subject, resource, auth_info, global_auth); + + if (rv != LAS_EVAL_TRUE) { + return rv; + } + + /* We have an authenticated user */ + if (!strcmp(attr_pattern, "all")) { + return comparator == CMP_OP_EQ ? LAS_EVAL_TRUE : LAS_EVAL_FALSE; + } + + /* do an ldap lookup for: (& (uid=<user>) (mail=<email>)) */ + rv = ACL_LDAPDatabaseHandle(errp, NULL, &ld, &basedn); + + if (rv != LAS_EVAL_TRUE) { + fprintf(stderr, "unable to get LDAP handle\n"); + return rv; + } + + /* Formulate the filter -- assume single e-mail in attr_pattern */ + /* If we support multiple comma separated e-mail addresses in the + * attr_pattern then the filter will look like: + * (& (uid=<user>) (| (mail=<email1>) (mail=<email2>))) + */ + sprintf(filter, "(& (uid=%s) (mail=%s))", uid, attr_pattern); + + rv = ldap_search_s(ld, basedn, LDAP_SCOPE_SUBTREE, filter, + 0, 0, &res); + + if (rv != LDAP_SUCCESS) + { + fprintf(stderr, "ldap_search_s: %s\n", ldap_err2string(rv)); + return LAS_EVAL_FAIL; + } + + numEntries = ldap_count_entries(ld, res); + + if (numEntries == 1) { + /* success */ + LDAPMessage *entry = ldap_first_entry(ld, res); + char *dn = ldap_get_dn(ld, entry); + + fprintf(stderr, "ldap_search_s: Entry found. DN: \"%s\"\n", dn); + ldap_memfree(dn); + matched = 1; + } + else if (numEntries == 0) { + /* not found -- but not an error */ + fprintf(stderr, "ldap_search_s: Entry not found. Filter: \"%s\"\n", + filter); + matched = 0; + } + else if (numEntries > 0) { + /* Found more than one entry! */ + fprintf(stderr, "ldap_search_s: Found more than one entry. Filter: \"%s\"\n", + filter); + return LAS_EVAL_FAIL; + } + + if (comparator == CMP_OP_EQ) { + rv = (matched ? LAS_EVAL_TRUE : LAS_EVAL_FALSE); + } + else { + rv = (matched ? LAS_EVAL_FALSE : LAS_EVAL_TRUE); + } + + return rv; +} + + +/* LASEmailFlush + * Deallocates any memory previously allocated by the LAS + */ +void +LASEmailFlush(void **las_cookie) +{ + /* do nothing */ + return; +} + +/* LASEmailModuleInit -- + * Register the e-mail LAS. + * + * To load this functions in the web server, compile the file in + * "lasemail.so" and add the following lines to the + * <ServerRoot>/https-<name>/config/obj.conf file. Be sure to change the + * "lasemail.so" portion to the full pathname. E.g. /nshome/lib/lasemail.so. + * + * Init fn="load-modules" funcs="LASEmailModuleInit" shlib="lasemail.so" + * Init fn="acl-register-module" module="lasemail" func="LASEmailModuleInit" + */ +int LASEmailModuleInit () +{ + NSErr_t err = NSERRINIT; + NSErr_t *errp = &err; + int rv; + + rv = ACL_LasRegister(errp, ACL_ATTR_EMAIL, LASEmailEval, LASEmailFlush); + + if (rv < 0) { + fprintf(stderr, "ACL_LasRegister failed. Error: %d\n", rv); + return rv; + } + + return rv; +} + diff --git a/lib/libaccess/utest/onetest.cpp b/lib/libaccess/utest/onetest.cpp new file mode 100644 index 00000000..3bcccbb1 --- /dev/null +++ b/lib/libaccess/utest/onetest.cpp @@ -0,0 +1,47 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <netsite.h> +#include <libaccess/nserror.h> +#include <base/session.h> +#include <libaccess/acl.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> +#include <base/plist.h> +#include <base/ereport.h> + +extern ACLListHandle_t *ACL_ParseFile(NSErr_t *errp, char *filename); +extern ACLEvalDestroyContext(NSErr_t *errp, ACLEvalHandle_t *acleval); + + +main(int arc, char **argv) +{ + int result; + int cachable; + void *las_cookie=NULL; + ACLEvalHandle_t eval; + char *rights[2]; + char filename[20]; + int i; + char *bong; + char *bong_type; + char *acl_tag; + int expr_num; + + /* ACL Eval Unit Tests + */ + rights[0] = "read"; + rights[1] = "write"; + rights[2] = NULL; + + eval.acllist = ACL_ParseFile((NSErr_t *)NULL, argv[1]); + result = ACL_EvalTestRights(NULL, &eval, &rights[0], NULL, &bong, &bong_type, &acl_tag, &expr_num); + ACLEvalDestroyContext(NULL, &eval); + ACL_ListDestroy(NULL, eval.acllist); + printf("%s = %d\n\n", argv[1], result); + +} diff --git a/lib/libaccess/utest/shexp.cpp b/lib/libaccess/utest/shexp.cpp new file mode 100644 index 00000000..23e9e909 --- /dev/null +++ b/lib/libaccess/utest/shexp.cpp @@ -0,0 +1,294 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * shexp.c: shell-like wildcard match routines + * + * + * See shexp.h for public documentation. + * + * Rob McCool + * + */ + +#include "shexp.h" +#include <ctype.h> /* isalpha, tolower */ + + +/* ----------------------------- shexp_valid ------------------------------ */ + + +int valid_subexp(char *exp, char stop) +{ + register int x,y,t; + int nsc,np,tld; + + x=0;nsc=0;tld=0; + + while(exp[x] && (exp[x] != stop)) { + switch(exp[x]) { + case '~': + if(tld) return INVALID_SXP; + else ++tld; + case '*': + case '?': + case '^': + case '$': + ++nsc; + break; + case '[': + ++nsc; + if((!exp[++x]) || (exp[x] == ']')) + return INVALID_SXP; + for(++x;exp[x] && (exp[x] != ']');++x) + if(exp[x] == '\\') + if(!exp[++x]) + return INVALID_SXP; + if(!exp[x]) + return INVALID_SXP; + break; + case '(': + ++nsc;np = 0; + while(1) { + if(exp[++x] == ')') + return INVALID_SXP; + for(y=x;(exp[y]) && (exp[y] != '|') && (exp[y] != ')');++y) + if(exp[y] == '\\') + if(!exp[++y]) + return INVALID_SXP; + if(!exp[y]) + return INVALID_SXP; + if(exp[y] == '|') + ++np; + t = valid_subexp(&exp[x],exp[y]); + if(t == INVALID_SXP) + return INVALID_SXP; + x+=t; + if(exp[x] == ')') { + if(!np) + return INVALID_SXP; + break; + } + } + break; + case ')': + case ']': + return INVALID_SXP; + case '\\': + if(!exp[++x]) + return INVALID_SXP; + default: + break; + } + ++x; + } + if((!stop) && (!nsc)) + return NON_SXP; + return ((exp[x] == stop) ? x : INVALID_SXP); +} + +NSAPI_PUBLIC int shexp_valid(char *exp) { + int x; + + x = valid_subexp(exp, '\0'); + return (x < 0 ? x : VALID_SXP); +} + + +/* ----------------------------- shexp_match ----------------------------- */ + + +#define MATCH 0 +#define NOMATCH 1 +#define ABORTED -1 + +int _shexp_match(char *str, char *exp); + +int handle_union(char *str, char *exp) +{ + char *e2 = (char *) MALLOC(sizeof(char)*strlen(exp)); + register int t,p2,p1 = 1; + int cp; + + while(1) { + for(cp=1;exp[cp] != ')';cp++) + if(exp[cp] == '\\') + ++cp; + for(p2 = 0;(exp[p1] != '|') && (p1 != cp);p1++,p2++) { + if(exp[p1] == '\\') + e2[p2++] = exp[p1++]; + e2[p2] = exp[p1]; + } + for(t=cp+1;(e2[p2] = exp[t]);++t,++p2); + if(_shexp_match(str,e2) == MATCH) { + FREE(e2); + return MATCH; + } + if(p1 == cp) { + FREE(e2); + return NOMATCH; + } + else ++p1; + } +} + + +int _shexp_match(char *str, char *exp) +{ + register int x,y; + int ret,neg; + + ret = 0; + for(x=0,y=0;exp[y];++y,++x) { + if((!str[x]) && (exp[y] != '(') && (exp[y] != '$') && (exp[y] != '*')) + ret = ABORTED; + else { + switch(exp[y]) { + case '$': + if( (str[x]) ) + ret = NOMATCH; + else + --x; /* we don't want loop to increment x */ + break; + case '*': + while(exp[++y] == '*'); + if(!exp[y]) + return MATCH; + while(str[x]) { + switch(_shexp_match(&str[x++],&exp[y])) { + case NOMATCH: + continue; + case ABORTED: + ret = ABORTED; + break; + default: + return MATCH; + } + break; + } + if((exp[y] == '$') && (exp[y+1] == '\0') && (!str[x])) + return MATCH; + else + ret = ABORTED; + break; + case '[': + if((neg = ((exp[++y] == '^') && (exp[y+1] != ']')))) + ++y; + + if((isalnum(exp[y])) && (exp[y+1] == '-') && + (isalnum(exp[y+2])) && (exp[y+3] == ']')) + { + int start = exp[y], end = exp[y+2]; + + /* Droolproofing for pinheads not included */ + if(neg ^ ((str[x] < start) || (str[x] > end))) { + ret = NOMATCH; + break; + } + y+=3; + } + else { + int matched; + + for(matched=0;exp[y] != ']';y++) + matched |= (str[x] == exp[y]); + if(neg ^ (!matched)) + ret = NOMATCH; + } + break; + case '(': + return handle_union(&str[x],&exp[y]); + break; + case '?': + break; + case '\\': + ++y; + default: +#ifdef XP_UNIX + if(str[x] != exp[y]) +#else /* XP_WIN32 */ + if(strnicmp(str + x, exp + y, 1)) +#endif /* XP_WIN32 */ + ret = NOMATCH; + break; + } + } + if(ret) + break; + } + return (ret ? ret : (str[x] ? NOMATCH : MATCH)); +} + +NSAPI_PUBLIC int shexp_match(char *str, char *xp) { + register int x; + char *exp = STRDUP(xp); + + for(x=strlen(exp)-1;x;--x) { + if((exp[x] == '~') && (exp[x-1] != '\\')) { + exp[x] = '\0'; + if(_shexp_match(str,&exp[++x]) == MATCH) + goto punt; + break; + } + } + if(_shexp_match(str,exp) == MATCH) { + FREE(exp); + return 0; + } + + punt: + FREE(exp); + return 1; +} + + +/* ------------------------------ shexp_cmp ------------------------------- */ + + +NSAPI_PUBLIC int shexp_cmp(char *str, char *exp) +{ + switch(shexp_valid(exp)) { + case INVALID_SXP: + return -1; + case NON_SXP: +#ifdef XP_UNIX + return (strcmp(exp,str) ? 1 : 0); +#else /* XP_WIN32 */ + return (stricmp(exp,str) ? 1 : 0); +#endif /* XP_WIN32 */ + default: + return shexp_match(str, exp); + } +} + + +/* ---------------------------- shexp_casecmp ----------------------------- */ + + +NSAPI_PUBLIC int shexp_casecmp(char *str, char *exp) +{ + char *lstr = STRDUP(str), *lexp = STRDUP(exp), *t; + int ret; + + for(t = lstr; *t; t++) + if(isalpha(*t)) *t = tolower(*t); + for(t = lexp; *t; t++) + if(isalpha(*t)) *t = tolower(*t); + + switch(shexp_valid(lexp)) { + case INVALID_SXP: + ret = -1; + break; + case NON_SXP: + ret = (strcmp(lexp, lstr) ? 1 : 0); + break; + default: + ret = shexp_match(lstr, lexp); + } + FREE(lstr); + FREE(lexp); + return ret; +} + diff --git a/lib/libaccess/utest/shexp.h b/lib/libaccess/utest/shexp.h new file mode 100644 index 00000000..edc91842 --- /dev/null +++ b/lib/libaccess/utest/shexp.h @@ -0,0 +1,131 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * shexp.h: Defines and prototypes for shell exp. match routines + * + * + * This routine will match a string with a shell expression. The expressions + * accepted are based loosely on the expressions accepted by zsh. + * + * o * matches anything + * o ? matches one character + * o \ will escape a special character + * o $ matches the end of the string + * o [abc] matches one occurence of a, b, or c. The only character that needs + * to be escaped in this is ], all others are not special. + * o [a-z] matches any character between a and z + * o [^az] matches any character except a or z + * o ~ followed by another shell expression will remove any pattern + * matching the shell expression from the match list + * o (foo|bar) will match either the substring foo, or the substring bar. + * These can be shell expressions as well. + * + * The public interface to these routines is documented below. + * + * Rob McCool + * + */ + +#ifndef SHEXP_H +#define SHEXP_H + +/* + * Requires that the macro MALLOC be set to a "safe" malloc that will + * exit if no memory is available. If not under MCC httpd, define MALLOC + * to be the real malloc and play with fire, or make your own function. + */ + +#include "../netsite.h" + +#include <ctype.h> /* isalnum */ +#include <string.h> /* strlen */ + + +/* + * Wrappers for shexp/regexp + * + * Portions of code that explicitly want to have either shexp's + * or regexp's should call those functions directly. + * + * Common code bases for multiple products should use the following + * macros instead to use either shell or regular expressions, + * depending on the flavor chosen for a given server. + * + */ +#if defined(MCC_PROXY) && defined(USE_REGEX) + +#include "base/regexp.h" + +#define WILDPAT_VALID(exp) regexp_valid(exp) +#define WILDPAT_MATCH(str, exp) regexp_match(str, exp) +#define WILDPAT_CMP(str, exp) regexp_cmp(str, exp) +#define WILDPAT_CASECMP(str, exp) regexp_casecmp(str, exp) + +#else /* HTTP servers */ + +#define WILDPAT_VALID(exp) shexp_valid(exp) +#define WILDPAT_MATCH(str, exp) shexp_match(str, exp) +#define WILDPAT_CMP(str, exp) shexp_cmp(str, exp) +#define WILDPAT_CASECMP(str, exp) shexp_casecmp(str, exp) + +#endif + + +/* --------------------------- Public routines ---------------------------- */ + +NSPR_BEGIN_EXTERN_C + +/* + * shexp_valid takes a shell expression exp as input. It returns: + * + * NON_SXP if exp is a standard string + * INVALID_SXP if exp is a shell expression, but invalid + * VALID_SXP if exp is a valid shell expression + */ + +#define NON_SXP -1 +#define INVALID_SXP -2 +#define VALID_SXP 1 + +/* and generic shexp/regexp versions */ +#define NON_WILDPAT NON_SXP +#define INVALID_WILDPAT INVALID_SXP +#define VALID_WILDPAT VALID_SXP + +/* and regexp versions */ +#define NON_REGEXP NON_SXP +#define INVALID_REGEXP INVALID_SXP +#define VALID_REGEXP VALID_SXP + + +NSAPI_PUBLIC int shexp_valid(char *exp); + +/* + * shexp_match + * + * Takes a prevalidated shell expression exp, and a string str. + * + * Returns 0 on match and 1 on non-match. + */ + +NSAPI_PUBLIC int shexp_match(char *str, char *exp); + + +/* + * shexp_cmp + * + * Same as above, but validates the exp first. 0 on match, 1 on non-match, + * -1 on invalid exp. shexp_casecmp does the same thing but is case + * insensitive. + */ + +NSAPI_PUBLIC int shexp_cmp(char *str, char *exp); +NSAPI_PUBLIC int shexp_casecmp(char *str, char *exp); + +NSPR_END_EXTERN_C + +#endif + diff --git a/lib/libaccess/utest/test.ref b/lib/libaccess/utest/test.ref new file mode 100644 index 00000000..d5207382 --- /dev/null +++ b/lib/libaccess/utest/test.ref @@ -0,0 +1,234 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +Failed ACL_FileMergeFile() test. +Method one is #1, rv=0 +Method two is #2, rv=0 +Method one repeated is #1, rv=0 +Method three is #3, rv=0 + Method[0] = "two" + Method[1] = "three" + Method[2] = "one" +DbType two is #2, rv=0 +DbType three is #3, rv=0 +DbType two repeated is #2, rv=0 +DbType four is #4, rv=0 + Database[0] = "db2" + Database[1] = "db1" + Database[2] = "db3" +position 1 +position 2 +position 3 +position 4 +aclfile0 = 0 + +aclfile1 = 1 + +aclfile2 = 1 + +aclfile3 = 1 + +aclfile4 = 0 + +aclfile5 = 0 + +aclfile6 = 0 + +aclfile7 = 1 + +aclfile8 = 0 + +aclfile9 = 3 + +ACL file: internal-buffer +Syntax error at line: 1, token: > +ACL file: internal-buffer +Syntax error at line: 2, token: junk +aclfile10 = 1 + +aclfile11 = 1 + +aclfile12 = 0 + +aclfile13 = 0 + +aclfile14 = 0 + +aclfile15 = 1 + +aclfile16 = 1 + +aclfile17 = 0 + +aclfile18 = 0 + +aclfile19 = 1 + +program = foo -1 + +program = nomatch -2 + +program = beer -1 + +program = http-grog -2 + +program = ubar -2 + +dnsalias = *? -1 + +dnsalias = aruba.mcom.com brain251.mcom.com? -1 + +dns = *? -1 + +dns != *? -2 + +dns = aruba.mcom.com? -1 + +dns = ai.mit.edu? -2 + +dns = *.ai.mit.edu? -2 + +dns = *.mit.edu? -2 + +dns = *.edu? -2 + +dns != *.edu? -1 + +mistake != *.edu? -5 + +dns > *.edu? -5 + +ip = *? -1 + +ip != *? -2 + +ip = *.*.*.*? -1 + +ip = 17.*? -1 + +ip = 17.*.*.*? -1 + +ip = 17.34.*? -1 + +ip = 17.34.*.*? -1 + +ip = 17.34.51.*? -1 + +ip = 17.34.51.*+255.255.255.255? -1 + +ip = 17.34.51.69+255.255.255.254, 123.45.67.89? -1 + +ip != 17.34.51.69+255.255.255.254, 123.45.67.89? -2 + +ip = 17.34.51.68, 17.34.51.69? -1 + +ip = 17.34.51.68, 17.34.51.69, 123.45.67.89? -1 + +ip != 17.34.51.68, 17.34.51.69, 123.45.67.89? -2 + +ip = 17.34.51.68? -1 + +ip = 17.34.51.69? -2 + +ip = 17.34.51.69+255.255.255.254? -1 + +ip = 17.34.50.69+255.255.254.0? -1 + +ip = 17.35.50.69+255.254.0.0? -1 + +ip = 16.35.50.69+254.0.0.0? -1 + +ip = 123.45.67.89? -2 + +ip != 123.45.67.89? -1 + +ip > 123.45.67.89? -5 + +ip < 123.45.67.89? -5 + +ip >= 123.45.67.89? -5 + +ip <= 123.45.67.89? -5 + +mistake <= 123.45.67.89? -5 + +time = 2120? -1 + +time != 2120? -2 + +time = 0700? -2 + +time != 0700? -1 + +time = 2400? -2 + +time != 2400? -1 + +time > 2120? -2 + +time < 2120? -2 + +time > 0700? -1 + +time < 0700? -2 + +time > 2400? -2 + +time < 2400? -1 + +time >= 2120? -1 + +time <= 2120? -1 + +time >= 0700? -1 + +time <= 0700? -2 + +time >= 2400? -2 + +time <= 2400? -1 + +mistake <= 2400? -5 + +time = 0800-2200? -1 + +time != 0800-2200? -2 + +time = 2200-0800? -2 + +time != 2200-0800? -1 + +time <= 2200-0800? -5 + += mon? -1 + += tUe? -2 + += weD? -2 + += THu? -2 + += FrI? -2 + += tUe? -2 + += Sun? -2 + += mon,tuewed,thu,frisatsun? -1 + +!= mon,tuewed,thu,frisatsun? -2 + +> Sun? -5 + +< Sun? -5 + +>= Sun? -5 + +<= Sun? -5 + +mistake <= Sun? -5 + diff --git a/lib/libaccess/utest/testmain.cpp b/lib/libaccess/utest/testmain.cpp new file mode 100644 index 00000000..4da14cee --- /dev/null +++ b/lib/libaccess/utest/testmain.cpp @@ -0,0 +1,52 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +/* + * ACL parser unit test program + */ + +#include <stdio.h> +#include <netsite.h> +#include <libaccess/acl.h> +#include <libaccess/nserror.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> + +main(int argc, char **argv) +{ + +ACLListHandle_t *acllist; +int ii; +char filename[255]; +ACLWrapper_t *wrap; +ACLExprHandle_t *expr; + + if ( argc < 2 ) { + fprintf(stderr, "usage: aclparse <filenames>\n"); + exit(1); + } + for (ii = 1; ii < argc; ii++ ) { + acllist = ACL_ParseFile(NULL, argv[ii]); + if ( acllist == NULL ) { + printf("Failed to parse ACL.\n"); + + } else { + for (wrap = acllist->acl_list_head; wrap; + wrap = wrap->wrap_next) { + for (expr=wrap->acl->expr_list_head; + expr; + expr = expr->expr_next ) { + ACL_ExprDisplay(expr); + } + } + } + + + sprintf(filename, "%s.v30", argv[ii]); + ACL_WriteFile(NULL, filename, acllist); + ACL_ListDestroy( acllist ); + } + +} diff --git a/lib/libaccess/utest/twotest.cpp b/lib/libaccess/utest/twotest.cpp new file mode 100644 index 00000000..2d4fb503 --- /dev/null +++ b/lib/libaccess/utest/twotest.cpp @@ -0,0 +1,57 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +#include <netsite.h> +#include <base/session.h> +#include <base/plist.h> +#include <base/ereport.h> +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/las.h> + + +extern ACLListHandle_t *ACL_ParseFile(NSErr_t *errp, char *filename); +extern ACLEvalDestroyContext(NSErr_t *errp, ACLEvalHandle_t *acleval); + +main(int arc, char **argv) +{ + int result; + int cachable; + void *las_cookie=NULL; + ACLEvalHandle_t eval; + char *rights[2]; + char *map_generic[7]; + char filename[20]; + int i; + char *bong; + char *bong_type; + char *acl_tag; + int expr_num; + + /* ACL Eval Unit Tests + */ + + rights[0] = "html_read"; + rights[1] = "html_write"; + rights[2] = NULL; + + map_generic[0] = "html_read"; + map_generic[1] = "html_write"; + map_generic[2] = "N/A"; + map_generic[3] = "html_create"; + map_generic[4] = "html_delete"; + map_generic[5] = "N/A"; + map_generic[6] = NULL; + + eval.acllist = ACL_ParseFile((NSErr_t *)NULL, argv[1]); + result = ACL_EvalTestRights(NULL, &eval, &rights[0], map_generic, &bong, &bong_type, &acl_tag, &expr_num); + ACLEvalDestroyContext(NULL, &eval); + ACL_ListDestroy(NULL, eval.acllist); + printf("%s = %d\n\n", argv[1], result); + +} diff --git a/lib/libaccess/utest/ustubs.cpp b/lib/libaccess/utest/ustubs.cpp new file mode 100644 index 00000000..ccfa3108 --- /dev/null +++ b/lib/libaccess/utest/ustubs.cpp @@ -0,0 +1,283 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <sys/types.h> +#include <malloc.h> +#include <string.h> +#include <base/crit.h> +#include <base/plist.h> + +#include <libaccess/nserror.h> +#include <libaccess/acl.h> +#include "../aclpriv.h" +#include <libaccess/aclproto.h> +#include <libaccess/ldapacl.h> +#include <ldaputil/dbconf.h> +#ifdef NSPR20 +#include <prprf.h> +#else +#include <nspr/prprf.h> +#endif + +NSPR_BEGIN_EXTERN_C +extern char * ACL_Program; +extern int conf_getglobals(); +extern int SPconf_getglobals(); +extern int ereport(int, char*, ...); +extern int SPereport(int, char*, ...); +extern char * GetAdminLanguage(void); +extern char * XP_GetStringFromDatabase(char *strLibraryName, char *strLanguage, int iToken); +extern void ACL_Restart(void *cntlData); +extern int XP_SetError(); +extern int XP_GetError(); +extern int acl_usr_cache_init(); +extern int acl_usr_cache_set_group(); +extern int acl_usr_cache_group_check(); +extern int sema_destroy(); +extern char *ldapu_err2string(int err); +extern int ACL_CacheFlush(void); +NSPR_END_EXTERN_C + +static char errbuf[10]; + +char * +ldapu_err2string(int err) +{ + sprintf(errbuf, "%d", err); + return errbuf; +} + + +void init_ldb_rwlock () +{ +} + +sema_destroy() +{ + return 0; +} + +#ifdef notdef +char *system_errmsg() +{ + static char errmsg[1024]; + + sprintf(errmsg, "Stubbed system_errmsg"); + return errmsg; +} +#endif + +int +ACL_CacheFlushRegister(AclCacheFlushFunc_t flush_func) +{ + return 0; +} + +acl_usr_cache_init() +{ + return 0; +} + +acl_usr_cache_group_check() +{ + return 0; +} + +acl_usr_cache_set_group() +{ + return 0; +} + +XP_SetError() +{ + return 0; +} + +XP_GetError() +{ + return 0; +} + +CRITICAL +crit_init() +{ + return (CRITICAL)1; +} + +void +crit_enter(CRITICAL c) +{ + return; +} + +void +crit_exit(CRITICAL c) +{ + return; +} + +void +crit_terminate(CRITICAL c) +{ + return; +} + +int crit_owner_is_me(CRITICAL id) +{ + return 1; +} + +symTableFindSym() +{ + return 0; +} + +int +ldap_auth_uid_groupid(LDAP *ld, char *uid, char *groupid, + char *base) +{ + return 0; +} + +LDAP * +init_ldap (char *host, int port, int use_ssl) +{ + return (LDAP *)"init_ldap_stub"; +} + +int ACL_LDAPDatabaseHandle (NSErr_t *errp, const char *dbname, LDAP **ld, + char **basedn) +{ + *ld = (LDAP *)"ACL_LDAPDatabaseHandle_stub"; + if (basedn) *basedn = strdup("unknown basedn"); + return LAS_EVAL_TRUE; +} + +#ifdef notdef +NSEFrame_t * nserrGenerate(NSErr_t * errp, long retcode, long errorid, + char * program, int errc, ...) +{ + return 0; +} +#endif + +char * ACL_Program; + +char * +LASUserGetUser() +{ + return "hmiller"; +} + +LASIpGetIp() +{ + return(0x11223344); +} + +LASDnsGetDns(char **dnsv) +{ + *dnsv = "aruba.mcom.com"; + return 0; +} + +int +ACL_DestroyList() +{ +return(0); +} + +aclCheckHosts() +{ +return(0); +} + +aclCheckUsers() +{ +return(0); +} + +char *LASGroupGetUser() +{ + return("hmiller"); +} + +int +SPconf_getglobals() +{ + return 0; +} + +int +conf_getglobals() +{ + return 0; +} + +int +SPereport(int degree, char *fmt, ...) +{ + va_list args; + char errstr[1024]; + + va_start(args, fmt); + PR_vsnprintf(&errstr[0], sizeof(errstr), fmt, args); + printf("%s", errstr); + va_end(args); + return 0; +} + +int +ereport(int degree, char *fmt, ...) +{ + va_list args; + char errstr[1024]; + + va_start(args, fmt); + PR_vsnprintf(&errstr[0], sizeof(errstr), fmt, args); + printf("%s", errstr); + va_end(args); + return 0; +} + +#ifdef notdef +int dbconf_read_config_file (const char *file, DBConfInfo_t **conf_info_out) +{ + return 0; +} +#endif + +char * +GetAdminLanguage(void) +{ + return ""; +} + +static char errstr[1024]; + +char * +XP_GetStringFromDatabase(char *strLibraryName, char *strLanguage, int iToken) +{ + sprintf(errstr, "XP_GetAdminStr called for error %d\n", iToken); + return errstr; +} + +void +ACL_Restart(void * cntlData) +{ + return; +} + +NSAPI_PUBLIC int +parse_ldap_url(NSErr_t *errp, ACLDbType_t dbtype, const char *name, const char +*url, PList_t plist, void **db) +{ + return 0; +} + +int +ACL_CacheFlush(void) +{ + return 0; +} diff --git a/lib/libaccess/winnt.l b/lib/libaccess/winnt.l new file mode 100644 index 00000000..ce72b535 --- /dev/null +++ b/lib/libaccess/winnt.l @@ -0,0 +1,762 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ +#include <stdio.h> +# define U(x) x +# define NLSTATE yyprevious=YYNEWLINE +# define BEGIN yybgin = yysvec + 1 + +# define INITIAL 0 +# define YYLERR yysvec +# define YYSTATE (yyestate-yysvec-1) +# define YYOPTIM 1 +# define YYLMAX BUFSIZ +#ifndef __cplusplus +# define output(c) (void)putc(c,yyout) +#else +# define lex_output(c) (void)putc(c,yyout) +#endif + +#if defined(__cplusplus) || defined(__STDC__) + +#if defined(__cplusplus) && defined(__EXTERN_C__) +extern "C" { +#endif + int yyback(int *, int); + int yyinput(void); + int yylook(void); + void yyoutput(int); + int yyracc(int); + int yyreject(void); + void yyunput(int); + int yylex(void); +#ifdef YYLEX_E + void yywoutput(wchar_t); + wchar_t yywinput(void); +#endif +#ifndef yyless + void yyless(int); +#endif +#ifndef yywrap + int yywrap(void); +#endif +#ifdef LEXDEBUG + void allprint(char); + void sprint(char *); +#endif +#if defined(__cplusplus) && defined(__EXTERN_C__) +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void exit(int); +#ifdef __cplusplus +} +#endif + +#endif +# define unput(c) {yytchar= (c);if(yytchar=='\n')yylineno--;*yysptr++=yytchar;} +# define yymore() (yymorfg=1) +#ifndef __cplusplus +# define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(yyin))==10?(yylineno++,yytchar):yytchar)==EOF?0:yytchar) +#else +# define lex_input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(yyin))==10?(yylineno++,yytchar):yytchar)==EOF?0:yytchar) +#endif +#define ECHO fprintf(yyout, "%s",yytext) +# define REJECT { nstr = yyreject(); goto yyfussy;} +int yyleng; extern char yytext[]; +int yymorfg; +extern char *yysptr, yysbuf[]; +int yytchar; +FILE *yyin = NULL, *yyout = NULL; +extern int yylineno; +struct yysvf { + struct yywork *yystoff; + struct yysvf *yyother; + int *yystops;}; +struct yysvf *yyestate; +extern struct yysvf yysvec[], *yybgin; + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "y.tab.h" +#include "libaccess/ava.h" +/*#include "netsite.h" */ + +int linenum = 1; +int first_time = 1; +int old_state; +int num_nested_comments = 0; + +extern AVAEntry tempEntry; +extern AVATable entryTable; + +void strip_quotes(void); + +# define COMMENT 2 +# define NORM 4 +# define DEFINES 6 +# define DEF_TYPE 8 +# define YYNEWLINE 10 +yylex(){ +int nstr; extern int yyprevious; + + if (yyin == NULL) yyin = stdin; + if (yyout == NULL) yyout = stdout; + if (first_time) { + BEGIN NORM; + first_time = tempEntry.numOrgs = 0; + old_state = NORM; + tempEntry.userid = 0; + tempEntry.country = 0; + tempEntry.CNEntry = 0; + tempEntry.email = 0; + tempEntry.locality = 0; + tempEntry.state = 0; + entryTable.numEntries = 0; + } +#ifdef __cplusplus +/* to avoid CC and lint complaining yyfussy not being used ...*/ +static int __lex_hack = 0; +if (__lex_hack) goto yyfussy; +#endif +while((nstr = yylook()) >= 0) +yyfussy: switch(nstr){ +case 0: +if(yywrap()) return(0); break; +case 1: + +# line 58 "avascan.l" + {BEGIN COMMENT; num_nested_comments++;} +break; +case 2: + +# line 59 "avascan.l" + {num_nested_comments--; + if (!num_nested_comments) BEGIN old_state;} +break; +case 3: + +# line 61 "avascan.l" + {;} +break; +case 4: + +# line 63 "avascan.l" + {yylval.string = system_strdup(yytext); + return USER_ID;} +break; +case 5: + +# line 65 "avascan.l" +{BEGIN DEF_TYPE; + old_state = DEF_TYPE;} +break; +case 6: + +# line 68 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_C; } +break; +case 7: + +# line 70 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_CO;} +break; +case 8: + +# line 72 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_OU;} +break; +case 9: + +# line 74 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_CN;} +break; +case 10: + +# line 76 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_L;} +break; +case 11: + +# line 78 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_E;} +break; +case 12: + +# line 80 "avascan.l" + {BEGIN DEFINES; old_state = DEFINES; + return DEF_ST;} +break; +case 13: + +# line 82 "avascan.l" + {BEGIN NORM;old_state = NORM;} +break; +case 14: + +# line 84 "avascan.l" + {return EQ_SIGN;} +break; +case 15: + +# line 85 "avascan.l" + {BEGIN DEF_TYPE; old_state = DEF_TYPE; + strip_quotes(); + return DEF_ID;} +break; +case 16: + +# line 89 "avascan.l" + {;} +break; +case 17: + +# line 90 "avascan.l" + {linenum++;} +break; +case 18: + +# line 91 "avascan.l" + {yyerror("Bad input character");} +break; +case -1: +break; +default: +(void)fprintf(yyout,"bad switch yylook %d",nstr); +} return(0); } +/* end of yylex */ + +int yywrap () { + return 1; +} + +void strip_quotes(void) { + yytext[strlen(yytext)-1]= '\0'; + yylval.string = system_strdup(&yytext[1]); +} +int yyvstop[] = { +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +16, +0, + +18, +0, + +16, +18, +0, + +17, +0, + +18, +0, + +3, +18, +0, + +3, +16, +18, +0, + +3, +18, +0, + +3, +18, +0, + +4, +18, +0, + +18, +0, + +18, +0, + +14, +18, +0, + +6, +18, +0, + +11, +18, +0, + +10, +18, +0, + +7, +18, +0, + +18, +0, + +13, +18, +0, + +16, +0, + +1, +0, + +2, +0, + +4, +0, + +5, +0, + +15, +0, + +9, +0, + +8, +0, + +12, +0, +0}; +# define YYTYPE unsigned char +struct yywork { YYTYPE verify, advance; } yycrank[] = { +0,0, 0,0, 1,11, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 1,12, 1,13, +0,0, 3,15, 12,29, 0,0, +20,33, 0,0, 0,0, 0,0, +0,0, 3,16, 3,13, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 9,11, 0,0, 1,11, +0,0, 12,29, 7,21, 20,33, +8,21, 9,12, 9,13, 14,30, +0,0, 1,11, 3,15, 4,17, +1,14, 1,11, 2,14, 7,14, +4,18, 8,14, 3,17, 5,19, +3,15, 17,31, 5,14, 3,18, +3,15, 6,19, 10,14, 21,35, +6,14, 7,22, 9,11, 8,22, +0,0, 5,20, 0,0, 21,35, +21,35, 0,0, 0,0, 6,20, +9,11, 0,0, 0,0, 9,14, +9,11, 23,37, 10,23, 0,0, +10,24, 27,39, 26,38, 0,0, +0,0, 0,0, 0,0, 10,25, +0,0, 0,0, 10,26, 0,0, +21,36, 0,0, 10,27, 9,23, +0,0, 9,24, 0,0, 0,0, +0,0, 0,0, 21,35, 0,0, +9,25, 0,0, 21,35, 9,26, +0,0, 0,0, 0,0, 9,27, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 0,0, 20,34, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 19,32, 0,0, 0,0, +10,28, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 0,0, +0,0, 0,0, 0,0, 0,0, +0,0, 9,28, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +0,0, 0,0, 0,0, 0,0, +19,32, 0,0, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +19,32, 19,32, 19,32, 19,32, +0,0}; +struct yysvf yysvec[] = { +0, 0, 0, +yycrank+-1, 0, yyvstop+1, +yycrank+-3, yysvec+1, yyvstop+3, +yycrank+-12, 0, yyvstop+5, +yycrank+-5, yysvec+3, yyvstop+7, +yycrank+-11, yysvec+1, yyvstop+9, +yycrank+-17, yysvec+1, yyvstop+11, +yycrank+-4, yysvec+1, yyvstop+13, +yycrank+-6, yysvec+1, yyvstop+15, +yycrank+-32, 0, yyvstop+17, +yycrank+-15, yysvec+9, yyvstop+19, +yycrank+0, 0, yyvstop+21, +yycrank+5, 0, yyvstop+23, +yycrank+0, 0, yyvstop+26, +yycrank+1, 0, yyvstop+28, +yycrank+0, 0, yyvstop+30, +yycrank+0, yysvec+12, yyvstop+33, +yycrank+10, 0, yyvstop+37, +yycrank+0, yysvec+14, yyvstop+40, +yycrank+93, 0, yyvstop+43, +yycrank+7, 0, yyvstop+46, +yycrank+-62, 0, yyvstop+48, +yycrank+0, 0, yyvstop+50, +yycrank+3, 0, yyvstop+53, +yycrank+0, 0, yyvstop+56, +yycrank+0, 0, yyvstop+59, +yycrank+1, 0, yyvstop+62, +yycrank+1, 0, yyvstop+65, +yycrank+0, 0, yyvstop+67, +yycrank+0, yysvec+12, yyvstop+70, +yycrank+0, 0, yyvstop+72, +yycrank+0, 0, yyvstop+74, +yycrank+0, yysvec+19, yyvstop+76, +yycrank+0, yysvec+20, 0, +yycrank+0, 0, yyvstop+78, +yycrank+0, yysvec+21, 0, +yycrank+0, 0, yyvstop+80, +yycrank+0, 0, yyvstop+82, +yycrank+0, 0, yyvstop+84, +yycrank+0, 0, yyvstop+86, +0, 0, 0}; +struct yywork *yytop = yycrank+215; +struct yysvf *yybgin = yysvec+1; +char yymatch[] = { + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 9, 10, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 9, 1, 34, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 44, 1, 1, 1, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 1, 1, 1, 1, 1, 1, + 1, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 1, 1, 1, 1, 44, + 1, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 44, 44, 44, 44, 44, + 44, 44, 44, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, +0}; +char yyextra[] = { +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0, +0}; +/* Copyright (c) 1989 AT&T */ +/* All Rights Reserved */ + +/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ +/* The copyright notice above does not evidence any */ +/* actual or intended publication of such source code. */ + +#pragma ident "@(#)ncform 6.7 93/06/07 SMI" + +int yylineno =1; +# define YYU(x) x +# define NLSTATE yyprevious=YYNEWLINE +char yytext[YYLMAX]; +struct yysvf *yylstate [YYLMAX], **yylsp, **yyolsp; +char yysbuf[YYLMAX]; +char *yysptr = yysbuf; +int *yyfnd; +extern struct yysvf *yyestate; +int yyprevious = YYNEWLINE; +#if defined(__cplusplus) || defined(__STDC__) +int yylook(void) +#else +yylook() +#endif +{ + register struct yysvf *yystate, **lsp; + register struct yywork *yyt; + struct yysvf *yyz; + int yych, yyfirst; + struct yywork *yyr; +# ifdef LEXDEBUG + int debug; +# endif + char *yylastch; + /* start off machines */ +# ifdef LEXDEBUG + debug = 0; +# endif + yyfirst=1; + if (!yymorfg) + yylastch = yytext; + else { + yymorfg=0; + yylastch = yytext+yyleng; + } + for(;;){ + lsp = yylstate; + yyestate = yystate = yybgin; + if (yyprevious==YYNEWLINE) yystate++; + for (;;){ +# ifdef LEXDEBUG + if(debug)fprintf(yyout,"state %d\n",yystate-yysvec-1); +# endif + yyt = yystate->yystoff; + if(yyt == yycrank && !yyfirst){ /* may not be any transitions */ + yyz = yystate->yyother; + if(yyz == 0)break; + if(yyz->yystoff == yycrank)break; + } +#ifndef __cplusplus + *yylastch++ = yych = input(); +#else + *yylastch++ = yych = lex_input(); +#endif + if(yylastch > &yytext[YYLMAX]) { + fprintf(yyout,"Input string too long, limit %d\n",YYLMAX); + exit(1); + } + yyfirst=0; + tryagain: +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"char "); + allprint(yych); + putchar('\n'); + } +# endif + yyr = yyt; + if ( (int)yyt > (int)yycrank){ + yyt = yyr + yych; + if (yyt <= yytop && yyt->verify+yysvec == yystate){ + if(yyt->advance+yysvec == YYLERR) /* error transitions */ + {unput(*--yylastch);break;} + *lsp++ = yystate = yyt->advance+yysvec; + if(lsp > &yylstate[YYLMAX]) { + fprintf(yyout,"Input string too long, limit %d\n",YYLMAX); + exit(1); + } + goto contin; + } + } +# ifdef YYOPTIM + else if((int)yyt < (int)yycrank) { /* r < yycrank */ + yyt = yyr = yycrank+(yycrank-yyt); +# ifdef LEXDEBUG + if(debug)fprintf(yyout,"compressed state\n"); +# endif + yyt = yyt + yych; + if(yyt <= yytop && yyt->verify+yysvec == yystate){ + if(yyt->advance+yysvec == YYLERR) /* error transitions */ + {unput(*--yylastch);break;} + *lsp++ = yystate = yyt->advance+yysvec; + if(lsp > &yylstate[YYLMAX]) { + fprintf(yyout,"Input string too long, limit %d\n",YYLMAX); + exit(1); + } + goto contin; + } + yyt = yyr + YYU(yymatch[yych]); +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"try fall back character "); + allprint(YYU(yymatch[yych])); + putchar('\n'); + } +# endif + if(yyt <= yytop && yyt->verify+yysvec == yystate){ + if(yyt->advance+yysvec == YYLERR) /* error transition */ + {unput(*--yylastch);break;} + *lsp++ = yystate = yyt->advance+yysvec; + if(lsp > &yylstate[YYLMAX]) { + fprintf(yyout,"Input string too long, limit %d\n",YYLMAX); + exit(1); + } + goto contin; + } + } + if ((yystate = yystate->yyother) && (yyt= yystate->yystoff) != yycrank){ +# ifdef LEXDEBUG + if(debug)fprintf(yyout,"fall back to state %d\n",yystate-yysvec-1); +# endif + goto tryagain; + } +# endif + else + {unput(*--yylastch);break;} + contin: +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"state %d char ",yystate-yysvec-1); + allprint(yych); + putchar('\n'); + } +# endif + ; + } +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"stopped at %d with ",*(lsp-1)-yysvec-1); + allprint(yych); + putchar('\n'); + } +# endif + while (lsp-- > yylstate){ + *yylastch-- = 0; + if (*lsp != 0 && (yyfnd= (*lsp)->yystops) && *yyfnd > 0){ + yyolsp = lsp; + if(yyextra[*yyfnd]){ /* must backup */ + while(yyback((*lsp)->yystops,-*yyfnd) != 1 && lsp > yylstate){ + lsp--; + unput(*yylastch--); + } + } + yyprevious = YYU(*yylastch); + yylsp = lsp; + yyleng = yylastch-yytext+1; + yytext[yyleng] = 0; +# ifdef LEXDEBUG + if(debug){ + fprintf(yyout,"\nmatch "); + sprint(yytext); + fprintf(yyout," action %d\n",*yyfnd); + } +# endif + return(*yyfnd++); + } + unput(*yylastch); + } + if (yytext[0] == 0 /* && feof(yyin) */) + { + yysptr=yysbuf; + return(0); + } +#ifndef __cplusplus + yyprevious = yytext[0] = input(); + if (yyprevious>0) + output(yyprevious); +#else + yyprevious = yytext[0] = lex_input(); + if (yyprevious>0) + lex_output(yyprevious); +#endif + yylastch=yytext; +# ifdef LEXDEBUG + if(debug)putchar('\n'); +# endif + } + } +#if defined(__cplusplus) || defined(__STDC__) +int yyback(int *p, int m) +#else +yyback(p, m) + int *p; +#endif +{ + if (p==0) return(0); + while (*p) { + if (*p++ == m) + return(1); + } + return(0); +} + /* the following are only used in the lex library */ +#if defined(__cplusplus) || defined(__STDC__) +int yyinput(void) +#else +yyinput() +#endif +{ +#ifndef __cplusplus + return(input()); +#else + return(lex_input()); +#endif + } +#if defined(__cplusplus) || defined(__STDC__) +void yyoutput(int c) +#else +yyoutput(c) + int c; +#endif +{ +#ifndef __cplusplus + output(c); +#else + lex_output(c); +#endif + } +#if defined(__cplusplus) || defined(__STDC__) +void yyunput(int c) +#else +yyunput(c) + int c; +#endif +{ + unput(c); + } diff --git a/lib/libaccess/winnt.v b/lib/libaccess/winnt.v new file mode 100644 index 00000000..9fea3453 --- /dev/null +++ b/lib/libaccess/winnt.v @@ -0,0 +1,156 @@ +/* Copyright (c) 1988 AT&T */
+/* All Rights Reserved */
+
+/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
+/* The copyright notice above does not evidence any */
+/* actual or intended publication of such source code. */
+
+#ifndef _VALUES_H
+#define _VALUES_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * These values work with any binary representation of integers
+ * where the high-order bit contains the sign.
+ */
+
+/* a number used normally for size of a shift */
+#define BITSPERBYTE 8
+
+#define BITS(type) (BITSPERBYTE * (int)sizeof (type))
+
+/* short, regular and long ints with only the high-order bit turned on */
+#define HIBITS ((short)(1 << BITS(short) - 1))
+
+#if defined(__STDC__)
+
+#define HIBITI (1U << BITS(int) - 1)
+#define HIBITL (1UL << BITS(long) - 1)
+
+#else
+
+#define HIBITI ((unsigned)1 << BITS(int) - 1)
+#define HIBITL (1L << BITS(long) - 1)
+
+#endif
+
+/* largest short, regular and long int */
+#define MAXSHORT ((short)~HIBITS)
+#define MAXINT ((int)(~HIBITI))
+#define MAXLONG ((long)(~HIBITL))
+
+/*
+ * various values that describe the binary floating-point representation
+ * _EXPBASE - the exponent base
+ * DMAXEXP - the maximum exponent of a double (as returned by frexp())
+ * FMAXEXP - the maximum exponent of a float (as returned by frexp())
+ * DMINEXP - the minimum exponent of a double (as returned by frexp())
+ * FMINEXP - the minimum exponent of a float (as returned by frexp())
+ * MAXDOUBLE - the largest double
+ * ((_EXPBASE ** DMAXEXP) * (1 - (_EXPBASE ** -DSIGNIF)))
+ * MAXFLOAT - the largest float
+ * ((_EXPBASE ** FMAXEXP) * (1 - (_EXPBASE ** -FSIGNIF)))
+ * MINDOUBLE - the smallest double (_EXPBASE ** (DMINEXP - 1))
+ * MINFLOAT - the smallest float (_EXPBASE ** (FMINEXP - 1))
+ * DSIGNIF - the number of significant bits in a double
+ * FSIGNIF - the number of significant bits in a float
+ * DMAXPOWTWO - the largest power of two exactly representable as a double
+ * FMAXPOWTWO - the largest power of two exactly representable as a float
+ * _IEEE - 1 if IEEE standard representation is used
+ * _DEXPLEN - the number of bits for the exponent of a double
+ * _FEXPLEN - the number of bits for the exponent of a float
+ * _HIDDENBIT - 1 if high-significance bit of mantissa is implicit
+ * LN_MAXDOUBLE - the natural log of the largest double -- log(MAXDOUBLE)
+ * LN_MINDOUBLE - the natural log of the smallest double -- log(MINDOUBLE)
+ * LN_MAXFLOAT - the natural log of the largest float -- log(MAXFLOAT)
+ * LN_MINFLOAT - the natural log of the smallest float -- log(MINFLOAT)
+ */
+
+#if defined(__STDC__)
+
+/*
+ * Note that the following construct, "!#machine(name)", is a non-standard
+ * extension to ANSI-C. It is maintained here to provide compatibility
+ * for existing compilations systems, but should be viewed as transitional
+ * and may be removed in a future release. If it is required that this
+ * file not contain this extension, edit this file to remove the offending
+ * condition.
+ *
+ * These machines are all IEEE-754:
+ */
+#if #machine(i386) || defined(__i386) || #machine(sparc) || defined(__sparc)
+#define MAXDOUBLE 1.79769313486231570e+308
+#define MAXFLOAT ((float)3.40282346638528860e+38)
+#define MINDOUBLE 4.94065645841246544e-324
+#define MINFLOAT ((float)1.40129846432481707e-45)
+#define _IEEE 1
+#define _DEXPLEN 11
+#define _HIDDENBIT 1
+#define _LENBASE 1
+#define DMINEXP (-(DMAXEXP + DSIGNIF - _HIDDENBIT - 3))
+#define FMINEXP (-(FMAXEXP + FSIGNIF - _HIDDENBIT - 3))
+#else
+#error ISA not supported
+#endif
+
+#else
+
+/*
+ * These machines are all IEEE-754:
+ */
+#if defined(i386) || defined(__i386) || defined(sparc) || defined(__sparc)
+#define MAXDOUBLE 1.79769313486231570e+308
+#define MAXFLOAT ((float)3.40282346638528860e+38)
+#define MINDOUBLE 4.94065645841246544e-324
+#define MINFLOAT ((float)1.40129846432481707e-45)
+#define _IEEE 1
+#define _DEXPLEN 11
+#define _HIDDENBIT 1
+#define _LENBASE 1
+#define DMINEXP (-(DMAXEXP + DSIGNIF - _HIDDENBIT - 3))
+#define FMINEXP (-(FMAXEXP + FSIGNIF - _HIDDENBIT - 3))
+#else
+/* #error is strictly ansi-C, but works as well as anything for K&R systems. */
+/*#error ISA not supported */
+#endif
+
+#endif /* __STDC__ */
+
+#define _EXPBASE (1 << _LENBASE)
+#define _FEXPLEN 8
+#define DSIGNIF (BITS(double) - _DEXPLEN + _HIDDENBIT - 1)
+#define FSIGNIF (BITS(float) - _FEXPLEN + _HIDDENBIT - 1)
+#define DMAXPOWTWO ((double)(1L << BITS(long) - 2) * \
+ (1L << DSIGNIF - BITS(long) + 1))
+#define FMAXPOWTWO ((float)(1L << FSIGNIF - 1))
+#define DMAXEXP ((1 << _DEXPLEN - 1) - 1 + _IEEE)
+#define FMAXEXP ((1 << _FEXPLEN - 1) - 1 + _IEEE)
+#define LN_MAXDOUBLE (M_LN2 * DMAXEXP)
+#define LN_MAXFLOAT (float)(M_LN2 * FMAXEXP)
+#define LN_MINDOUBLE (M_LN2 * (DMINEXP - 1))
+#define LN_MINFLOAT (float)(M_LN2 * (FMINEXP - 1))
+#define H_PREC (DSIGNIF % 2 ? (1L << DSIGNIF/2) * M_SQRT2 : 1L << DSIGNIF/2)
+#define FH_PREC \
+ (float)(FSIGNIF % 2 ? (1L << FSIGNIF/2) * M_SQRT2 : 1L << FSIGNIF/2)
+#define X_EPS (1.0/H_PREC)
+#define FX_EPS (float)((float)1.0/FH_PREC)
+#define X_PLOSS ((double)(long)(M_PI * H_PREC))
+#define FX_PLOSS ((float)(long)(M_PI * FH_PREC))
+#define X_TLOSS (M_PI * DMAXPOWTWO)
+#define FX_TLOSS (float)(M_PI * FMAXPOWTWO)
+#define M_LN2 0.69314718055994530942
+#define M_PI 3.14159265358979323846
+#define M_SQRT2 1.41421356237309504880
+#define MAXBEXP DMAXEXP /* for backward compatibility */
+#define MINBEXP DMINEXP /* for backward compatibility */
+#define MAXPOWTWO DMAXPOWTWO /* for backward compatibility */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VALUES_H */
diff --git a/lib/libaccess/winnt.y b/lib/libaccess/winnt.y new file mode 100644 index 00000000..0ac06dfd --- /dev/null +++ b/lib/libaccess/winnt.y @@ -0,0 +1,793 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + + +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "libaccess/ava.h" +/*#include "libaccess/avapfile.h" */ +/* #include "netsite.h" */ + +extern char *currFile; + +extern int linenum; +extern char yytext[]; + +static void AddDefType (int defType, char *defId); +static void AddAVA (char* userID); + +void yyerror(const char* string); +extern void logerror(const char* string,int num, char *file); + +AVAEntry tempEntry; +AVATable entryTable; + + +typedef union +#ifdef __cplusplus + YYSTYPE +#endif + { + char *string; + int num; +} YYSTYPE; +# define DEF_C 257 +# define DEF_CO 258 +# define DEF_OU 259 +# define DEF_CN 260 +# define EQ_SIGN 261 +# define DEF_START 262 +# define DEF_L 263 +# define DEF_E 264 +# define DEF_ST 265 +# define USER_ID 266 +# define DEF_ID 267 + +#ifdef __STDC__ +#include <stdlib.h> +#include <string.h> +#else +#include <malloc.h> +#include <memory.h> +#endif + +#include <values.h> + +#ifdef __cplusplus + +#ifndef yyerror + void yyerror(const char *); +#endif + +#ifndef yylex +#ifdef __EXTERN_C__ + extern "C" { int yylex(void); } +#else + int yylex(void); +#endif +#endif + int yyparse(void); + +#endif +#define yyclearin yychar = -1 +#define yyerrok yyerrflag = 0 +extern int yychar; +extern int yyerrflag; +YYSTYPE yylval; +YYSTYPE yyval; +typedef int yytabelem; +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 150 +#endif +#if YYMAXDEPTH > 0 +int yy_yys[YYMAXDEPTH], *yys = yy_yys; +YYSTYPE yy_yyv[YYMAXDEPTH], *yyv = yy_yyv; +#else /* user does initial allocation */ +int *yys; +YYSTYPE *yyv; +#endif +static int yymaxdepth = YYMAXDEPTH; +# define YYERRCODE 256 + + + +void yyerror(const char* string) { + logerror(string,linenum,currFile); +} + + +void AddDefType (int defType, char *defId) { + switch (defType) { + case DEF_C: + tempEntry.country = defId; + break; + case DEF_CO: + tempEntry.company = defId; + break; + case DEF_OU: + if (tempEntry.numOrgs % ORGS_ALLOCSIZE == 0) { + if (tempEntry.numOrgs == 0) { + tempEntry.organizations = + system_malloc_perm (sizeof (char*) * ORGS_ALLOCSIZE); + } else { + char **temp; + temp = + system_malloc_perm(sizeof(char*) * (tempEntry.numOrgs + ORGS_ALLOCSIZE)); + memcpy (temp, tempEntry.organizations, + sizeof(char*)*tempEntry.numOrgs); + system_free_perm(tempEntry.organizations); + tempEntry.organizations = temp; + } + } + tempEntry.organizations[tempEntry.numOrgs++] = defId; + break; + case DEF_CN: + tempEntry.CNEntry = defId; + break; + case DEF_E: + tempEntry.email = defId; + break; + case DEF_L: + tempEntry.locality = defId; + break; + case DEF_ST: + tempEntry.state = defId; + break; + default: + break; + } +} + +void AddAVA (char* userID) { + AVAEntry *newAVA; + + newAVA = (AVAEntry*)system_malloc_perm(sizeof(AVAEntry)); + if (!newAVA) { + yyerror ("Out of Memory in AddAVA"); + return; + } + *newAVA = tempEntry; + newAVA->userid = userID; + + _addAVAtoTable (newAVA, &entryTable); + + tempEntry.CNEntry = tempEntry.userid = tempEntry.country = tempEntry.company = 0; + tempEntry.email = tempEntry.locality = tempEntry.state = NULL; + tempEntry.numOrgs = 0; +} +yytabelem yyexca[] ={ +-1, 1, + 0, -1, + -2, 0, + }; +# define YYNPROD 18 +# define YYLAST 19 +yytabelem yyact[]={ + + 10, 11, 12, 13, 19, 4, 14, 15, 16, 18, + 8, 3, 7, 6, 5, 2, 1, 9, 17 }; +yytabelem yypact[]={ + + -261,-10000000, -261,-10000000, -257,-10000000,-10000000, -257,-10000000, -252, +-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000, -263,-10000000 }; +yytabelem yypgo[]={ + + 0, 17, 16, 15, 11, 13, 12, 10 }; +yytabelem yyr1[]={ + + 0, 2, 2, 3, 3, 4, 5, 5, 6, 6, + 7, 1, 1, 1, 1, 1, 1, 1 }; +yytabelem yyr2[]={ + + 0, 2, 0, 4, 2, 5, 2, 0, 4, 2, + 7, 3, 3, 3, 3, 3, 3, 3 }; +yytabelem yychk[]={ + +-10000000, -2, -3, -4, 266, -4, -5, -6, -7, -1, + 257, 258, 259, 260, 263, 264, 265, -7, 261, 267 }; +yytabelem yydef[]={ + + 2, -2, 1, 4, 7, 3, 5, 6, 9, 0, + 11, 12, 13, 14, 15, 16, 17, 8, 0, 10 }; +typedef struct +#ifdef __cplusplus + yytoktype +#endif +{ char *t_name; int t_val; } yytoktype; +#ifndef YYDEBUG +# define YYDEBUG 0 /* don't allow debugging */ +#endif + +#if YYDEBUG + +yytoktype yytoks[] = +{ + "DEF_C", 257, + "DEF_CO", 258, + "DEF_OU", 259, + "DEF_CN", 260, + "EQ_SIGN", 261, + "DEF_START", 262, + "DEF_L", 263, + "DEF_E", 264, + "DEF_ST", 265, + "USER_ID", 266, + "DEF_ID", 267, + "-unknown-", -1 /* ends search */ +}; + +char * yyreds[] = +{ + "-no such reduction-", + "source : ava.database", + "source : /* empty */", + "ava.database : ava.database ava", + "ava.database : ava", + "ava : USER_ID definitions", + "definitions : definition.list", + "definitions : /* empty */", + "definition.list : definition.list definition", + "definition.list : definition", + "definition : def.type EQ_SIGN DEF_ID", + "def.type : DEF_C", + "def.type : DEF_CO", + "def.type : DEF_OU", + "def.type : DEF_CN", + "def.type : DEF_L", + "def.type : DEF_E", + "def.type : DEF_ST", +}; +#endif /* YYDEBUG */ + + +/* +** Skeleton parser driver for yacc output +*/ + +/* +** yacc user known macros and defines +*/ +#define YYERROR goto yyerrlab +#define YYACCEPT return(0) +#define YYABORT return(1) +#define YYBACKUP( newtoken, newvalue )\ +{\ + if ( yychar >= 0 || ( yyr2[ yytmp ] >> 1 ) != 1 )\ + {\ + yyerror( "syntax error - cannot backup" );\ + goto yyerrlab;\ + }\ + yychar = newtoken;\ + yystate = *yyps;\ + yylval = newvalue;\ + goto yynewstate;\ +} +#define YYRECOVERING() (!!yyerrflag) +#define YYNEW(type) system_malloc(sizeof(type) * yynewmax) +#define YYCOPY(to, from, type) \ + (type *) memcpy(to, (char *) from, yynewmax * sizeof(type)) +#define YYENLARGE( from, type) \ + (type *) system_realloc((char *) from, yynewmax * sizeof(type)) +#ifndef YYDEBUG +# define YYDEBUG 1 /* make debugging available */ +#endif + +/* +** user known globals +*/ +int yydebug; /* set to 1 to get debugging */ + +/* +** driver internal defines +*/ +#define YYFLAG (-10000000) + +/* +** global variables used by the parser +*/ +YYSTYPE *yypv; /* top of value stack */ +int *yyps; /* top of state stack */ + +int yystate; /* current state */ +int yytmp; /* extra var (lasts between blocks) */ + +int yynerrs; /* number of errors */ +int yyerrflag; /* error recovery flag */ +int yychar; /* current input token number */ + + + +#ifdef YYNMBCHARS +#define YYLEX() yycvtok(yylex()) +/* +** yycvtok - return a token if i is a wchar_t value that exceeds 255. +** If i<255, i itself is the token. If i>255 but the neither +** of the 30th or 31st bit is on, i is already a token. +*/ +#if defined(__STDC__) || defined(__cplusplus) +int yycvtok(int i) +#else +int yycvtok(i) int i; +#endif +{ + int first = 0; + int last = YYNMBCHARS - 1; + int mid; + wchar_t j; + + if(i&0x60000000){/*Must convert to a token. */ + if( yymbchars[last].character < i ){ + return i;/*Giving up*/ + } + while ((last>=first)&&(first>=0)) {/*Binary search loop*/ + mid = (first+last)/2; + j = yymbchars[mid].character; + if( j==i ){/*Found*/ + return yymbchars[mid].tvalue; + }else if( j<i ){ + first = mid + 1; + }else{ + last = mid -1; + } + } + /*No entry in the table.*/ + return i;/* Giving up.*/ + }else{/* i is already a token. */ + return i; + } +} +#else/*!YYNMBCHARS*/ +#define YYLEX() yylex() +#endif/*!YYNMBCHARS*/ + +/* +** yyparse - return 0 if worked, 1 if syntax error not recovered from +*/ +#if defined(__STDC__) || defined(__cplusplus) +int yyparse(void) +#else +int yyparse() +#endif +{ + register YYSTYPE *yypvt; /* top of value stack for $vars */ + +#if defined(__cplusplus) || defined(lint) +/* + hacks to please C++ and lint - goto's inside switch should never be + executed; yypvt is set to 0 to avoid "used before set" warning. +*/ + static int __yaccpar_lint_hack__ = 0; + switch (__yaccpar_lint_hack__) + { + case 1: goto yyerrlab; + case 2: goto yynewstate; + } + yypvt = 0; +#endif + + /* + ** Initialize externals - yyparse may be called more than once + */ + yypv = &yyv[-1]; + yyps = &yys[-1]; + yystate = 0; + yytmp = 0; + yynerrs = 0; + yyerrflag = 0; + yychar = -1; + +#if YYMAXDEPTH <= 0 + if (yymaxdepth <= 0) + { + if ((yymaxdepth = YYEXPAND(0)) <= 0) + { + yyerror("yacc initialization error"); + YYABORT; + } + } +#endif + + { + register YYSTYPE *yy_pv; /* top of value stack */ + register int *yy_ps; /* top of state stack */ + register int yy_state; /* current state */ + register int yy_n; /* internal state number info */ + goto yystack; /* moved from 6 lines above to here to please C++ */ + + /* + ** get globals into registers. + ** branch to here only if YYBACKUP was called. + */ + yynewstate: + yy_pv = yypv; + yy_ps = yyps; + yy_state = yystate; + goto yy_newstate; + + /* + ** get globals into registers. + ** either we just started, or we just finished a reduction + */ + yystack: + yy_pv = yypv; + yy_ps = yyps; + yy_state = yystate; + + /* + ** top of for (;;) loop while no reductions done + */ + yy_stack: + /* + ** put a state and value onto the stacks + */ +#if YYDEBUG + /* + ** if debugging, look up token value in list of value vs. + ** name pairs. 0 and negative (-1) are special values. + ** Note: linear search is used since time is not a real + ** consideration while debugging. + */ + if ( yydebug ) + { + register int yy_i; + + printf( "State %d, token ", yy_state ); + if ( yychar == 0 ) + printf( "end-of-file\n" ); + else if ( yychar < 0 ) + printf( "-none-\n" ); + else + { + for ( yy_i = 0; yytoks[yy_i].t_val >= 0; + yy_i++ ) + { + if ( yytoks[yy_i].t_val == yychar ) + break; + } + printf( "%s\n", yytoks[yy_i].t_name ); + } + } +#endif /* YYDEBUG */ + if ( ++yy_ps >= &yys[ yymaxdepth ] ) /* room on stack? */ + { + /* + ** reallocate and recover. Note that pointers + ** have to be reset, or bad things will happen + */ + int yyps_index = (yy_ps - yys); + int yypv_index = (yy_pv - yyv); + int yypvt_index = (yypvt - yyv); + int yynewmax; +#ifdef YYEXPAND + yynewmax = YYEXPAND(yymaxdepth); +#else + yynewmax = 2 * yymaxdepth; /* double table size */ + if (yymaxdepth == YYMAXDEPTH) /* first time growth */ + { + char *newyys = (char *)YYNEW(int); + char *newyyv = (char *)YYNEW(YYSTYPE); + if (newyys != 0 && newyyv != 0) + { + yys = YYCOPY(newyys, yys, int); + yyv = YYCOPY(newyyv, yyv, YYSTYPE); + } + else + yynewmax = 0; /* failed */ + } + else /* not first time */ + { + yys = YYENLARGE(yys, int); + yyv = YYENLARGE(yyv, YYSTYPE); + if (yys == 0 || yyv == 0) + yynewmax = 0; /* failed */ + } +#endif + if (yynewmax <= yymaxdepth) /* tables not expanded */ + { + yyerror( "yacc stack overflow" ); + YYABORT; + } + yymaxdepth = yynewmax; + + yy_ps = yys + yyps_index; + yy_pv = yyv + yypv_index; + yypvt = yyv + yypvt_index; + } + *yy_ps = yy_state; + *++yy_pv = yyval; + + /* + ** we have a new state - find out what to do + */ + yy_newstate: + if ( ( yy_n = yypact[ yy_state ] ) <= YYFLAG ) + goto yydefault; /* simple state */ +#if YYDEBUG + /* + ** if debugging, need to mark whether new token grabbed + */ + yytmp = yychar < 0; +#endif + if ( ( yychar < 0 ) && ( ( yychar = YYLEX() ) < 0 ) ) + yychar = 0; /* reached EOF */ +#if YYDEBUG + if ( yydebug && yytmp ) + { + register int yy_i; + + printf( "Received token " ); + if ( yychar == 0 ) + printf( "end-of-file\n" ); + else if ( yychar < 0 ) + printf( "-none-\n" ); + else + { + for ( yy_i = 0; yytoks[yy_i].t_val >= 0; + yy_i++ ) + { + if ( yytoks[yy_i].t_val == yychar ) + break; + } + printf( "%s\n", yytoks[yy_i].t_name ); + } + } +#endif /* YYDEBUG */ + if ( ( ( yy_n += yychar ) < 0 ) || ( yy_n >= YYLAST ) ) + goto yydefault; + if ( yychk[ yy_n = yyact[ yy_n ] ] == yychar ) /*valid shift*/ + { + yychar = -1; + yyval = yylval; + yy_state = yy_n; + if ( yyerrflag > 0 ) + yyerrflag--; + goto yy_stack; + } + + yydefault: + if ( ( yy_n = yydef[ yy_state ] ) == -2 ) + { +#if YYDEBUG + yytmp = yychar < 0; +#endif + if ( ( yychar < 0 ) && ( ( yychar = YYLEX() ) < 0 ) ) + yychar = 0; /* reached EOF */ +#if YYDEBUG + if ( yydebug && yytmp ) + { + register int yy_i; + + printf( "Received token " ); + if ( yychar == 0 ) + printf( "end-of-file\n" ); + else if ( yychar < 0 ) + printf( "-none-\n" ); + else + { + for ( yy_i = 0; + yytoks[yy_i].t_val >= 0; + yy_i++ ) + { + if ( yytoks[yy_i].t_val + == yychar ) + { + break; + } + } + printf( "%s\n", yytoks[yy_i].t_name ); + } + } +#endif /* YYDEBUG */ + /* + ** look through exception table + */ + { + register int *yyxi = yyexca; + + while ( ( *yyxi != -1 ) || + ( yyxi[1] != yy_state ) ) + { + yyxi += 2; + } + while ( ( *(yyxi += 2) >= 0 ) && + ( *yyxi != yychar ) ) + ; + if ( ( yy_n = yyxi[1] ) < 0 ) + YYACCEPT; + } + } + + /* + ** check for syntax error + */ + if ( yy_n == 0 ) /* have an error */ + { + /* no worry about speed here! */ + switch ( yyerrflag ) + { + case 0: /* new error */ + yyerror( "syntax error" ); + goto skip_init; + yyerrlab: + /* + ** get globals into registers. + ** we have a user generated syntax type error + */ + yy_pv = yypv; + yy_ps = yyps; + yy_state = yystate; + skip_init: + yynerrs++; + /* FALLTHRU */ + case 1: + case 2: /* incompletely recovered error */ + /* try again... */ + yyerrflag = 3; + /* + ** find state where "error" is a legal + ** shift action + */ + while ( yy_ps >= yys ) + { + yy_n = yypact[ *yy_ps ] + YYERRCODE; + if ( yy_n >= 0 && yy_n < YYLAST && + yychk[yyact[yy_n]] == YYERRCODE) { + /* + ** simulate shift of "error" + */ + yy_state = yyact[ yy_n ]; + goto yy_stack; + } + /* + ** current state has no shift on + ** "error", pop stack + */ +#if YYDEBUG +# define _POP_ "Error recovery pops state %d, uncovers state %d\n" + if ( yydebug ) + printf( _POP_, *yy_ps, + yy_ps[-1] ); +# undef _POP_ +#endif + yy_ps--; + yy_pv--; + } + /* + ** there is no state on stack with "error" as + ** a valid shift. give up. + */ + YYABORT; + case 3: /* no shift yet; eat a token */ +#if YYDEBUG + /* + ** if debugging, look up token in list of + ** pairs. 0 and negative shouldn't occur, + ** but since timing doesn't matter when + ** debugging, it doesn't hurt to leave the + ** tests here. + */ + if ( yydebug ) + { + register int yy_i; + + printf( "Error recovery discards " ); + if ( yychar == 0 ) + printf( "token end-of-file\n" ); + else if ( yychar < 0 ) + printf( "token -none-\n" ); + else + { + for ( yy_i = 0; + yytoks[yy_i].t_val >= 0; + yy_i++ ) + { + if ( yytoks[yy_i].t_val + == yychar ) + { + break; + } + } + printf( "token %s\n", + yytoks[yy_i].t_name ); + } + } +#endif /* YYDEBUG */ + if ( yychar == 0 ) /* reached EOF. quit */ + YYABORT; + yychar = -1; + goto yy_newstate; + } + }/* end if ( yy_n == 0 ) */ + /* + ** reduction by production yy_n + ** put stack tops, etc. so things right after switch + */ +#if YYDEBUG + /* + ** if debugging, print the string that is the user's + ** specification of the reduction which is just about + ** to be done. + */ + if ( yydebug ) + printf( "Reduce by (%d) \"%s\"\n", + yy_n, yyreds[ yy_n ] ); +#endif + yytmp = yy_n; /* value to switch over */ + yypvt = yy_pv; /* $vars top of value stack */ + /* + ** Look in goto table for next state + ** Sorry about using yy_state here as temporary + ** register variable, but why not, if it works... + ** If yyr2[ yy_n ] doesn't have the low order bit + ** set, then there is no action to be done for + ** this reduction. So, no saving & unsaving of + ** registers done. The only difference between the + ** code just after the if and the body of the if is + ** the goto yy_stack in the body. This way the test + ** can be made before the choice of what to do is needed. + */ + { + /* length of production doubled with extra bit */ + register int yy_len = yyr2[ yy_n ]; + + if ( !( yy_len & 01 ) ) + { + yy_len >>= 1; + yyval = ( yy_pv -= yy_len )[1]; /* $$ = $1 */ + yy_state = yypgo[ yy_n = yyr1[ yy_n ] ] + + *( yy_ps -= yy_len ) + 1; + if ( yy_state >= YYLAST || + yychk[ yy_state = + yyact[ yy_state ] ] != -yy_n ) + { + yy_state = yyact[ yypgo[ yy_n ] ]; + } + goto yy_stack; + } + yy_len >>= 1; + yyval = ( yy_pv -= yy_len )[1]; /* $$ = $1 */ + yy_state = yypgo[ yy_n = yyr1[ yy_n ] ] + + *( yy_ps -= yy_len ) + 1; + if ( yy_state >= YYLAST || + yychk[ yy_state = yyact[ yy_state ] ] != -yy_n ) + { + yy_state = yyact[ yypgo[ yy_n ] ]; + } + } + /* save until reenter driver code */ + yystate = yy_state; + yyps = yy_ps; + yypv = yy_pv; + } + /* + ** code supplied by user is placed in this switch + */ + switch( yytmp ) + { + +case 5: +{AddAVA(yypvt[-1].string);} break; +case 10: +{AddDefType(yypvt[-2].num, yypvt[-0].string);} break; +case 11: +{yyval.num = DEF_C; } break; +case 12: +{yyval.num = DEF_CO;} break; +case 13: +{yyval.num = DEF_OU;} break; +case 14: +{yyval.num = DEF_CN;} break; +case 15: +{yyval.num = DEF_L; } break; +case 16: +{yyval.num = DEF_E; } break; +case 17: +{yyval.num = DEF_ST;} break; + } + goto yystack; /* reset registers in driver code */ +} + diff --git a/lib/libaccess/wintab.h b/lib/libaccess/wintab.h new file mode 100644 index 00000000..b764cf78 --- /dev/null +++ b/lib/libaccess/wintab.h @@ -0,0 +1,26 @@ +/** BEGIN COPYRIGHT BLOCK + * Copyright 2001 Sun Microsystems, Inc. + * Portions copyright 1999, 2001-2003 Netscape Communications Corporation. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +typedef union +#ifdef __cplusplus + YYSTYPE +#endif + { + char *string; + int num; +} YYSTYPE; +extern YYSTYPE yylval; +# define DEF_C 257 +# define DEF_CO 258 +# define DEF_OU 259 +# define DEF_CN 260 +# define EQ_SIGN 261 +# define DEF_START 262 +# define DEF_L 263 +# define DEF_E 264 +# define DEF_ST 265 +# define USER_ID 266 +# define DEF_ID 267 diff --git a/lib/libaccess/yy-sed b/lib/libaccess/yy-sed new file mode 100644 index 00000000..24c4eaac --- /dev/null +++ b/lib/libaccess/yy-sed @@ -0,0 +1,21 @@ +# +# BEGIN COPYRIGHT BLOCK +# Copyright 2001 Sun Microsystems, Inc. +# Portions copyright 1999, 2001-2003 Netscape Communications Corporation. +# All rights reserved. +# END COPYRIGHT BLOCK +# +/#include <unistd.h>/d +/#include <values.h>/d +/#pragma ident/d +s/malloc.h/netsite.h/g +s/yyparse/acl_Parse/g +s/yy_flex_alloc/ACL_FLEX_ALLOC/g +s/yy_flex_realloc/ACL_FLEX_REALLOC/g +s/yy_flex_free/ACL_FLEX_FREE/g +s/malloc(/PERM_MALLOC(/g +s/free(/PERM_FREE(/g +s/realloc(/PERM_REALLOC(/g +s/yy/acl/g +s/YY/ACL/g +s/lex.acl.c/acl.yy.cpp/g |