summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/ldapd/Makefile4
-rw-r--r--usr.sbin/ldapd/schema.c42
-rw-r--r--usr.sbin/ldapd/schema.h16
-rw-r--r--usr.sbin/ldapd/syntax.c351
-rw-r--r--usr.sbin/ldapd/validate.c13
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. */