1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_ldap/kdb_ldap.c */
4 * Copyright (c) 2004-2005, Novell, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
10 * * Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * The copyright holder's name is not used to endorse or promote products
16 * derived from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
38 #include "ldap_misc.h"
40 #include <kadm5/admin.h>
42 #if !defined(isblank) && defined(HAVE_ISBLANK)
43 #if defined(NEED_ISBLANK_PROTO)
46 #else /* isblank missing */
48 #define isblank isspace
53 krb5_ldap_get_db_opt(char *input, char **opt, char **val)
55 char *pos = strchr(input, '=');
64 int len = pos - input;
65 *opt = malloc((unsigned) len + 1);
69 memcpy(*opt, input, (unsigned) len);
70 /* ignore trailing blanks */
71 while (isblank((*opt)[len-1]))
75 pos += 1; /* move past '=' */
76 while (isblank(*pos)) /* ignore leading blanks */
95 krb5_ldap_get_age(context, db_name, age)
105 * read startup information - kerberos and realm container
108 krb5_ldap_read_startup_information(krb5_context context)
110 krb5_error_code retval = 0;
111 kdb5_dal_handle *dal_handle=NULL;
112 krb5_ldap_context *ldap_context=NULL;
116 if ((retval=krb5_ldap_read_krbcontainer_params(context, &(ldap_context->krbcontainer)))) {
117 prepend_err_str(context, _("Unable to read Kerberos container"),
122 if ((retval=krb5_ldap_read_realm_params(context, context->default_realm, &(ldap_context->lrparams), &mask))) {
123 prepend_err_str(context, _("Unable to read Realm"), retval, retval);
127 if (((mask & LDAP_REALM_MAXTICKETLIFE) == 0) || ((mask & LDAP_REALM_MAXRENEWLIFE) == 0)
128 || ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0)) {
129 kadm5_config_params params_in, params_out;
131 memset(¶ms_in, 0, sizeof(params_in));
132 memset(¶ms_out, 0, sizeof(params_out));
134 retval = kadm5_get_config_params(context, 1, ¶ms_in, ¶ms_out);
136 if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
137 ldap_context->lrparams->max_life = 24 * 60 * 60; /* 1 day */
139 if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
140 ldap_context->lrparams->max_renewable_life = 0;
142 if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
143 ldap_context->lrparams->tktflags = KRB5_KDB_DEF_FLAGS;
149 if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
150 if (params_out.mask & KADM5_CONFIG_MAX_LIFE)
151 ldap_context->lrparams->max_life = params_out.max_life;
154 if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
155 if (params_out.mask & KADM5_CONFIG_MAX_RLIFE)
156 ldap_context->lrparams->max_renewable_life = params_out.max_rlife;
159 if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
160 if (params_out.mask & KADM5_CONFIG_FLAGS)
161 ldap_context->lrparams->tktflags = params_out.flags;
164 kadm5_free_config_params(context, ¶ms_out);
173 * Interrogate the root DSE (zero length DN) for an attribute
177 has_rootdse_ava(krb5_context context, char *ldap_server, char *attribute,
180 int i=0, flag=0, ret=0, retval=0;
181 char *attrs[2], **values=NULL;
183 LDAPMessage *msg=NULL, *res=NULL;
186 attrs[0] = attribute;
189 retval = ldap_initialize(&ld, ldap_server);
190 if (retval != LDAP_SUCCESS) {
191 ret = 2; /* Don't know */
199 retval = ldap_sasl_bind_s(ld, "", NULL, &cred, NULL, NULL, NULL);
200 if (retval != LDAP_SUCCESS) {
201 ret = 2; /* Don't know */
205 retval = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, NULL, NULL, NULL, 0, &res);
206 if (retval != LDAP_SUCCESS) {
207 ret = 2; /* Don't know */
211 msg = ldap_first_message(ld, res);
213 ret = 2; /* Don't know */
217 values = ldap_get_values(ld, msg, attribute);
218 if (values == NULL) {
219 ret = 1; /* Not supported */
223 for (i = 0; values[i] != NULL; i++) {
224 if (strcmp(values[i], value) == 0) {
231 ret = 1; /* Not supported */
238 ldap_value_free(values);
244 ldap_unbind_ext_s(ld, NULL, NULL);
249 #define ERR_MSG1 _("Unable to check if SASL EXTERNAL mechanism is supported by LDAP server. Proceeding anyway ...")
250 #define ERR_MSG2 _("SASL EXTERNAL mechanism not supported by LDAP server. Can't perform certificate-based bind.")
252 /* Function to check if a LDAP server supports the SASL external mechanism
255 * 1 => does not support
259 has_sasl_external_mech(krb5_context context, char *ldap_server)
263 ret = has_rootdse_ava(context, ldap_server,
264 "supportedSASLMechanisms", "EXTERNAL");
266 case 1: /* not supported */
267 krb5_set_error_message(context, 1, "%s", ERR_MSG2);
269 case 2: /* don't know */
270 krb5_set_error_message(context, 1, "%s", ERR_MSG1);
280 has_modify_increment(context, ldap_server)
281 krb5_context context;
284 return has_rootdse_ava(context, ldap_server,
285 "supportedFeatures", "1.3.6.1.1.14");
289 krb5_ldap_alloc(krb5_context context, void *ptr, size_t size)
291 return realloc(ptr, size);
295 krb5_ldap_free(krb5_context context, void *ptr)
301 krb5_ldap_open(krb5_context context, char *conf_section, char **db_args,
304 krb5_error_code status = 0;
305 char **t_ptr = db_args;
306 krb5_ldap_context *ldap_context=NULL;
308 kdb5_dal_handle *dal_handle=NULL;
310 /* Clear the global error string */
311 krb5_clear_error_message(context);
313 ldap_context = calloc(1, sizeof(krb5_ldap_context));
314 if (ldap_context == NULL) {
319 ldap_context->kcontext = context;
321 while (t_ptr && *t_ptr) {
322 char *opt = NULL, *val = NULL;
324 if ((status = krb5_ldap_get_db_opt(*t_ptr, &opt, &val)) != 0) {
327 if (opt && !strcmp(opt, "binddn")) {
328 if (ldap_context->bind_dn) {
332 krb5_set_error_message(context, status, _("'binddn' missing"));
337 krb5_set_error_message(context, status,
338 _("'binddn' value missing"));
342 ldap_context->bind_dn = strdup(val);
343 if (ldap_context->bind_dn == NULL) {
349 } else if (opt && !strcmp(opt, "nconns")) {
350 if (ldap_context->max_server_conns) {
354 krb5_set_error_message(context, status, _("'nconns' missing"));
359 krb5_set_error_message(context, status,
360 _("'nconns' value missing"));
364 ldap_context->max_server_conns = atoi(val) ? atoi(val) : DEFAULT_CONNS_PER_SERVER;
365 } else if (opt && !strcmp(opt, "bindpwd")) {
366 if (ldap_context->bind_pwd) {
370 krb5_set_error_message(context, status,
371 _("'bindpwd' missing"));
376 krb5_set_error_message(context, status,
377 _("'bindpwd' value missing"));
381 ldap_context->bind_pwd = strdup(val);
382 if (ldap_context->bind_pwd == NULL) {
388 } else if (opt && !strcmp(opt, "host")) {
391 krb5_set_error_message(context, status,
392 _("'host' value missing"));
396 if (ldap_context->server_info_list == NULL)
397 ldap_context->server_info_list = (krb5_ldap_server_info **) calloc (SERV_COUNT+1, sizeof (krb5_ldap_server_info *)) ;
399 if (ldap_context->server_info_list == NULL) {
406 ldap_context->server_info_list[srv_cnt] = (krb5_ldap_server_info *) calloc (1, sizeof (krb5_ldap_server_info));
407 if (ldap_context->server_info_list[srv_cnt] == NULL) {
414 ldap_context->server_info_list[srv_cnt]->server_status = NOTSET;
416 ldap_context->server_info_list[srv_cnt]->server_name = strdup(val);
417 if (ldap_context->server_info_list[srv_cnt]->server_name == NULL) {
425 #ifdef HAVE_EDIRECTORY
426 } else if (opt && !strcmp(opt, "cert")) {
429 krb5_set_error_message(context, status,
430 _("'cert' value missing"));
435 if (ldap_context->root_certificate_file == NULL) {
436 ldap_context->root_certificate_file = strdup(val);
437 if (ldap_context->root_certificate_file == NULL) {
446 if (asprintf(&newstr, "%s %s",
447 ldap_context->root_certificate_file, val) < 0) {
453 free(ldap_context->root_certificate_file);
454 ldap_context->root_certificate_file = newstr;
458 /* ignore hash argument. Might have been passed from create */
460 if (opt && !strcmp(opt, "temporary")) {
462 * temporary is passed in when kdb5_util load without -update is done.
463 * This is unsupported by the LDAP plugin.
465 krb5_set_error_message(context, status,
466 _("open of LDAP directory aborted, "
467 "plugin requires -update argument"));
469 krb5_set_error_message (context, status,
470 _("unknown option \'%s\'"),
483 dal_handle = context->dal_handle;
484 dal_handle->db_context = ldap_context;
485 status = krb5_ldap_read_server_params(context, conf_section, mode & 0x0300);
488 krb5_ldap_free_ldap_context(ldap_context);
490 dal_handle->db_context = NULL;
491 prepend_err_str(context, _("Error reading LDAP server params: "),
495 if ((status=krb5_ldap_db_init(context, ldap_context)) != 0) {
499 if ((status=krb5_ldap_read_startup_information(context)) != 0) {
504 /* may be clearing up is not required db_fini might do it for us, check out */
506 krb5_ldap_close(context);
511 #include "ldap_err.h"
513 set_ldap_error(krb5_context ctx, int st, int op)
515 int translated_st = translate_ldap_error(st, op);
516 krb5_set_error_message(ctx, translated_st, "%s", ldap_err2string(st));
517 return translated_st;
521 prepend_err_str(krb5_context ctx, const char *str, krb5_error_code err,
522 krb5_error_code oerr)
525 if (oerr == 0) oerr = err;
526 omsg = krb5_get_error_message (ctx, err);
527 krb5_set_error_message (ctx, err, "%s %s", str, omsg);
530 extern krb5int_access accessor;
531 MAKE_INIT_FUNCTION(kldap_init_fn);
536 /* Global (per-module) initialization. */
537 return krb5int_accessor (&accessor, KRB5INT_ACCESS_VERSION);
541 kldap_ensure_initialized(void)
543 return CALL_INIT_FUNCTION (kldap_init_fn);
547 krb5_ldap_check_policy_as(krb5_context kcontext, krb5_kdc_req *request,
548 krb5_db_entry *client, krb5_db_entry *server,
549 krb5_timestamp kdc_time, const char **status,
550 krb5_pa_data ***e_data)
552 krb5_error_code retval;
554 retval = krb5_ldap_lockout_check_policy(kcontext, client, kdc_time);
555 if (retval == KRB5KDC_ERR_CLIENT_REVOKED)
556 *status = "LOCKED_OUT";
561 krb5_ldap_audit_as_req(krb5_context kcontext, krb5_kdc_req *request,
562 krb5_db_entry *client, krb5_db_entry *server,
563 krb5_timestamp authtime, krb5_error_code error_code)
565 (void) krb5_ldap_lockout_audit(kcontext, client, authtime, error_code);
569 krb5_ldap_check_allowed_to_delegate(krb5_context context,
570 krb5_const_principal client,
571 const krb5_db_entry *server,
572 krb5_const_principal proxy)
574 krb5_error_code code;
577 code = KRB5KDC_ERR_POLICY;
579 for (tlp = server->tl_data; tlp != NULL; tlp = tlp->tl_data_next) {
582 if (tlp->tl_data_type != KRB5_TL_CONSTRAINED_DELEGATION_ACL)
585 if (krb5_parse_name(context, (char *)tlp->tl_data_contents, &acl) != 0)
588 if (krb5_principal_compare(context, proxy, acl)) {
590 krb5_free_principal(context, acl);
593 krb5_free_principal(context, acl);