diff options
author | Martin Hedenfal <martinh@cvs.openbsd.org> | 2010-06-29 21:54:39 +0000 |
---|---|---|
committer | Martin Hedenfal <martinh@cvs.openbsd.org> | 2010-06-29 21:54:39 +0000 |
commit | 7bc63086587ad011543c291c6445a074fb481b9a (patch) | |
tree | ae36132dccb27adb556354296d93902392c5f6a9 /usr.sbin | |
parent | 43fdf4137164b918d6bcedc03351f24880bacb1a (diff) |
Add support for referrals. Referrals are configured in the config file,
either in the global context or in a namespace. The latter can be used to
delegate requests to different servers for specific parts of the DIT. The
former is a global catch-all referral.
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/ldapd/auth.c | 6 | ||||
-rw-r--r-- | usr.sbin/ldapd/ldapd.conf.5 | 22 | ||||
-rw-r--r-- | usr.sbin/ldapd/ldapd.h | 16 | ||||
-rw-r--r-- | usr.sbin/ldapd/ldape.c | 82 | ||||
-rw-r--r-- | usr.sbin/ldapd/modify.c | 32 | ||||
-rw-r--r-- | usr.sbin/ldapd/namespace.c | 44 | ||||
-rw-r--r-- | usr.sbin/ldapd/parse.y | 28 | ||||
-rw-r--r-- | usr.sbin/ldapd/search.c | 10 |
8 files changed, 217 insertions, 23 deletions
diff --git a/usr.sbin/ldapd/auth.c b/usr.sbin/ldapd/auth.c index ec2c6018ad2..3bc1406a492 100644 --- a/usr.sbin/ldapd/auth.c +++ b/usr.sbin/ldapd/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.3 2010/06/23 13:10:14 martinh Exp $ */ +/* $OpenBSD: auth.c,v 1.4 2010/06/29 21:54:38 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -276,12 +276,14 @@ ldap_auth_simple(struct request *req, char *binddn, struct ber_element *auth) return LDAP_UNWILLING_TO_PERFORM; } - if ((ns = namespace_for_base(binddn)) == NULL) + if ((ns = namespace_lookup_base(binddn, 1)) == NULL) return LDAP_INVALID_CREDENTIALS; if (strcmp(ns->rootdn, binddn) == 0) { if (check_password(ns->rootpw, password) == 0) ok = 1; + } else if (namespace_has_referrals(ns)) { + return LDAP_INVALID_CREDENTIALS; } else { if (!authorized(req->conn, ns, ACI_BIND, binddn, LDAP_SCOPE_BASE)) diff --git a/usr.sbin/ldapd/ldapd.conf.5 b/usr.sbin/ldapd/ldapd.conf.5 index b478f97475f..02a6a46b3c6 100644 --- a/usr.sbin/ldapd/ldapd.conf.5 +++ b/usr.sbin/ldapd/ldapd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ldapd.conf.5,v 1.2 2010/06/29 21:23:13 martinh Exp $ +.\" $OpenBSD: ldapd.conf.5,v 1.3 2010/06/29 21:54:38 martinh Exp $ .\" .\" Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> .\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org> @@ -100,6 +100,20 @@ Only secured connections accept plain text password authentication. Connections using TLS or unix domain sockets are always considered secured. The secure keyword can be used to mark an otherwise insecure connection secured, e.g. if IPsec is used. +.It referral Ar URL +Specify a default referral. +If no namespace matches the base DN in a request, the request is +delegated to another LDAP server instead of returning an error. +.Pp +This option can be given multiple times, in which case the URLs are +considered equal. +Clients may choose to follow any of the referral URLs. +.Pp +The URL format has the following format: +.Bd -literal -offset indent +ldap://ldap.example.com +ldaps://ldap.example.com:3890 +.Ed .It schema Ar filename Add schema definitions from the specified file. .El @@ -117,8 +131,14 @@ namespace "dc=example,dc=com" { } .Ed .Pp +When matching requests aginst namespace suffixes, the most specific +match is used. +Each namespace stores data in a separate database file. +.Pp A namespace has the following configuration properties: .Bl -tag -width Ds +.It referral Ar URL +Specify a referral to return for requests matching the suffix. .It rootdn Ar dn Specify the distinguished name of the root user for the namespace. The root user is always allowed to read and write entries in the namespace. diff --git a/usr.sbin/ldapd/ldapd.h b/usr.sbin/ldapd/ldapd.h index 340fb224d22..5837391a984 100644 --- a/usr.sbin/ldapd/ldapd.h +++ b/usr.sbin/ldapd/ldapd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ldapd.h,v 1.13 2010/06/29 21:00:34 martinh Exp $ */ +/* $OpenBSD: ldapd.h,v 1.14 2010/06/29 21:54:38 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -103,9 +103,16 @@ struct attr_index { }; TAILQ_HEAD(attr_index_list, attr_index); +struct referral { + SLIST_ENTRY(referral) next; + char *url; +}; +SLIST_HEAD(referrals, referral); + struct namespace { TAILQ_ENTRY(namespace) next; char *suffix; + struct referrals referrals; char *rootdn; char *rootpw; char *data_path; @@ -231,6 +238,7 @@ struct ldapd_config struct namespace_list namespaces; struct listenerlist listeners; SPLAY_HEAD(ssltree, ssl) *sc_ssl; + struct referrals referrals; struct acl acl; struct schema *schema; }; @@ -348,6 +356,8 @@ int ldap_extended(struct request *req); void send_ldap_result(struct conn *conn, int msgid, unsigned long type, long long result_code); int ldap_respond(struct request *req, int code); +int ldap_refer(struct request *req, const char *basedn, + struct search *search, struct referrals *refs); /* namespace.c */ @@ -367,7 +377,11 @@ int namespace_add(struct namespace *ns, char *dn, int namespace_update(struct namespace *ns, char *dn, struct ber_element *root); int namespace_del(struct namespace *ns, char *dn); +struct namespace *namespace_lookup_base(const char *basedn, + int include_referrals); struct namespace *namespace_for_base(const char *basedn); +int namespace_has_referrals(struct namespace *ns); +struct referrals *namespace_referrals(const char *basedn); int namespace_has_index(struct namespace *ns, const char *attr, enum index_type type); int namespace_begin_txn(struct namespace *ns, diff --git a/usr.sbin/ldapd/ldape.c b/usr.sbin/ldapd/ldape.c index d4b8a9ad76a..4f8c4744dd8 100644 --- a/usr.sbin/ldapd/ldape.c +++ b/usr.sbin/ldapd/ldape.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ldape.c,v 1.7 2010/06/29 21:00:34 martinh Exp $ */ +/* $OpenBSD: ldape.c,v 1.8 2010/06/29 21:54:38 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -103,6 +103,84 @@ fail: ber_free_elements(root); } +int +ldap_refer(struct request *req, const char *basedn, struct search *search, + struct referrals *refs) +{ + struct ber_element *root, *elm, *ref_root = NULL; + struct referral *ref; + long long result_code = LDAP_REFERRAL; + unsigned long type; + int rc; + void *buf; + char *url, *scope_str = NULL; + + if (req->type == LDAP_REQ_SEARCH) + type = LDAP_RES_SEARCH_RESULT; + else + type = req->type + 1; + + if (search != NULL) { + if (search->scope != LDAP_SCOPE_SUBTREE) + scope_str = "base"; + else + scope_str = "sub"; + } + + log_debug("sending referral in response %u on msgid %i", type, req->msgid); + + if ((root = ber_add_sequence(NULL)) == NULL) + goto fail; + + if ((elm = ref_root = ber_add_sequence(NULL)) == NULL) + goto fail; + ber_set_header(ref_root, BER_CLASS_CONTEXT, LDAP_REQ_SEARCH); + SLIST_FOREACH(ref, refs, next) { + if (search != NULL) + asprintf(&url, "%s/%s??%s", ref->url, basedn, + scope_str); + else + asprintf(&url, "%s/%s", ref->url, basedn); + if (url == NULL) { + log_warn("asprintf"); + goto fail; + } + log_debug("adding referral '%s'", url); + elm = ber_add_string(elm, url); + free(url); + if (elm == NULL) + goto fail; + } + + elm = ber_printf_elements(root, "d{tEsse", + req->msgid, BER_CLASS_APP, type, result_code, "", "", ref_root); + if (elm == NULL) + goto fail; + ref_root = NULL; + + rc = ber_write_elements(&req->conn->ber, root); + ber_free_elements(root); + + if (rc < 0) + log_warn("failed to create ldap result"); + else { + ber_get_writebuf(&req->conn->ber, &buf); + if (bufferevent_write(req->conn->bev, buf, rc) != 0) + log_warn("failed to send ldap result"); + } + + request_free(req); + return LDAP_REFERRAL; + +fail: + if (root != NULL) + ber_free_elements(root); + if (ref_root != NULL) + ber_free_elements(ref_root); + request_free(req); + return LDAP_REFERRAL; +} + void send_ldap_result(struct conn *conn, int msgid, unsigned long type, long long result_code) @@ -292,7 +370,7 @@ ldape(struct passwd *pw, char *csockpath, int pipe_parent2ldap[2]) } TAILQ_FOREACH(ns, &conf->namespaces, next) { - if (namespace_open(ns) != 0) + if (!namespace_has_referrals(ns) && namespace_open(ns) != 0) fatal(ns->suffix); } diff --git a/usr.sbin/ldapd/modify.c b/usr.sbin/ldapd/modify.c index 12507c276d3..a14d3c9efba 100644 --- a/usr.sbin/ldapd/modify.c +++ b/usr.sbin/ldapd/modify.c @@ -1,4 +1,4 @@ -/* $OpenBSD: modify.c,v 1.5 2010/06/29 02:54:20 martinh Exp $ */ +/* $OpenBSD: modify.c,v 1.6 2010/06/29 21:54:38 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -32,6 +32,7 @@ ldap_delete(struct request *req) { char *dn; struct namespace *ns; + struct referrals *refs; ++stats.req_mod; @@ -41,8 +42,13 @@ ldap_delete(struct request *req) normalize_dn(dn); log_debug("deleting entry %s", dn); - if ((ns = namespace_for_base(dn)) == NULL) - return ldap_respond(req, LDAP_NAMING_VIOLATION); + if ((ns = namespace_for_base(dn)) == NULL) { + refs = namespace_referrals(dn); + if (refs == NULL) + return ldap_respond(req, LDAP_NAMING_VIOLATION); + else + return ldap_refer(req, dn, NULL, refs); + } if (!authorized(req->conn, ns, ACI_WRITE, dn, LDAP_SCOPE_BASE)) return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS); @@ -76,6 +82,7 @@ ldap_add(struct request *req) char *dn; struct ber_element *attrs, *set; struct namespace *ns; + struct referrals *refs; int rc; ++stats.req_mod; @@ -89,8 +96,13 @@ ldap_add(struct request *req) if (*dn == '\0') return ldap_respond(req, LDAP_INVALID_DN_SYNTAX); - if ((ns = namespace_for_base(dn)) == NULL) - return ldap_respond(req, LDAP_NAMING_VIOLATION); + if ((ns = namespace_for_base(dn)) == NULL) { + refs = namespace_referrals(dn); + if (refs == NULL) + return ldap_respond(req, LDAP_NAMING_VIOLATION); + else + return ldap_refer(req, dn, NULL, refs); + } if (!authorized(req->conn, ns, ACI_WRITE, dn, LDAP_SCOPE_BASE) != 0) return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS); @@ -143,6 +155,7 @@ ldap_modify(struct request *req) struct ber_element *mods, *entry, *mod, *vals, *a, *set; struct namespace *ns; struct attr_type *at; + struct referrals *refs; ++stats.req_mod; @@ -155,8 +168,13 @@ ldap_modify(struct request *req) if (*dn == 0) return ldap_respond(req, LDAP_INVALID_DN_SYNTAX); - if ((ns = namespace_for_base(dn)) == NULL) - return ldap_respond(req, LDAP_NAMING_VIOLATION); + if ((ns = namespace_for_base(dn)) == NULL) { + refs = namespace_referrals(dn); + if (refs == NULL) + return ldap_respond(req, LDAP_NAMING_VIOLATION); + else + return ldap_refer(req, dn, NULL, refs); + } if (!authorized(req->conn, ns, ACI_WRITE, dn, LDAP_SCOPE_BASE) != 0) return ldap_respond(req, LDAP_INSUFFICIENT_ACCESS); diff --git a/usr.sbin/ldapd/namespace.c b/usr.sbin/ldapd/namespace.c index 72eb3743bd9..741ac5a2583 100644 --- a/usr.sbin/ldapd/namespace.c +++ b/usr.sbin/ldapd/namespace.c @@ -1,4 +1,4 @@ -/* $OpenBSD: namespace.c,v 1.8 2010/06/23 13:10:14 martinh Exp $ */ +/* $OpenBSD: namespace.c,v 1.9 2010/06/29 21:54:38 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -396,22 +396,54 @@ namespace_del(struct namespace *ns, char *dn) return rc; } +int +namespace_has_referrals(struct namespace *ns) +{ + return !SLIST_EMPTY(&ns->referrals); +} + struct namespace * -namespace_for_base(const char *basedn) +namespace_lookup_base(const char *basedn, int include_referrals) { size_t blen, slen; - struct namespace *ns; + struct namespace *ns, *matched_ns = NULL; assert(basedn); blen = strlen(basedn); TAILQ_FOREACH(ns, &conf->namespaces, next) { slen = strlen(ns->suffix); - if (blen >= slen && - bcmp(basedn + blen - slen, ns->suffix, slen) == 0) - return ns; + if ((include_referrals || !namespace_has_referrals(ns)) && + blen >= slen && + bcmp(basedn + blen - slen, ns->suffix, slen) == 0) { + /* Match the longest namespace suffix. */ + if (matched_ns == NULL || + strlen(ns->suffix) > strlen(matched_ns->suffix)) + matched_ns = ns; + } } + return matched_ns; +} + +struct namespace * +namespace_for_base(const char *basedn) +{ + return namespace_lookup_base(basedn, 0); +} + +struct referrals * +namespace_referrals(const char *basedn) +{ + struct namespace *ns; + + if ((ns = namespace_lookup_base(basedn, 1)) != NULL && + namespace_has_referrals(ns)) + return &ns->referrals; + + if (!SLIST_EMPTY(&conf->referrals)) + return &conf->referrals; + return NULL; } diff --git a/usr.sbin/ldapd/parse.y b/usr.sbin/ldapd/parse.y index 09b8d87c3d4..8297ab90292 100644 --- a/usr.sbin/ldapd/parse.y +++ b/usr.sbin/ldapd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.5 2010/06/29 02:45:46 martinh Exp $ */ +/* $OpenBSD: parse.y,v 1.6 2010/06/29 21:54:38 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martinh@openbsd.org> @@ -108,8 +108,7 @@ static struct namespace *current_ns = NULL; %token ERROR LISTEN ON TLS LDAPS PORT NAMESPACE ROOTDN ROOTPW INDEX %token SECURE RELAX STRICT SCHEMA USE COMPRESSION LEVEL %token INCLUDE CERTIFICATE FSYNC CACHE_SIZE INDEX_CACHE_SIZE -%token DISTRIBUTED_OPERATION DSA_OPERATION -%token DENY ALLOW READ WRITE BIND ACCESS TO ROOT +%token DENY ALLOW READ WRITE BIND ACCESS TO ROOT REFERRAL %token ANY CHILDREN OF ATTRIBUTE IN SUBTREE BY SELF %token <v.string> STRING %token <v.number> NUMBER @@ -200,6 +199,16 @@ conf_main : LISTEN ON STRING port ssl certname { free($6); free($3); } + | REFERRAL STRING { + struct referral *ref; + if ((ref = calloc(1, sizeof(*ref))) == NULL) { + yyerror("calloc"); + free($2); + YYERROR; + } + ref->url = $2; + SLIST_INSERT_HEAD(&conf->referrals, ref, next); + } ; namespace : NAMESPACE STRING '{' '\n' { @@ -258,6 +267,16 @@ ns_opt : ROOTDN STRING { | RELAX SCHEMA { current_ns->relax = 1; } | STRICT SCHEMA { current_ns->relax = 0; } | USE COMPRESSION comp_level { current_ns->compression_level = $3; } + | REFERRAL STRING { + struct referral *ref; + if ((ref = calloc(1, sizeof(*ref))) == NULL) { + yyerror("calloc"); + free($2); + YYERROR; + } + ref->url = $2; + SLIST_INSERT_HEAD(¤t_ns->referrals, ref, next); + } ; comp_level : /* empty */ { $$ = 6; } @@ -405,6 +424,7 @@ lookup(char *s) { "on", ON }, { "port", PORT }, { "read", READ }, + { "referral", REFERRAL }, { "relax", RELAX }, { "root", ROOT }, { "rootdn", ROOTDN }, @@ -759,6 +779,7 @@ parse_config(char *filename) fatal(NULL); SPLAY_INIT(conf->sc_ssl); SIMPLEQ_INIT(&conf->acl); + SLIST_INIT(&conf->referrals); if ((file = pushfile(filename, 1)) == NULL) { free(conf); @@ -1128,6 +1149,7 @@ namespace_new(const char *suffix) TAILQ_INIT(&ns->indices); TAILQ_INIT(&ns->request_queue); SIMPLEQ_INIT(&ns->acl); + SLIST_INIT(&ns->referrals); return ns; } diff --git a/usr.sbin/ldapd/search.c b/usr.sbin/ldapd/search.c index f60ce2f09de..e9e73b221a1 100644 --- a/usr.sbin/ldapd/search.c +++ b/usr.sbin/ldapd/search.c @@ -1,4 +1,4 @@ -/* $OpenBSD: search.c,v 1.7 2010/06/29 02:45:46 martinh Exp $ */ +/* $OpenBSD: search.c,v 1.8 2010/06/29 21:54:38 martinh Exp $ */ /* * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> @@ -705,6 +705,7 @@ int ldap_search(struct request *req) { long long reason = LDAP_OTHER; + struct referrals *refs; struct search *search = NULL; int rc; @@ -779,6 +780,13 @@ ldap_search(struct request *req) } if ((search->ns = namespace_for_base(search->basedn)) == NULL) { + refs = namespace_referrals(search->basedn); + if (refs != NULL) { + ldap_refer(req, search->basedn, search, refs); + search->req = NULL; /* request free'd by ldap_refer */ + search_close(search); + return LDAP_REFERRAL; + } log_debug("no database configured for suffix %s", search->basedn); reason = LDAP_NO_SUCH_OBJECT; |