diff options
author | Martin Hedenfal <martinh@cvs.openbsd.org> | 2010-06-29 02:45:47 +0000 |
---|---|---|
committer | Martin Hedenfal <martinh@cvs.openbsd.org> | 2010-06-29 02:45:47 +0000 |
commit | 166c6674ba30863af6aaed61d390552332f569a7 (patch) | |
tree | 8e6194d97affdda4e666af1071456579edf64caa /usr.sbin/ldapd | |
parent | 6c3eb79a9477bb18f4d31b8751097558c1b078f2 (diff) |
Rewrite the schema parser, as it's not a context-free grammar.
This also brings the config parser more in line with other parse.y in the
tree. The new schema parser also supports symbolic OID names.
You need to update your /etc/ldapd.conf. Schema files are no longer
included with the 'include' keyword, you have to use 'schema' for that.
Moves schema-related structures to a separate include file to ease reuse.
Diffstat (limited to 'usr.sbin/ldapd')
-rw-r--r-- | usr.sbin/ldapd/Makefile | 6 | ||||
-rw-r--r-- | usr.sbin/ldapd/ldapd.h | 92 | ||||
-rw-r--r-- | usr.sbin/ldapd/modify.c | 6 | ||||
-rw-r--r-- | usr.sbin/ldapd/parse.y | 539 | ||||
-rw-r--r-- | usr.sbin/ldapd/schema.c | 925 | ||||
-rw-r--r-- | usr.sbin/ldapd/schema.h | 139 | ||||
-rw-r--r-- | usr.sbin/ldapd/search.c | 4 | ||||
-rw-r--r-- | usr.sbin/ldapd/validate.c | 8 |
8 files changed, 1141 insertions, 578 deletions
diff --git a/usr.sbin/ldapd/Makefile b/usr.sbin/ldapd/Makefile index 661d911b637..7a51c9e066d 100644 --- a/usr.sbin/ldapd/Makefile +++ b/usr.sbin/ldapd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.5 2010/06/23 12:40:19 martinh Exp $ +# $OpenBSD: Makefile,v 1.6 2010/06/29 02:45:46 martinh Exp $ PROG= ldapd MAN= ldapd.8 ldapd.conf.5 @@ -6,11 +6,11 @@ SRCS= ber.c log.c control.c \ util.c ldapd.c ldape.c conn.c attributes.c namespace.c \ btree.c filter.c search.c parse.y \ auth.c modify.c index.c ssl.c ssl_privsep.c \ - validate.c uuid.c + validate.c uuid.c schema.c LDADD= -levent -lssl -lcrypto -lz -lutil DPADD= ${LIBEVENT} ${LIBCRYPTO} ${LIBSSL} ${LIBZ} ${LIBUTIL} -CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR} -g CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual diff --git a/usr.sbin/ldapd/ldapd.h b/usr.sbin/ldapd/ldapd.h index 4b12b67f46d..29cf70ce06e 100644 --- a/usr.sbin/ldapd/ldapd.h +++ b/usr.sbin/ldapd/ldapd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ldapd.h,v 1.11 2010/06/27 18:31:12 martinh Exp $ */ +/* $OpenBSD: ldapd.h,v 1.12 2010/06/29 02:45:46 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -33,6 +33,7 @@ #include <stdarg.h> #include "aldap.h" +#include "schema.h" #include "btree.h" #define CONFFILE "/etc/ldapd.conf" @@ -214,84 +215,6 @@ struct conn }; TAILQ_HEAD(conn_list, conn) conn_list; -enum usage { - USAGE_USER_APP, - USAGE_DIR_OP, /* operational attribute */ - USAGE_DIST_OP, /* operational attribute */ - USAGE_DSA_OP /* operational attribute */ -}; - -struct name { - SLIST_ENTRY(name) next; - const char *name; -}; -SLIST_HEAD(name_list, name); - -struct attr_type { - RB_ENTRY(attr_type) link; - const char *oid; - struct name_list *names; - char *desc; - int obsolete; - struct attr_type *sup; - char *equality; - char *ordering; - char *substr; - char *syntax; - int single; - int collective; - int immutable; /* no-user-modification */ - enum usage usage; -}; -RB_HEAD(attr_type_tree, attr_type); -RB_PROTOTYPE(attr_type_tree, attr_type, link, attr_oid_cmp); - -struct attr_ptr { - SLIST_ENTRY(attr_ptr) next; - struct attr_type *attr_type; -}; -SLIST_HEAD(attr_list, attr_ptr); - -enum object_kind { - KIND_ABSTRACT, - KIND_STRUCTURAL, - KIND_AUXILIARY -}; - -struct object; -struct obj_ptr { - SLIST_ENTRY(obj_ptr) next; - struct object *object; -}; -SLIST_HEAD(obj_list, obj_ptr); - -struct object { - RB_ENTRY(object) link; - const char *oid; - struct name_list *names; - char *desc; - int obsolete; - struct obj_list *sup; - enum object_kind kind; - struct attr_list *must; - struct attr_list *may; -}; -RB_HEAD(object_tree, object); -RB_PROTOTYPE(object_tree, object, link, obj_oid_cmp); - -struct oidname { - RB_ENTRY(oidname) link; - const char *on_name; -#define on_attr_type on_ptr.ou_attr_type -#define on_object on_ptr.ou_object - union { - struct attr_type *ou_attr_type; - struct object *ou_object; - } on_ptr; -}; -RB_HEAD(oidname_tree, oidname); -RB_PROTOTYPE(oidname_tree, oidname, link, oidname_cmp); - struct ssl { SPLAY_ENTRY(ssl) ssl_nodes; char ssl_name[PATH_MAX]; @@ -305,13 +228,10 @@ struct ssl { struct ldapd_config { struct namespace_list namespaces; - struct attr_type_tree attr_types; - struct oidname_tree attr_names; - struct object_tree objects; - struct oidname_tree object_names; struct listenerlist listeners; SPLAY_HEAD(ssltree, ssl) *sc_ssl; struct acl acl; + struct schema *schema; }; struct ldapd_stats @@ -516,12 +436,6 @@ int authorized(struct conn *conn, struct namespace *ns, /* parse.y */ int parse_config(char *filename); -struct attr_type *lookup_attribute_by_oid(const char *oid); -struct attr_type *lookup_attribute_by_name(const char *name); -struct attr_type *lookup_attribute(const char *oid_or_name); -struct object *lookup_object_by_oid(const char *oid); -struct object *lookup_object_by_name(const char *name); -struct object *lookup_object(const char *oid_or_name); int cmdline_symset(char *s); /* log.c */ diff --git a/usr.sbin/ldapd/modify.c b/usr.sbin/ldapd/modify.c index b2a25d8ff1a..79921dd14c2 100644 --- a/usr.sbin/ldapd/modify.c +++ b/usr.sbin/ldapd/modify.c @@ -1,4 +1,4 @@ -/* $OpenBSD: modify.c,v 1.3 2010/06/23 13:10:14 martinh Exp $ */ +/* $OpenBSD: modify.c,v 1.4 2010/06/29 02:45:46 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -139,7 +139,7 @@ ldap_modify(struct request *req) int rc; char *dn; long long op; - const char *attr; + char *attr; struct ber_element *mods, *entry, *mod, *vals, *a, *set; struct namespace *ns; struct attr_type *at; @@ -181,7 +181,7 @@ ldap_modify(struct request *req) goto done; } - if ((at = lookup_attribute(attr)) == NULL && !ns->relax) { + if ((at = lookup_attribute(conf->schema, attr)) == NULL && !ns->relax) { log_debug("unknown attribute type %s", attr); rc = LDAP_NO_SUCH_ATTRIBUTE; goto done; diff --git a/usr.sbin/ldapd/parse.y b/usr.sbin/ldapd/parse.y index 480b4d4be2f..09b8d87c3d4 100644 --- a/usr.sbin/ldapd/parse.y +++ b/usr.sbin/ldapd/parse.y @@ -1,7 +1,7 @@ -/* $OpenBSD: parse.y,v 1.4 2010/06/15 19:30:26 martinh Exp $ */ +/* $OpenBSD: parse.y,v 1.5 2010/06/29 02:45:46 martinh Exp $ */ /* - * Copyright (c) 2009 Martin Hedenfalk <martin@bzero.se> + * Copyright (c) 2009, 2010 Martin Hedenfalk <martinh@openbsd.org> * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -46,28 +46,6 @@ #include "ldapd.h" -static int -attr_oid_cmp(struct attr_type *a, struct attr_type *b) -{ - return strcasecmp(a->oid, b->oid); -} - -static int -obj_oid_cmp(struct object *a, struct object *b) -{ - return strcasecmp(a->oid, b->oid); -} - -static int -oidname_cmp(struct oidname *a, struct oidname *b) -{ - return strcasecmp(a->on_name, b->on_name); -} - -RB_GENERATE(attr_type_tree, attr_type, link, attr_oid_cmp); -RB_GENERATE(object_tree, object, link, obj_oid_cmp); -RB_GENERATE(oidname_tree, oidname, link, oidname_cmp); - TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); static struct file { TAILQ_ENTRY(file) entry; @@ -111,77 +89,48 @@ char *symget(const char *); struct ldapd_config *conf; -static struct attr_list *append_attr(struct attr_list *alist, - struct attr_type *a); -static struct obj_list *append_obj(struct obj_list *olist, struct object *obj); -int is_oidstr(const char *oidstr); -static struct name_list *append_name(struct name_list *nl, const char *name); static struct aci *mk_aci(int type, int rights, enum scope scope, char *target, char *subject); typedef struct { union { - int64_t number; - char *string; - struct attr_type *attr; - struct attr_list *attrlist; - struct object *obj; - struct obj_list *objlist; - struct name_list *namelist; - struct { - int type; - void *data; - long long value; - } data; - struct aci *aci; + int64_t number; + char *string; + struct aci *aci; } v; int lineno; } YYSTYPE; -static struct attr_type *cattr = NULL; -static struct object *cobj = NULL; -static struct namespace *cns = NULL; +static struct namespace *current_ns = NULL; %} %token ERROR LISTEN ON TLS LDAPS PORT NAMESPACE ROOTDN ROOTPW INDEX -%token SUP SYNTAX EQUALITY ORDERING SUBSTR OBSOLETE NAME SINGLE_VALUE -%token DESC USAGE ATTRIBUTETYPE NO_USER_MOD COLLECTIVE +%token SECURE RELAX STRICT SCHEMA USE COMPRESSION LEVEL %token INCLUDE CERTIFICATE FSYNC CACHE_SIZE INDEX_CACHE_SIZE -%token OBJECTCLASS MUST MAY SECURE RELAX STRICT SCHEMA -%token ABSTRACT AUXILIARY STRUCTURAL USE COMPRESSION LEVEL -%token USER_APPLICATIONS DIRECTORY_OPERATION %token DISTRIBUTED_OPERATION DSA_OPERATION %token DENY ALLOW READ WRITE BIND ACCESS TO ROOT %token ANY CHILDREN OF ATTRIBUTE IN SUBTREE BY SELF %token <v.string> STRING %token <v.number> NUMBER -%type <v.string> numericoid oidlen -%type <v.string> qdescr certname -%type <v.number> usage kind port ssl boolean -%type <v.attrlist> attrptrs attrlist -%type <v.attr> attrptr -%type <v.objlist> objptrs objlist -%type <v.obj> objptr -%type <v.namelist> qdescrs qdescrlist +%type <v.number> port ssl boolean comp_level %type <v.number> aci_type aci_access aci_rights aci_right aci_scope -%type <v.string> aci_target aci_subject +%type <v.string> aci_target aci_subject certname %type <v.aci> aci -%type <v.number> comp_level %% grammar : /* empty */ - | grammar include - | grammar attr_type - | grammar object - | grammar varset - | grammar conf_main - | grammar database - | grammar aci { + | grammar '\n' + | grammar include '\n' + | grammar varset '\n' + | grammar conf_main '\n' + | grammar error '\n' { file->errors++; } + | grammar namespace '\n' + | grammar aci '\n' { SIMPLEQ_INSERT_TAIL(&conf->acl, $2, entry); } - | grammar error { file->errors++; } + | grammar schema '\n' ; ssl : /* empty */ { $$ = 0; } @@ -253,13 +202,12 @@ conf_main : LISTEN ON STRING port ssl certname { } ; -database : NAMESPACE STRING '{' { - cns = namespace_new($2); +namespace : NAMESPACE STRING '{' '\n' { + log_debug("parsing namespace %s", $2); + current_ns = namespace_new($2); free($2); - TAILQ_INSERT_TAIL(&conf->namespaces, cns, next); - } ns_opts '}' { - cns = NULL; - } + TAILQ_INSERT_TAIL(&conf->namespaces, current_ns, next); + } ns_opts '}' { current_ns = NULL; } ; boolean : STRING { @@ -281,43 +229,35 @@ boolean : STRING { ; ns_opts : /* empty */ - | ns_opts ROOTDN STRING { - cns->rootdn = $3; - normalize_dn(cns->rootdn); + | ns_opts '\n' + | ns_opts ns_opt '\n' + ; + +ns_opt : ROOTDN STRING { + current_ns->rootdn = $2; + normalize_dn(current_ns->rootdn); } - | ns_opts ROOTPW STRING { cns->rootpw = $3; } - | ns_opts INDEX STRING { + | ROOTPW STRING { current_ns->rootpw = $2; } + | INDEX STRING { struct attr_index *ai; if ((ai = calloc(1, sizeof(*ai))) == NULL) { yyerror("calloc"); - free($3); + free($2); YYERROR; } - ai->attr = $3; + ai->attr = $2; ai->type = INDEX_EQUAL; - TAILQ_INSERT_TAIL(&cns->indices, ai, next); - } - | ns_opts CACHE_SIZE NUMBER { - cns->cache_size = $3; - } - | ns_opts INDEX_CACHE_SIZE NUMBER { - cns->index_cache_size = $3; - } - | ns_opts FSYNC boolean { - cns->sync = $3; - } - | ns_opts aci { - SIMPLEQ_INSERT_TAIL(&cns->acl, $2, entry); - } - | ns_opts RELAX SCHEMA { - cns->relax = 1; + TAILQ_INSERT_TAIL(¤t_ns->indices, ai, next); } - | ns_opts STRICT SCHEMA { - cns->relax = 0; - } - | ns_opts USE COMPRESSION comp_level { - cns->compression_level = $4; + | CACHE_SIZE NUMBER { current_ns->cache_size = $2; } + | INDEX_CACHE_SIZE NUMBER { current_ns->index_cache_size = $2; } + | FSYNC boolean { current_ns->sync = $2; } + | aci { + SIMPLEQ_INSERT_TAIL(¤t_ns->acl, $1, entry); } + | RELAX SCHEMA { current_ns->relax = 1; } + | STRICT SCHEMA { current_ns->relax = 0; } + | USE COMPRESSION comp_level { current_ns->compression_level = $3; } ; comp_level : /* empty */ { $$ = 6; } @@ -374,209 +314,6 @@ aci_subject : /* empty */ { $$ = NULL; } | BY SELF { $$ = strdup("@"); } ; -attr_type : ATTRIBUTETYPE '(' numericoid { - struct attr_type *p; - - if ((cattr = calloc(1, sizeof(*cattr))) == NULL) { - yyerror("calloc"); - free($3); - YYERROR; - } - cattr->oid = $3; - p = RB_INSERT(attr_type_tree, &conf->attr_types, cattr); - if (p != NULL) { - yyerror("attribute '%s' already defined", $3); - free($3); - free(cattr); - cattr = NULL; - YYERROR; - } - } attr_data ')' { - cattr = NULL; - } - ; - -attr_data : /* empty */ - | attr_data NAME qdescrs { cattr->names = $3; } - | attr_data DESC STRING { cattr->desc = $3; } - | attr_data OBSOLETE { cattr->obsolete = 1; } - | attr_data SUP STRING { - cattr->sup = lookup_attribute($3); - if (cattr->sup == NULL) - yyerror("%s: no such attribute", $3); - free($3); - if (cattr->sup == NULL) - YYERROR; - } - | attr_data EQUALITY STRING { cattr->equality = $3; } - | attr_data ORDERING STRING { cattr->ordering = $3; } - | attr_data SUBSTR STRING { cattr->substr = $3; } - | attr_data SYNTAX oidlen { cattr->syntax = $3; } - | attr_data SINGLE_VALUE { cattr->single = 1; } - | attr_data COLLECTIVE { cattr->collective = 1; } - | attr_data NO_USER_MOD { cattr->immutable = 1; } - | attr_data USAGE usage { cattr->usage = $3; } - ; - -usage : USER_APPLICATIONS { $$ = USAGE_USER_APP; } - | DIRECTORY_OPERATION { $$ = USAGE_DIR_OP; } - | DISTRIBUTED_OPERATION { $$ = USAGE_DIST_OP; } - | DSA_OPERATION { $$ = USAGE_DSA_OP; } - ; - -object : OBJECTCLASS '(' numericoid { - struct object *p; - - if ((cobj = calloc(1, sizeof(*cobj))) == NULL) { - yyerror("calloc"); - free($3); - YYERROR; - } - cobj->oid = $3; - p = RB_INSERT(object_tree, &conf->objects, cobj); - if (p != NULL) { - yyerror("object '%s' already defined", $3); - free($3); - free(cobj); - cobj = NULL; - YYERROR; - } - } obj_data ')' { - cobj = NULL; - } - ; - -obj_data : /* empty */ - | obj_data NAME qdescrs { cobj->names = $3; } - | obj_data DESC STRING { cobj->desc = $3; } - | obj_data OBSOLETE { cobj->obsolete = 1; } - | obj_data SUP objlist { cobj->sup = $3; } - | obj_data kind { cobj->kind = $2; } - | obj_data MUST attrlist { cobj->must = $3; } - | obj_data MAY attrlist { cobj->may = $3; } - ; - -attrptr : STRING { - $$ = lookup_attribute($1); - if ($$ == NULL) { - yyerror("undeclared attribute '%s'", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -attrptrs : attrptr { - if (($$ = append_attr(NULL, $1)) == NULL) - YYERROR; - } - | attrptrs '$' attrptr { - if (($$ = append_attr($1, $3)) == NULL) - YYERROR; - } - ; - -attrlist : attrptr { - if (($$ = append_attr(NULL, $1)) == NULL) - YYERROR; - } - | '(' attrptrs ')' { $$ = $2; } - ; - -objptr : STRING { - $$ = lookup_object($1); - if ($$ == NULL) { - yyerror("undeclared object class '%s'", $1); - free($1); - YYERROR; - } - free($1); - } - ; - -objptrs : objptr { - if (($$ = append_obj(NULL, $1)) == NULL) - YYERROR; - } - | objptrs '$' objptr { - if (($$ = append_obj($1, $3)) == NULL) - YYERROR; - } - ; - -objlist : objptr { - if (($$ = append_obj(NULL, $1)) == NULL) - YYERROR; - } - | '(' objptrs ')' { $$ = $2; } - ; - -kind : ABSTRACT { $$ = KIND_ABSTRACT; } - | STRUCTURAL { $$ = KIND_STRUCTURAL; } - | AUXILIARY { $$ = KIND_AUXILIARY; } - ; - -qdescr : STRING { - struct oidname *on, *old; - - if ((on = calloc(1, sizeof(*on))) == NULL) { - yyerror("calloc"); - free($1); - YYERROR; - } - on->on_name = $1; - if (cattr) { - on->on_attr_type = cattr; - old = RB_INSERT(oidname_tree, &conf->attr_names, - on); - if (old != NULL) { - yyerror("attribute name '%s' already" - " defined for oid %s", - $1, old->on_attr_type->oid); - free($1); - free(on); - YYERROR; - } - } else { - on->on_object = cobj; - old = RB_INSERT(oidname_tree, - &conf->object_names, on); - if (old != NULL) { - yyerror("object name '%s' already" - " defined for oid %s", - $1, old->on_object->oid); - free($1); - free(on); - YYERROR; - } - } - } - ; - -qdescrs : qdescr { $$ = append_name(NULL, $1); } - | '(' qdescrlist ')' { $$ = $2; } - ; - -qdescrlist : /* empty */ { $$ = NULL; } - | qdescrlist qdescr { $$ = append_name($1, $2); } - ; - -numericoid : STRING { - if (!is_oidstr($1)) { - yyerror("invalid OID: %s", $1); - free($1); - YYERROR; - } - $$ = $1; - } - ; - -oidlen : STRING { $$ = $1; } - | numericoid '{' NUMBER '}' { - $$ = $1; - } - include : INCLUDE STRING { struct file *nfile; @@ -588,18 +325,29 @@ include : INCLUDE STRING { free($2); file = nfile; + lungetc('\n'); } ; varset : STRING '=' STRING { - /*if (conf->opts & BGPD_OPT_VERBOSE)*/ - printf("%s = \"%s\"\n", $1, $3); if (symset($1, $3, 0) == -1) fatal("cannot store variable"); free($1); free($3); } ; + +schema : SCHEMA STRING { + int ret; + + ret = schema_parse(conf->schema, $2); + free($2); + if (ret != 0) { + YYERROR; + } + } + ; + %% struct keywords { @@ -634,37 +382,16 @@ lookup(char *s) { /* this has to be sorted always */ static const struct keywords keywords[] = { - { "ABSTRACT", ABSTRACT }, - { "AUXILIARY", AUXILIARY }, - { "COLLECTIVE", COLLECTIVE }, - { "DESC", DESC }, - { "EQUALITY", EQUALITY }, - { "MAY", MAY }, - { "MUST", MUST }, - { "NAME", NAME }, - { "NO-USER-MODIFICATION", NO_USER_MOD}, - { "ORDERING", ORDERING }, - { "SINGLE-VALUE", SINGLE_VALUE }, - { "STRUCTURAL", STRUCTURAL }, - { "SUBSTR", SUBSTR }, - { "SUP", SUP }, - { "SYNTAX", SYNTAX }, - { "USAGE", USAGE }, { "access", ACCESS }, { "allow", ALLOW }, { "any", ANY }, - { "attribute", ATTRIBUTE }, - { "attributetype", ATTRIBUTETYPE }, { "bind", BIND }, { "by", BY }, { "cache-size", CACHE_SIZE }, { "certificate", CERTIFICATE }, { "children", CHILDREN }, { "compression", COMPRESSION }, - { "dSAOperation", DSA_OPERATION }, { "deny", DENY }, - { "directoryOperation", DIRECTORY_OPERATION }, - { "distributedOperation", DISTRIBUTED_OPERATION }, { "fsync", FSYNC }, { "in", IN }, { "include", INCLUDE }, @@ -674,7 +401,6 @@ lookup(char *s) { "level", LEVEL }, { "listen", LISTEN }, { "namespace", NAMESPACE }, - { "objectclass", OBJECTCLASS }, { "of", OF }, { "on", ON }, { "port", PORT }, @@ -691,7 +417,6 @@ lookup(char *s) { "tls", TLS }, { "to", TO }, { "use", USE }, - { "userApplications", USER_APPLICATIONS }, { "write", WRITE }, }; @@ -803,7 +528,7 @@ findeol(void) int yylex(void) { - char buf[8096]; + char buf[4096]; char *p, *val; int quotec, next, c; int token; @@ -834,8 +559,6 @@ top: lungetc(c); break; } - if (*buf == '\0') - return ('$'); val = symget(buf); if (val == NULL) { yyerror("macro '%s' not defined", buf); @@ -870,7 +593,7 @@ top: break; } if (p + 1 >= buf + sizeof(buf) - 1) { - yyerror("string too long"); + log_warnx("string too long"); return (findeol()); } *p++ = (char)c; @@ -941,7 +664,6 @@ nodigits: if (c == '\n') { yylval.lineno = file->lineno; file->lineno++; - goto top; } if (c == EOF) return (0); @@ -990,7 +712,6 @@ pushfile(const char *name, int secret) free(nfile); return (NULL); } -#if 0 if (secret && check_file_secrecy(fileno(nfile->stream), nfile->name)) { fclose(nfile->stream); @@ -998,7 +719,6 @@ pushfile(const char *name, int secret) free(nfile); return (NULL); } -#endif nfile->lineno = 1; TAILQ_INSERT_TAIL(&files, nfile, entry); return (nfile); @@ -1029,8 +749,10 @@ parse_config(char *filename) if ((conf = calloc(1, sizeof(struct ldapd_config))) == NULL) fatal(NULL); - RB_INIT(&conf->attr_types); - RB_INIT(&conf->objects); + conf->schema = schema_new(); + if (conf->schema == NULL) + fatal("schema_new"); + TAILQ_INIT(&conf->namespaces); TAILQ_INIT(&conf->listeners); if ((conf->sc_ssl = calloc(1, sizeof(*conf->sc_ssl))) == NULL) @@ -1051,9 +773,7 @@ parse_config(char *filename) /* Free macros and check which have not been used. */ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { next = TAILQ_NEXT(sym, entry); - if (/*(conf->opts & BGPD_OPT_VERBOSE2) &&*/ !sym->used) - fprintf(stderr, "warning: macro \"%s\" not " - "used\n", sym->nam); + log_debug("warning: macro \"%s\" not used", sym->nam); if (!sym->persist) { free(sym->nam); free(sym->val); @@ -1139,141 +859,6 @@ symget(const char *nam) return (NULL); } -struct attr_type * -lookup_attribute_by_name(const char *name) -{ - struct oidname *on, find; - - find.on_name = name; - on = RB_FIND(oidname_tree, &conf->attr_names, &find); - - if (on) - return on->on_attr_type; - return NULL; -} - -struct attr_type * -lookup_attribute_by_oid(const char *oid) -{ - struct attr_type find; - - find.oid = oid; - return RB_FIND(attr_type_tree, &conf->attr_types, &find); -} - -struct attr_type * -lookup_attribute(const char *oid_or_name) -{ - if (is_oidstr(oid_or_name)) - return lookup_attribute_by_oid(oid_or_name); - return lookup_attribute_by_name(oid_or_name); -} - -struct object * -lookup_object_by_oid(const char *oid) -{ - struct object find; - - find.oid = oid; - return RB_FIND(object_tree, &conf->objects, &find); -} - -struct object * -lookup_object_by_name(const char *name) -{ - struct oidname *on, find; - - find.on_name = name; - on = RB_FIND(oidname_tree, &conf->object_names, &find); - - if (on) - return on->on_object; - return NULL; -} - -struct object * -lookup_object(const char *oid_or_name) -{ - if (is_oidstr(oid_or_name)) - return lookup_object_by_oid(oid_or_name); - return lookup_object_by_name(oid_or_name); -} - -static struct attr_list * -append_attr(struct attr_list *alist, struct attr_type *a) -{ - struct attr_ptr *aptr; - - if (alist == NULL) { - if ((alist = calloc(1, sizeof(*alist))) == NULL) { - yyerror("calloc"); - return NULL; - } - SLIST_INIT(alist); - } - - if ((aptr = calloc(1, sizeof(*aptr))) == NULL) { - yyerror("calloc"); - return NULL; - } - aptr->attr_type = a; - SLIST_INSERT_HEAD(alist, aptr, next); - - return alist; -} - -static struct obj_list * -append_obj(struct obj_list *olist, struct object *obj) -{ - struct obj_ptr *optr; - - if (olist == NULL) { - if ((olist = calloc(1, sizeof(*olist))) == NULL) { - yyerror("calloc"); - return NULL; - } - SLIST_INIT(olist); - } - - if ((optr = calloc(1, sizeof(*optr))) == NULL) { - yyerror("calloc"); - return NULL; - } - optr->object = obj; - SLIST_INSERT_HEAD(olist, optr, next); - - return olist; -} - -int -is_oidstr(const char *oidstr) -{ - struct ber_oid oid; - return (ber_string2oid(oidstr, &oid) == 0); -} - -static struct name_list * -append_name(struct name_list *nl, const char *name) -{ - struct name *n; - - if (nl == NULL) { - if ((nl = calloc(1, sizeof(*nl))) == NULL) { - yyerror("calloc"); - return NULL; - } - SLIST_INIT(nl); - } - if ((n = calloc(1, sizeof(*n))) == NULL) { - yyerror("calloc"); - return NULL; - } - n->name = name; - SLIST_INSERT_HEAD(nl, n, next); - - return nl; -} - struct listener * host_unix(const char *path) { diff --git a/usr.sbin/ldapd/schema.c b/usr.sbin/ldapd/schema.c new file mode 100644 index 00000000000..4d2d79a249a --- /dev/null +++ b/usr.sbin/ldapd/schema.c @@ -0,0 +1,925 @@ +/* $OpenBSD: schema.c,v 1.1 2010/06/29 02:45:46 martinh Exp $ */ + +/* + * Copyright (c) 2010 Martin Hedenfalk <martinh@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "ldapd.h" + +#define ERROR -1 +#define STRING 1 + +static int +attr_oid_cmp(struct attr_type *a, struct attr_type *b) +{ + return strcasecmp(a->oid, b->oid); +} + +static int +obj_oid_cmp(struct object *a, struct object *b) +{ + return strcasecmp(a->oid, b->oid); +} + +static int +oidname_cmp(struct oidname *a, struct oidname *b) +{ + return strcasecmp(a->on_name, b->on_name); +} + +static int +symoid_cmp(struct symoid *a, struct symoid *b) +{ + return strcasecmp(a->name, b->name); +} + +RB_GENERATE(attr_type_tree, attr_type, link, attr_oid_cmp); +RB_GENERATE(object_tree, object, link, obj_oid_cmp); +RB_GENERATE(oidname_tree, oidname, link, oidname_cmp); +RB_GENERATE(symoid_tree, symoid, link, symoid_cmp); + +static struct attr_list *push_attr(struct attr_list *alist, struct attr_type *a); +static struct obj_list *push_obj(struct obj_list *olist, struct object *obj); +static struct name_list *push_name(struct name_list *nl, const char *name); +int is_oidstr(const char *oidstr); + +struct attr_type * +lookup_attribute_by_name(struct schema *schema, char *name) +{ + struct oidname *on, find; + + find.on_name = name; + on = RB_FIND(oidname_tree, &schema->attr_names, &find); + + if (on) + return on->on_attr_type; + return NULL; +} + +struct attr_type * +lookup_attribute_by_oid(struct schema *schema, char *oid) +{ + struct attr_type find; + + find.oid = oid; + return RB_FIND(attr_type_tree, &schema->attr_types, &find); +} + +struct attr_type * +lookup_attribute(struct schema *schema, char *oid_or_name) +{ + if (is_oidstr(oid_or_name)) + return lookup_attribute_by_oid(schema, oid_or_name); + return lookup_attribute_by_name(schema, oid_or_name); +} + +struct object * +lookup_object_by_oid(struct schema *schema, char *oid) +{ + struct object find; + + find.oid = oid; + return RB_FIND(object_tree, &schema->objects, &find); +} + +struct object * +lookup_object_by_name(struct schema *schema, char *name) +{ + struct oidname *on, find; + + find.on_name = name; + on = RB_FIND(oidname_tree, &schema->object_names, &find); + + if (on) + return on->on_object; + return NULL; +} + +struct object * +lookup_object(struct schema *schema, char *oid_or_name) +{ + if (is_oidstr(oid_or_name)) + return lookup_object_by_oid(schema, oid_or_name); + return lookup_object_by_name(schema, oid_or_name); +} + +/* Looks up a symbolic OID, optionally with a suffix OID, so if + * SYMBOL = 1.2.3.4 + * then + * SYMBOL:5.6 = 1.2.3.4.5.6 + * + * Returned string must be freed by the caller. + * Modifies the name argument. + */ +static char * +lookup_symbolic_oid(struct schema *schema, char *name) +{ + struct symoid *symoid, find; + char *colon, *oid; + size_t sz; + + colon = strchr(name, ':'); + if (colon != NULL) { + if (!is_oidstr(colon + 1)) { + log_warnx("invalid OID after colon: %s", colon + 1); + return NULL; + } + *colon = '\0'; + } + + find.name = name; + symoid = RB_FIND(symoid_tree, &schema->symbolic_oids, &find); + if (symoid == NULL) + return NULL; + + if (colon == NULL) + return strdup(symoid->oid); + + /* Expand SYMBOL:OID. + */ + sz = strlen(symoid->oid) + 1 + strlen(colon + 1) + 1; + if ((oid = malloc(sz)) == NULL) { + log_warnx("malloc"); + return NULL; + } + + strlcpy(oid, symoid->oid, sz); + strlcat(oid, ".", sz); + strlcat(oid, colon + 1, sz); + + return oid; +} + +/* Push a symbol-OID pair on the tree. Name and OID must be valid pointers + * during the lifetime of the tree. + */ +static struct symoid * +push_symbolic_oid(struct schema *schema, char *name, char *oid) +{ + struct symoid *symoid, find; + + find.name = name; + symoid = RB_FIND(symoid_tree, &schema->symbolic_oids, &find); + + if (symoid == NULL) { + symoid = calloc(1, sizeof(*symoid)); + if (symoid == NULL) { + log_warnx("calloc"); + return NULL; + } + + symoid->name = name; + RB_INSERT(symoid_tree, &schema->symbolic_oids, symoid); + } + + free(symoid->oid); + symoid->oid = oid; + + return symoid; +} + +static struct attr_list * +push_attr(struct attr_list *alist, struct attr_type *a) +{ + struct attr_ptr *aptr; + + if (alist == NULL) { + if ((alist = calloc(1, sizeof(*alist))) == NULL) { + log_warn("calloc"); + return NULL; + } + SLIST_INIT(alist); + } + + if ((aptr = calloc(1, sizeof(*aptr))) == NULL) { + log_warn("calloc"); + return NULL; + } + aptr->attr_type = a; + SLIST_INSERT_HEAD(alist, aptr, next); + + return alist; +} + +static struct obj_list * +push_obj(struct obj_list *olist, struct object *obj) +{ + struct obj_ptr *optr; + + if (olist == NULL) { + if ((olist = calloc(1, sizeof(*olist))) == NULL) { + log_warn("calloc"); + return NULL; + } + SLIST_INIT(olist); + } + + if ((optr = calloc(1, sizeof(*optr))) == NULL) { + log_warn("calloc"); + return NULL; + } + optr->object = obj; + SLIST_INSERT_HEAD(olist, optr, next); + + return olist; +} + +int +is_oidstr(const char *oidstr) +{ + struct ber_oid oid; + return (ber_string2oid(oidstr, &oid) == 0); +} + +static struct name_list * +push_name(struct name_list *nl, const char *name) +{ + struct name *n; + + if (nl == NULL) { + if ((nl = calloc(1, sizeof(*nl))) == NULL) { + log_warn("calloc"); + return NULL; + } + SLIST_INIT(nl); + } + if ((n = calloc(1, sizeof(*n))) == NULL) { + log_warn("calloc"); + return NULL; + } + n->name = name; + SLIST_INSERT_HEAD(nl, n, next); + + return nl; +} + +static int +schema_getc(struct schema *schema, int quotec) +{ + int c, next; + + if (schema->pushback_index) + return (schema->pushback_buffer[--schema->pushback_index]); + + if (quotec) { + if ((c = getc(schema->fp)) == EOF) { + log_warnx("reached end of file while parsing " + "quoted string"); + return EOF; + } + return (c); + } + + while ((c = getc(schema->fp)) == '\\') { + next = getc(schema->fp); + if (next != '\n') { + c = next; + break; + } + schema->lineno++; + } + + return (c); +} + +static int +schema_ungetc(struct schema *schema, int c) +{ + if (c == EOF) + return EOF; + + if (schema->pushback_index < SCHEMA_MAXPUSHBACK-1) + return (schema->pushback_buffer[schema->pushback_index++] = c); + else + return (EOF); +} + +static int +findeol(struct schema *schema) +{ + int c; + + /* skip to either EOF or the first real EOL */ + while (1) { + if (schema->pushback_index) + c = schema->pushback_buffer[--schema->pushback_index]; + else + c = schema_getc(schema, 0); + if (c == '\n') { + schema->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +static int +schema_lex(struct schema *schema, char **kw) +{ + char buf[8096]; + char *p; + int quotec, next, c; + + if (kw) + *kw = NULL; + +top: + p = buf; + while ((c = schema_getc(schema, 0)) == ' ' || c == '\t') + ; /* nothing */ + + if (c == '#') + while ((c = schema_getc(schema, 0)) != '\n' && c != EOF) + ; /* nothing */ + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = schema_getc(schema, quotec)) == EOF) + return (0); + if (c == '\n') { + schema->lineno++; + continue; + } else if (c == '\\') { + if ((next = schema_getc(schema, quotec)) == EOF) + return (0); + if (next == quotec || c == ' ' || c == '\t') + c = next; + else if (next == '\n') + continue; + else + schema_ungetc(schema, next); + } else if (c == quotec) { + *p = '\0'; + break; + } + if (p + 1 >= buf + sizeof(buf) - 1) { + log_warnx("string too long"); + return (findeol(schema)); + } + *p++ = (char)c; + } + if (kw != NULL && (*kw = strdup(buf)) == NULL) + fatal("schema_lex: strdup"); + return (STRING); + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && x != '<' && x != '>' && \ + x != '!' && x != '=' && x != '/' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_' || c == '*') { + do { + *p++ = c; + if ((unsigned)(p-buf) >= sizeof(buf)) { + log_warnx("string too long"); + return (findeol(schema)); + } + } while ((c = schema_getc(schema, 0)) != EOF && (allowed_in_string(c))); + schema_ungetc(schema, c); + *p = '\0'; + if (kw != NULL && (*kw = strdup(buf)) == NULL) + fatal("schema_lex: strdup"); + return STRING; + } + if (c == '\n') { + schema->lineno++; + goto top; + } + if (c == EOF) + return (0); + return (c); +} + +struct schema * +schema_new(void) +{ + struct schema *schema; + + if ((schema = calloc(1, sizeof(*schema))) == NULL) + return NULL; + + RB_INIT(&schema->attr_types); + RB_INIT(&schema->attr_names); + RB_INIT(&schema->objects); + RB_INIT(&schema->object_names); + RB_INIT(&schema->symbolic_oids); + + return schema; +} + +static void +schema_err(struct schema *schema, const char *fmt, ...) +{ + va_list ap; + char *nfmt; + + va_start(ap, fmt); + if (asprintf(&nfmt, "%s:%d: %s", schema->filename, schema->lineno, + fmt) == -1) + fatal("asprintf"); + vlog(LOG_CRIT, nfmt, ap); + va_end(ap); + free(nfmt); + + schema->error++; +} + +static int +schema_link_attr_name(struct schema *schema, const char *name, struct attr_type *attr) +{ + struct oidname *oidname, *prev; + + if ((oidname = calloc(1, sizeof(*oidname))) == NULL) { + log_warn("calloc"); + return -1; + } + + oidname->on_name = name; + oidname->on_attr_type = attr; + prev = RB_INSERT(oidname_tree, &schema->attr_names, oidname); + if (prev != NULL) { + schema_err(schema, "attribute type name '%s'" + " already defined for oid %s", + name, prev->on_attr_type->oid); + free(oidname); + return -1; + } + + return 0; +} + +static int +schema_link_attr_names(struct schema *schema, struct attr_type *attr) +{ + struct name *name; + + SLIST_FOREACH(name, attr->names, next) { + if (schema_link_attr_name(schema, name->name, attr) != 0) + return -1; + } + return 0; +} + +static int +schema_link_obj_name(struct schema *schema, const char *name, struct object *obj) +{ + struct oidname *oidname, *prev; + + if ((oidname = calloc(1, sizeof(*oidname))) == NULL) { + log_warn("calloc"); + return -1; + } + + oidname->on_name = name; + oidname->on_object = obj; + prev = RB_INSERT(oidname_tree, &schema->object_names, oidname); + if (prev != NULL) { + schema_err(schema, "object class name '%s'" + " already defined for oid %s", + name, prev->on_object->oid); + free(oidname); + return -1; + } + + return 0; +} + +static int +schema_link_obj_names(struct schema *schema, struct object *obj) +{ + struct name *name; + + SLIST_FOREACH(name, obj->names, next) { + if (schema_link_obj_name(schema, name->name, obj) != 0) + return -1; + } + return 0; +} + +static struct name_list * +schema_parse_names(struct schema *schema) +{ + struct name_list *nlist = NULL; + char *kw; + int token; + + token = schema_lex(schema, &kw); + if (token == STRING) + return push_name(NULL, kw); + + if (token != '(') + goto fail; + + for (;;) { + token = schema_lex(schema, &kw); + if (token == ')') + break; + if (token != STRING) + goto fail; + nlist = push_name(nlist, kw); + } + + return nlist; + +fail: + free(kw); + /* FIXME: leaks nlist here */ + return NULL; +} + +static struct attr_list * +schema_parse_attrlist(struct schema *schema) +{ + struct attr_list *alist = NULL; + struct attr_type *attr; + char *kw; + int token, want_dollar = 0; + + token = schema_lex(schema, &kw); + if (token == STRING) { + if ((attr = lookup_attribute(schema, kw)) == NULL) { + schema_err(schema, "undeclared attribute type '%s'", kw); + goto fail; + } + free(kw); + return push_attr(NULL, attr); + } + + if (token != '(') + goto fail; + + for (;;) { + token = schema_lex(schema, &kw); + if (token == ')') + break; + if (token == '$') { + if (!want_dollar) + goto fail; + want_dollar = 0; + continue; + } + if (token != STRING) + goto fail; + if ((attr = lookup_attribute(schema, kw)) == NULL) + goto fail; + alist = push_attr(alist, attr); + want_dollar = 1; + } + + return alist; + +fail: + free(kw); + /* FIXME: leaks alist here */ + return NULL; +} + +static struct obj_list * +schema_parse_objlist(struct schema *schema) +{ + struct obj_list *olist = NULL; + struct object *obj; + char *kw; + int token, want_dollar = 0; + + token = schema_lex(schema, &kw); + if (token == STRING) { + if ((obj = lookup_object(schema, kw)) == NULL) { + schema_err(schema, "undeclared object class '%s'", kw); + goto fail; + } + free(kw); + return push_obj(NULL, obj); + } + + if (token != '(') + goto fail; + + for (;;) { + token = schema_lex(schema, &kw); + if (token == ')') + break; + if (token == '$') { + if (!want_dollar) + goto fail; + want_dollar = 0; + continue; + } + if (token != STRING) + goto fail; + if ((obj = lookup_object(schema, kw)) == NULL) + goto fail; + olist = push_obj(olist, obj); + want_dollar = 1; + } + + return olist; + +fail: + free(kw); + /* FIXME: leaks olist here */ + return NULL; +} + +static int +schema_parse_attributetype(struct schema *schema) +{ + struct attr_type *attr = NULL, *prev; + char *kw, *arg = NULL; + int token, ret = 0, c; + + if (schema_lex(schema, NULL) != '(') + goto fail; + + if (schema_lex(schema, &kw) != STRING) + goto fail; + + if ((attr = calloc(1, sizeof(*attr))) == NULL) { + log_warn("calloc"); + goto fail; + } + + if (is_oidstr(kw)) + attr->oid = kw; + else { + attr->oid = lookup_symbolic_oid(schema, kw); + if (attr->oid == NULL) + goto fail; + free(kw); + } + kw = NULL; + + prev = RB_INSERT(attr_type_tree, &schema->attr_types, attr); + if (prev != NULL) { + schema_err(schema, "attribute type %s already defined", attr->oid); + goto fail; + } + + while (ret == 0) { + token = schema_lex(schema, &kw); + if (token == ')') + break; + else if (token != STRING) + goto fail; + if (strcasecmp(kw, "NAME") == 0) { + attr->names = schema_parse_names(schema); + if (attr->names == NULL) + goto fail; + schema_link_attr_names(schema, attr); + } else if (strcasecmp(kw, "DESC") == 0) { + if (schema_lex(schema, &attr->desc) != STRING) + goto fail; + } else if (strcasecmp(kw, "OBSOLETE") == 0) { + attr->obsolete = 1; + } else if (strcasecmp(kw, "SUP") == 0) { + if (schema_lex(schema, &arg) != STRING) + goto fail; + if ((attr->sup = lookup_attribute(schema, arg)) == NULL) { + schema_err(schema, "%s: no such attribute", arg); + goto fail; + } + } else if (strcasecmp(kw, "EQUALITY") == 0) { + if (schema_lex(schema, &attr->equality) != STRING) + goto fail; + } else if (strcasecmp(kw, "ORDERING") == 0) { + if (schema_lex(schema, &attr->ordering) != STRING) + goto fail; + } else if (strcasecmp(kw, "SUBSTR") == 0) { + if (schema_lex(schema, &attr->substr) != STRING) + goto fail; + } else if (strcasecmp(kw, "SYNTAX") == 0) { + if (schema_lex(schema, &attr->syntax) != STRING || + !is_oidstr(attr->syntax)) + goto fail; + if ((c = schema_getc(schema, 0)) == '{') { + if (schema_lex(schema, NULL) != STRING || + schema_lex(schema, NULL) != '}') + goto fail; + } else + schema_ungetc(schema, c); + } else if (strcasecmp(kw, "SINGLE-VALUE") == 0) { + attr->single = 1; + } else if (strcasecmp(kw, "COLLECTIVE") == 0) { + attr->collective = 1; + } else if (strcasecmp(kw, "NO-USER-MODIFICATION") == 0) { + attr->immutable = 1; + } else if (strcasecmp(kw, "USAGE") == 0) { + if (schema_lex(schema, &arg) != STRING) + goto fail; + if (strcasecmp(arg, "dSAOperation") == 0) + attr->usage = USAGE_DSA_OP; + else if (strcasecmp(arg, "directoryOperation") == 0) + attr->usage = USAGE_DIR_OP; + else if (strcasecmp(arg, "distributedOperation") == 0) + attr->usage = USAGE_DIST_OP; + else if (strcasecmp(arg, "userApplications") == 0) + attr->usage = USAGE_USER_APP; + else { + schema_err(schema, "invalid usage '%s'", arg); + goto fail; + } + } + } + + return 0; + +fail: + free(kw); + free(arg); + if (attr != NULL) { + if (attr->oid != NULL) { + RB_REMOVE(attr_type_tree, &schema->attr_types, attr); + free(attr->oid); + } + free(attr->desc); + free(attr); + } + return -1; +} + +static int +schema_parse_objectclass(struct schema *schema) +{ + struct object *obj = NULL, *prev; + char *kw; + int token, ret = 0; + + if (schema_lex(schema, NULL) != '(') + goto fail; + + if (schema_lex(schema, &kw) != STRING) + goto fail; + + if ((obj = calloc(1, sizeof(*obj))) == NULL) { + log_warn("calloc"); + goto fail; + } + + if (is_oidstr(kw)) + obj->oid = kw; + else { + obj->oid = lookup_symbolic_oid(schema, kw); + if (obj->oid == NULL) + goto fail; + free(kw); + } + kw = NULL; + + prev = RB_INSERT(object_tree, &schema->objects, obj); + if (prev != NULL) { + schema_err(schema, "object class %s already defined", obj->oid); + goto fail; + } + + while (ret == 0) { + token = schema_lex(schema, &kw); + if (token == ')') + break; + else if (token != STRING) + goto fail; + if (strcasecmp(kw, "NAME") == 0) { + obj->names = schema_parse_names(schema); + if (obj->names == NULL) + goto fail; + schema_link_obj_names(schema, obj); + } else if (strcasecmp(kw, "DESC") == 0) { + if (schema_lex(schema, &obj->desc) != STRING) + goto fail; + } else if (strcasecmp(kw, "OBSOLETE") == 0) { + obj->obsolete = 1; + } else if (strcasecmp(kw, "SUP") == 0) { + obj->sup = schema_parse_objlist(schema); + if (obj->sup == NULL) + goto fail; + } else if (strcasecmp(kw, "ABSTRACT") == 0) { + obj->kind = KIND_ABSTRACT; + } else if (strcasecmp(kw, "STRUCTURAL") == 0) { + obj->kind = KIND_STRUCTURAL; + } else if (strcasecmp(kw, "AUXILIARY") == 0) { + obj->kind = KIND_AUXILIARY; + } else if (strcasecmp(kw, "MUST") == 0) { + obj->must = schema_parse_attrlist(schema); + if (obj->must == NULL) + goto fail; + } else if (strcasecmp(kw, "MAY") == 0) { + obj->may = schema_parse_attrlist(schema); + if (obj->may == NULL) + goto fail; + } + } + + return 0; + +fail: + free(kw); + if (obj != NULL) { + if (obj->oid != NULL) { + RB_REMOVE(object_tree, &schema->objects, obj); + free(obj->oid); + } + free(obj->desc); + free(obj); + } + return -1; +} + +static int +schema_parse_objectidentifier(struct schema *schema) +{ + char *symname = NULL, *symoid = NULL; + char *oid = NULL; + + if (schema_lex(schema, &symname) != STRING) + goto fail; + if (schema_lex(schema, &symoid) != STRING) + goto fail; + + if (is_oidstr(symoid)) { + oid = symoid; + symoid = NULL; + } else if ((oid = lookup_symbolic_oid(schema, symoid)) == NULL) + goto fail; + + if (push_symbolic_oid(schema, symname, oid) == NULL) + goto fail; + + free(symoid); + return 0; + +fail: + free(symname); + free(symoid); + free(oid); + return -1; +} + +int +schema_parse(struct schema *schema, const char *filename) +{ + char *kw; + int token, ret = 0; + + log_debug("parsing schema file '%s'", filename); + + if ((schema->fp = fopen(filename, "r")) == NULL) + return -1; + schema->filename = filename; + schema->lineno = 1; + + while (ret == 0) { + token = schema_lex(schema, &kw); + if (token == STRING) { + if (strcasecmp(kw, "attributetype") == 0) + ret = schema_parse_attributetype(schema); + else if (strcasecmp(kw, "objectclass") == 0) + ret = schema_parse_objectclass(schema); + else if (strcasecmp(kw, "objectidentifier") == 0) + ret = schema_parse_objectidentifier(schema); + else { + schema_err(schema, "syntax error at '%s'", kw); + ret = -1; + } + if (ret == -1 && schema->error == 0) + schema_err(schema, "syntax error"); + free(kw); + } else if (token == 0) { /* EOF */ + break; + } else { + schema_err(schema, "syntax error"); + ret = -1; + } + } + + fclose(schema->fp); + schema->fp = NULL; + schema->filename = NULL; + + return ret; +} + diff --git a/usr.sbin/ldapd/schema.h b/usr.sbin/ldapd/schema.h new file mode 100644 index 00000000000..271a1038d4e --- /dev/null +++ b/usr.sbin/ldapd/schema.h @@ -0,0 +1,139 @@ +/* $OpenBSD: schema.h,v 1.1 2010/06/29 02:45:46 martinh Exp $ */ + +/* + * Copyright (c) 2010 Martin Hedenfalk <martinh@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _schema_h_ +#define _schema_h_ + +enum usage { + USAGE_USER_APP, + USAGE_DIR_OP, /* operational attribute */ + USAGE_DIST_OP, /* operational attribute */ + USAGE_DSA_OP /* operational attribute */ +}; + +struct name { + SLIST_ENTRY(name) next; + const char *name; +}; +SLIST_HEAD(name_list, name); + +struct attr_type { + RB_ENTRY(attr_type) link; + char *oid; + struct name_list *names; + char *desc; + int obsolete; + struct attr_type *sup; + char *equality; + char *ordering; + char *substr; + char *syntax; + int single; + int collective; + int immutable; /* no-user-modification */ + enum usage usage; +}; +RB_HEAD(attr_type_tree, attr_type); +RB_PROTOTYPE(attr_type_tree, attr_type, link, attr_oid_cmp); + +struct attr_ptr { + SLIST_ENTRY(attr_ptr) next; + struct attr_type *attr_type; +}; +SLIST_HEAD(attr_list, attr_ptr); + +enum object_kind { + KIND_ABSTRACT, + KIND_STRUCTURAL, + KIND_AUXILIARY +}; + +struct object; +struct obj_ptr { + SLIST_ENTRY(obj_ptr) next; + struct object *object; +}; +SLIST_HEAD(obj_list, obj_ptr); + +struct object { + RB_ENTRY(object) link; + char *oid; + struct name_list *names; + char *desc; + int obsolete; + struct obj_list *sup; + enum object_kind kind; + struct attr_list *must; + struct attr_list *may; +}; +RB_HEAD(object_tree, object); +RB_PROTOTYPE(object_tree, object, link, obj_oid_cmp); + +struct oidname { + RB_ENTRY(oidname) link; + const char *on_name; +#define on_attr_type on_ptr.ou_attr_type +#define on_object on_ptr.ou_object + union { + struct attr_type *ou_attr_type; + struct object *ou_object; + } on_ptr; +}; +RB_HEAD(oidname_tree, oidname); +RB_PROTOTYPE(oidname_tree, oidname, link, oidname_cmp); + +struct symoid { + RB_ENTRY(symoid) link; + char *name; /* symbolic name */ + char *oid; +}; +RB_HEAD(symoid_tree, symoid); +RB_PROTOTYPE(symoid_tree, symoid, link, symoid_cmp); + +#define SCHEMA_MAXPUSHBACK 128 + +struct schema +{ + struct attr_type_tree attr_types; + struct oidname_tree attr_names; + struct object_tree objects; + struct oidname_tree object_names; + struct symoid_tree symbolic_oids; + + FILE *fp; + const char *filename; + char pushback_buffer[SCHEMA_MAXPUSHBACK]; + int pushback_index; + int lineno; + int error; +}; + +struct schema *schema_new(void); +int schema_parse(struct schema *schema, + const char *filename); + +struct attr_type *lookup_attribute_by_oid(struct schema *schema, char *oid); +struct attr_type *lookup_attribute_by_name(struct schema *schema, char *name); +struct attr_type *lookup_attribute(struct schema *schema, char *oid_or_name); +struct object *lookup_object_by_oid(struct schema *schema, char *oid); +struct object *lookup_object_by_name(struct schema *schema, char *name); +struct object *lookup_object(struct schema *schema, char *oid_or_name); +int is_oidstr(const char *oidstr); + +#endif + diff --git a/usr.sbin/ldapd/search.c b/usr.sbin/ldapd/search.c index 37446d527c1..f60ce2f09de 100644 --- a/usr.sbin/ldapd/search.c +++ b/usr.sbin/ldapd/search.c @@ -1,4 +1,4 @@ -/* $OpenBSD: search.c,v 1.6 2010/06/23 13:10:14 martinh Exp $ */ +/* $OpenBSD: search.c,v 1.7 2010/06/29 02:45:46 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -55,7 +55,7 @@ is_operational(char *adesc) { struct attr_type *at; - at = lookup_attribute(adesc); + at = lookup_attribute(conf->schema, adesc); if (at) return at->usage != USAGE_USER_APP; diff --git a/usr.sbin/ldapd/validate.c b/usr.sbin/ldapd/validate.c index b7a3358aadd..6b7a01cc0df 100644 --- a/usr.sbin/ldapd/validate.c +++ b/usr.sbin/ldapd/validate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: validate.c,v 1.1 2010/05/31 17:36:31 martinh Exp $ */ +/* $OpenBSD: validate.c,v 1.2 2010/06/29 02:45:46 martinh Exp $ */ /* * Copyright (c) 2010 Martin Hedenfalk <martin@bzero.se> @@ -140,7 +140,7 @@ validate_dn(const char *dn, struct ber_element *entry) log_debug("got naming attribute %s", na); log_debug("got distinguished value %s", dv); - if ((at = lookup_attribute(na)) == NULL) { + if ((at = lookup_attribute(conf->schema, na)) == NULL) { log_debug("attribute %s not defined in schema", na); goto fail; } @@ -240,7 +240,7 @@ validate_entry(const char *dn, struct ber_element *entry, int relax) for (a = objclass->be_sub; a != NULL; a = a->be_next) { if (ber_get_string(a, &s) != 0) return LDAP_INVALID_SYNTAX; - if ((obj = lookup_object(s)) == NULL) { + if ((obj = lookup_object(conf->schema, s)) == NULL) { log_debug("objectClass %s not defined in schema", s); return LDAP_NAMING_VIOLATION; } @@ -265,7 +265,7 @@ validate_entry(const char *dn, struct ber_element *entry, int relax) for (a = entry->be_sub; a != NULL; a = a->be_next) { if (ber_scanf_elements(a, "{se{", &s, &vals) != 0) return LDAP_INVALID_SYNTAX; - if ((at = lookup_attribute(s)) == NULL) { + if ((at = lookup_attribute(conf->schema, s)) == NULL) { log_debug("attribute %s not defined in schema", s); return LDAP_NAMING_VIOLATION; } |