diff options
-rw-r--r-- | usr.sbin/ldapd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/ldapd/schema.c | 42 | ||||
-rw-r--r-- | usr.sbin/ldapd/schema.h | 16 | ||||
-rw-r--r-- | usr.sbin/ldapd/syntax.c | 351 | ||||
-rw-r--r-- | usr.sbin/ldapd/validate.c | 13 |
5 files changed, 410 insertions, 16 deletions
diff --git a/usr.sbin/ldapd/Makefile b/usr.sbin/ldapd/Makefile index efba6758031..f3ee37b6df2 100644 --- a/usr.sbin/ldapd/Makefile +++ b/usr.sbin/ldapd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.7 2010/09/01 17:34:15 martinh Exp $ +# $OpenBSD: Makefile,v 1.8 2010/09/03 09:39:17 martinh Exp $ PROG= ldapd MAN= ldapd.8 ldapd.conf.5 @@ -6,7 +6,7 @@ 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 schema.c imsgev.c + validate.c uuid.c schema.c imsgev.c syntax.c LDADD= -levent -lssl -lcrypto -lz -lutil DPADD= ${LIBEVENT} ${LIBCRYPTO} ${LIBSSL} ${LIBZ} ${LIBUTIL} diff --git a/usr.sbin/ldapd/schema.c b/usr.sbin/ldapd/schema.c index a73f020c3f4..2daca246015 100644 --- a/usr.sbin/ldapd/schema.c +++ b/usr.sbin/ldapd/schema.c @@ -1,4 +1,4 @@ -/* $OpenBSD: schema.c,v 1.9 2010/09/01 18:30:48 martinh Exp $ */ +/* $OpenBSD: schema.c,v 1.10 2010/09/03 09:39:17 martinh Exp $ */ /* * Copyright (c) 2010 Martin Hedenfalk <martinh@openbsd.org> @@ -122,7 +122,8 @@ lookup_object(struct schema *schema, char *oid_or_name) return lookup_object_by_name(schema, oid_or_name); } -/* Looks up a symbolic OID, optionally with a suffix OID, so if +/* + * 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 @@ -130,7 +131,7 @@ lookup_object(struct schema *schema, char *oid_or_name) * Returned string must be freed by the caller. * Modifies the name argument. */ -static char * +char * lookup_symbolic_oid(struct schema *schema, char *name) { struct symoid *symoid, find; @@ -154,8 +155,7 @@ lookup_symbolic_oid(struct schema *schema, char *name) if (colon == NULL) return strdup(symoid->oid); - /* Expand SYMBOL:OID. - */ + /* Expand SYMBOL:OID. */ sz = strlen(symoid->oid) + 1 + strlen(colon + 1) + 1; if ((oid = malloc(sz)) == NULL) { log_warnx("malloc"); @@ -169,7 +169,8 @@ lookup_symbolic_oid(struct schema *schema, char *name) return oid; } -/* Push a symbol-OID pair on the tree. Name and OID must be valid pointers +/* + * Push a symbol-OID pair on the tree. Name and OID must be valid pointers * during the lifetime of the tree. */ static struct symoid * @@ -666,7 +667,7 @@ fail: static int schema_parse_attributetype(struct schema *schema) { - struct attr_type *attr = NULL, *prev; + struct attr_type *attr = NULL, *prev, *sup; struct name_list *xnames; char *kw = NULL, *arg = NULL; int token, ret = 0, c; @@ -733,15 +734,23 @@ schema_parse_attributetype(struct schema *schema) 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)) + if (schema_lex(schema, &arg) != STRING || + !is_oidstr(arg)) goto fail; + + if ((attr->syntax = syntax_lookup(arg)) == NULL) { + schema_err(schema, "syntax not supported: %s", + arg); + 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); + free(arg); } else if (strcasecmp(kw, "SINGLE-VALUE") == 0) { attr->single = 1; } else if (strcasecmp(kw, "COLLECTIVE") == 0) { @@ -777,6 +786,19 @@ schema_parse_attributetype(struct schema *schema) free(kw); } + /* Check that a syntax is defined, either directly or + * indirectly via a superior attribute type. + */ + sup = attr->sup; + while (attr->syntax == NULL && sup != NULL) { + attr->syntax = sup->syntax; + sup = sup->sup; + } + if (attr->syntax == NULL) { + schema_err(schema, "%s: no syntax defined", ATTR_NAME(attr)); + goto fail; + } + return 0; fail: @@ -1200,7 +1222,7 @@ schema_dump_attribute(struct attr_type *at, char *buf, size_t size) if (at->syntax != NULL) if (strlcat(buf, " SYNTAX ", size) >= size || - strlcat(buf, at->syntax, size) >= size) + strlcat(buf, at->syntax->oid, size) >= size) return -1; if (at->single && strlcat(buf, " SINGLE-VALUE", size) >= size) diff --git a/usr.sbin/ldapd/schema.h b/usr.sbin/ldapd/schema.h index d00dfaa14e3..e5c305d0dcd 100644 --- a/usr.sbin/ldapd/schema.h +++ b/usr.sbin/ldapd/schema.h @@ -1,4 +1,4 @@ -/* $OpenBSD: schema.h,v 1.4 2010/07/02 05:23:40 martinh Exp $ */ +/* $OpenBSD: schema.h,v 1.5 2010/09/03 09:39:17 martinh Exp $ */ /* * Copyright (c) 2010 Martin Hedenfalk <martinh@openbsd.org> @@ -36,6 +36,14 @@ struct name { }; SLIST_HEAD(name_list, name); +struct schema; +struct syntax { + char *oid; + char *desc; + int (*is_valid)(struct schema *schema, char *value, + size_t len); +}; + struct attr_type { RB_ENTRY(attr_type) link; char *oid; @@ -46,7 +54,7 @@ struct attr_type { char *equality; char *ordering; char *substr; - char *syntax; + const struct syntax *syntax; int single; int collective; int immutable; /* no-user-modification */ @@ -141,7 +149,11 @@ 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); +char *lookup_symbolic_oid(struct schema *schema, char *name); int is_oidstr(const char *oidstr); +/* syntax.c */ +const struct syntax *syntax_lookup(const char *oid); + #endif diff --git a/usr.sbin/ldapd/syntax.c b/usr.sbin/ldapd/syntax.c new file mode 100644 index 00000000000..69a46388015 --- /dev/null +++ b/usr.sbin/ldapd/syntax.c @@ -0,0 +1,351 @@ +/* $OpenBSD: syntax.c,v 1.1 2010/09/03 09:39:17 martinh Exp $ */ + +/* + * Copyright (c) 2010 Martin Hedenfalk <martin@bzero.se> + * + * 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 <sys/queue.h> +#include <sys/tree.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "schema.h" +#include "uuid.h" + +#define SYNTAX_DECL(TYPE) \ + static int syntax_is_##TYPE(struct schema *schema, char *value, size_t len) + +SYNTAX_DECL(bit_string); +SYNTAX_DECL(boolean); +SYNTAX_DECL(country); +SYNTAX_DECL(directory_string); +SYNTAX_DECL(dn); +SYNTAX_DECL(gentime); +SYNTAX_DECL(ia5_string); +SYNTAX_DECL(integer); +SYNTAX_DECL(numeric_string); +SYNTAX_DECL(octet_string); +SYNTAX_DECL(oid); +SYNTAX_DECL(printable_string); +SYNTAX_DECL(utctime); +SYNTAX_DECL(uuid); + +static struct syntax syntaxes[] = { + /* + * Keep these sorted. + */ + { "1.3.6.1.1.1.0.0", "NIS netgroup triple", NULL }, + { "1.3.6.1.1.1.0.1", "Boot parameter", NULL }, + { "1.3.6.1.1.16.1", "UUID", syntax_is_uuid }, + { "1.3.6.1.4.1.1466.115.121.1.11", "Country String", syntax_is_country }, + { "1.3.6.1.4.1.1466.115.121.1.12", "DN", syntax_is_dn }, + { "1.3.6.1.4.1.1466.115.121.1.14", "Delivery Method", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.15", "Directory String", syntax_is_directory_string }, + { "1.3.6.1.4.1.1466.115.121.1.16", "DIT Content Rule Description", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.17", "DIT Structure Rule Description", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.21", "Enhanced Guide", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.22", "Facsimile Telephone Number", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.23", "Fax", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.24", "Generalized Time", syntax_is_gentime }, + { "1.3.6.1.4.1.1466.115.121.1.25", "Guide", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.26", "IA5 String", syntax_is_ia5_string }, + { "1.3.6.1.4.1.1466.115.121.1.27", "INTEGER", syntax_is_integer }, + { "1.3.6.1.4.1.1466.115.121.1.28", "JPEG", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.3", "Attribute Type Description", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.30", "Matching Rule Description", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.31", "Matching Rule Use Description", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.34", "Name And Optional UID", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.35", "Name Form Description", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.36", "Numeric String", syntax_is_numeric_string }, + { "1.3.6.1.4.1.1466.115.121.1.37", "Object Class Description", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.38", "OID", syntax_is_oid }, + { "1.3.6.1.4.1.1466.115.121.1.39", "Other Mailbox", syntax_is_ia5_string }, + { "1.3.6.1.4.1.1466.115.121.1.40", "Octet String", syntax_is_octet_string }, + { "1.3.6.1.4.1.1466.115.121.1.41", "Postal Address", syntax_is_directory_string }, + { "1.3.6.1.4.1.1466.115.121.1.44", "Printable String", syntax_is_printable_string }, + { "1.3.6.1.4.1.1466.115.121.1.45", "Subtree Specification", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.5", "Binary", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.50", "Telephone Number", syntax_is_printable_string }, + { "1.3.6.1.4.1.1466.115.121.1.51", "Teletex Terminal Identifier", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.52", "Telex Number", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.53", "UTC Time", syntax_is_utctime }, + { "1.3.6.1.4.1.1466.115.121.1.54", "LDAP Syntax Description", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.58", "Substring Assertion", NULL }, + { "1.3.6.1.4.1.1466.115.121.1.6", "Bit String", syntax_is_bit_string }, + { "1.3.6.1.4.1.1466.115.121.1.7", "Boolean", syntax_is_boolean }, + { "1.3.6.1.4.1.1466.115.121.1.8", "Certificate", NULL }, + +}; + +static int +syntax_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct syntax *)e)->oid)); +} + +const struct syntax * +syntax_lookup(const char *oid) +{ + return bsearch(oid, syntaxes, sizeof(syntaxes)/sizeof(syntaxes[0]), + sizeof(syntaxes[0]), syntax_cmp); +} + +/* + * A value of the Octet String syntax is a sequence of zero, one, or + * more arbitrary octets. + */ +static int +syntax_is_octet_string(struct schema *schema, char *value, size_t len) +{ + return 1; +} + +/* + * A string of one or more arbitrary UTF-8 characters. + */ +static int +syntax_is_directory_string(struct schema *schema, char *value, size_t len) +{ + /* FIXME: validate UTF-8 characters. */ + return len >= 1 && *value != '\0'; +} + +/* + * A value of the Printable String syntax is a string of one or more + * latin alphabetic, numeric, and selected punctuation characters as + * specified by the <PrintableCharacter> rule in Section 3.2. + * + * PrintableCharacter = ALPHA / DIGIT / SQUOTE / LPAREN / RPAREN / + * PLUS / COMMA / HYPHEN / DOT / EQUALS / + * SLASH / COLON / QUESTION / SPACE + */ +static int +syntax_is_printable_string(struct schema *schema, char *value, size_t len) +{ + static char *special = "'()+,-.=/:? "; + char *p; + + for (p = value; len > 0 && *p != '\0'; p++, len--) { + if (!isalnum(*p) && strchr(special, *p) == NULL) + return 0; + } + + return (p != value); +} + +/* + * A value of the IA5 String syntax is a string of zero, one, or more + * characters from International Alphabet 5 (IA5). + * IA5String = *(%x00-7F) + */ +static int +syntax_is_ia5_string(struct schema *schema, char *value, size_t len) +{ + char *p; + + for (p = value; *p != '\0'; p++) { + if ((unsigned char)*p > 0x7F) + return 0; + } + + return 1; +} + +/* + * A value of the Integer syntax is a whole number of unlimited magnitude. + * Integer = ( HYPHEN LDIGIT *DIGIT ) / number + * number = DIGIT / ( LDIGIT 1*DIGIT ) + */ +static int +syntax_is_integer(struct schema *schema, char *value, size_t len) +{ + if (*value == '-') + value++; + if (*value == '0') + return value[1] == '\0'; + for (value++; *value != '\0'; value++) + if (!isdigit(*value)) + return 0; + return 1; +} + +static int +syntax_is_dn(struct schema *schema, char *value, size_t len) +{ + if (!syntax_is_directory_string(schema, value, len)) + return 0; + + /* FIXME: DN syntax not implemented */ + + return 1; +} + +static int +syntax_is_oid(struct schema *schema, char *value, size_t len) +{ + char *symoid = NULL; + + if (len == 0 || *value == '\0') + return 0; + if (is_oidstr(value)) + return 1; + + /* + * Check for a symbolic OID: object class, attribute type or symoid. + */ + if (lookup_object_by_name(schema, value) != NULL || + lookup_attribute_by_name(schema, value) != NULL || + (symoid = lookup_symbolic_oid(schema, value)) != NULL) { + free(symoid); + return 1; + } + + return 0; +} + +static int +syntax_is_uuid(struct schema *schema, char *value, size_t len) +{ + int i; + + if (len != 36) + return 0; + +#define IS_XDIGITS(n, c) \ + do { \ + for (i = 0; i < (n); i++) \ + if (!isxdigit(*value++)) \ + return 0; \ + if (*value++ != (c)) \ + return 0; \ + } while(0) + + IS_XDIGITS(8, '-'); + IS_XDIGITS(4, '-'); + IS_XDIGITS(4, '-'); + IS_XDIGITS(4, '-'); + IS_XDIGITS(12, '\0'); + + return 1; +} + +/* + * NumericString = 1*(DIGIT / SPACE) + */ +static int +syntax_is_numeric_string(struct schema *schema, char *value, size_t len) +{ + char *p; + + for (p = value; *p != '\0'; p++) + if (!isdigit(*p) || *p != ' ') + return 0; + + return p != value; +} + +static int +syntax_is_time(struct schema *schema, char *value, size_t len, int gen) +{ + int n; + char *p = value; + +#define CHECK_RANGE(min, max) \ + if (!isdigit(p[0]) || !isdigit(p[1])) \ + return 0; \ + n = (p[0] - '0') * 10 + (p[1] - '0'); \ + if (n < min || n > max) \ + return 0; \ + p += 2; + + if (gen) + CHECK_RANGE(0, 99) /* century */ + CHECK_RANGE(0, 99) /* year */ + CHECK_RANGE(1, 12) /* month */ + CHECK_RANGE(1, 31) /* day */ + /* FIXME: should check number of days in month */ + CHECK_RANGE(0, 23); /* hour */ + + if (!gen || isdigit(*p)) { + CHECK_RANGE(0, 59); /* minute */ + if (!gen && isdigit(*p)) + CHECK_RANGE(0, 59+gen); /* second or leap-second */ + if (*p == '\0') + return 1; + } + /* fraction */ + if (!gen && ((*p == ',' || *p == '.') && !isdigit(*++p))) + return 0; + + if (*p == '-' || *p == '+') { + ++p; + CHECK_RANGE(0, 23); /* hour */ + if (!gen || isdigit(*p)) + CHECK_RANGE(0, 59); /* minute */ + } else if (*p++ != 'Z') + return 0; + + return *p == '\0'; +} + +static int +syntax_is_gentime(struct schema *schema, char *value, size_t len) +{ + return syntax_is_time(schema, value, len, 1); +} + +static int +syntax_is_utctime(struct schema *schema, char *value, size_t len) +{ + return syntax_is_time(schema, value, len, 0); +} + +static int +syntax_is_country(struct schema *schema, char *value, size_t len) +{ + if (len != 2) + return 0; + return syntax_is_printable_string(schema, value, len); +} + +static int +syntax_is_bit_string(struct schema *schema, char *value, size_t len) +{ + if (*value++ != '\'') + return 0; + + for (; *value != '\0'; value++) { + if (*value == '\'') + break; + if (*value != '0' && *value != '1') + return 0; + } + + if (++*value != 'B') + return 0; + + return *value == '\0'; +} + +static int +syntax_is_boolean(struct schema *schema, char *value, size_t len) +{ + return strcmp(value, "TRUE") == 0 || strcmp(value, "FALSE") == 0; +} + diff --git a/usr.sbin/ldapd/validate.c b/usr.sbin/ldapd/validate.c index a1cc215e470..8df2c710180 100644 --- a/usr.sbin/ldapd/validate.c +++ b/usr.sbin/ldapd/validate.c @@ -1,4 +1,4 @@ -/* $OpenBSD: validate.c,v 1.7 2010/07/01 06:15:55 martinh Exp $ */ +/* $OpenBSD: validate.c,v 1.8 2010/09/03 09:39:17 martinh Exp $ */ /* * Copyright (c) 2010 Martin Hedenfalk <martin@bzero.se> @@ -51,6 +51,7 @@ validate_attribute(struct attr_type *at, struct ber_element *vals) { int nvals = 0; struct ber_element *elm; + char *val; if (vals == NULL) { log_debug("missing values"); @@ -63,7 +64,7 @@ validate_attribute(struct attr_type *at, struct ber_element *vals) } for (elm = vals->be_sub; elm != NULL; elm = elm->be_next) { - if (elm->be_type != BER_TYPE_OCTETSTRING) { + if (ber_get_string(elm, &val) == -1) { log_debug("attribute value not an octet-string"); return LDAP_PROTOCOL_ERROR; } @@ -73,6 +74,14 @@ validate_attribute(struct attr_type *at, struct ber_element *vals) " attribute %s", ATTR_NAME(at)); return LDAP_CONSTRAINT_VIOLATION; } + + if (at->syntax->is_valid != NULL && + !at->syntax->is_valid(conf->schema, val, elm->be_len)) { + log_debug("%s: invalid syntax", ATTR_NAME(at)); + log_debug("syntax = %s", at->syntax->desc); + log_debug("value: [%.*s]", elm->be_len, val); + return LDAP_INVALID_SYNTAX; + } } /* There must be at least one value in an attribute. */ |