1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_ldap/ldap_misc.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.
31 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
32 * Use is subject to license terms.
35 #include "ldap_misc.h"
36 #include "ldap_handle.h"
38 #include "ldap_principal.h"
39 #include "princ_xdr.h"
40 #include "ldap_pwd_policy.h"
43 #include <kadm5/admin.h>
45 #ifdef NEED_STRPTIME_PROTO
46 extern char *strptime(const char *, const char *, struct tm *);
49 static void remove_overlapping_subtrees(char **listin, int *subtcount,
52 /* Set an extended error message about being unable to read name. */
53 static krb5_error_code
54 attr_read_error(krb5_context ctx, krb5_error_code code, const char *name)
56 k5_setmsg(ctx, code, _("Error reading '%s' attribute: %s"), name,
61 /* Get integer or string values from the config section, falling back to the
62 * default section, then to hard-coded values. */
63 static krb5_error_code
64 prof_get_integer_def(krb5_context ctx, const char *conf_section,
65 const char *name, int dfl, krb5_ui_4 *out)
70 ret = profile_get_integer(ctx->profile, KDB_MODULE_SECTION, conf_section,
73 return attr_read_error(ctx, ret, name);
78 ret = profile_get_integer(ctx->profile, KDB_MODULE_DEF_SECTION, name, NULL,
81 return attr_read_error(ctx, ret, name);
86 /* Get integer or string values from the config section, falling back to the
87 * default section, then to hard-coded values. */
88 static krb5_error_code
89 prof_get_boolean_def(krb5_context ctx, const char *conf_section,
90 const char *name, krb5_boolean dfl, krb5_boolean *out)
95 ret = profile_get_boolean(ctx->profile, KDB_MODULE_SECTION, conf_section,
98 return attr_read_error(ctx, ret, name);
103 ret = profile_get_boolean(ctx->profile, KDB_MODULE_DEF_SECTION, name, NULL,
106 return attr_read_error(ctx, ret, name);
111 /* We don't have non-null defaults in any of our calls, so don't bother with
112 * the extra argument. */
113 static krb5_error_code
114 prof_get_string_def(krb5_context ctx, const char *conf_section,
115 const char *name, char **out)
119 ret = profile_get_string(ctx->profile, KDB_MODULE_SECTION, conf_section,
122 return attr_read_error(ctx, ret, name);
125 ret = profile_get_string(ctx->profile, KDB_MODULE_DEF_SECTION, name, NULL,
128 return attr_read_error(ctx, ret, name);
132 static krb5_error_code
133 get_db_opt(const char *input, char **opt_out, char **val_out)
137 char *opt, *val = NULL;
140 *opt_out = *val_out = NULL;
141 pos = strchr(input, '=');
148 /* Ignore trailing spaces. */
149 while (len > 0 && isspace((unsigned char)input[len - 1]))
151 opt = k5memdup0(input, len, &ret);
155 pos++; /* Move past '='. */
156 while (isspace(*pos)) /* Ignore leading spaces. */
171 static krb5_error_code
172 add_server_entry(krb5_context context, const char *name)
174 krb5_ldap_context *ctx = context->dal_handle->db_context;
175 krb5_ldap_server_info **sp, **list, *server;
178 /* Allocate list space for the new entry and null terminator. */
179 for (sp = ctx->server_info_list; sp != NULL && *sp != NULL; sp++)
181 list = realloc(ctx->server_info_list, (count + 2) * sizeof(*list));
184 ctx->server_info_list = list;
186 server = calloc(1, sizeof(krb5_ldap_server_info));
189 server->server_status = NOTSET;
190 server->server_name = strdup(name);
191 if (server->server_name == NULL) {
195 list[count] = server;
196 list[count + 1] = NULL;
201 krb5_ldap_parse_db_params(krb5_context context, char **db_args)
203 char *opt = NULL, *val = NULL;
204 krb5_error_code ret = 0;
205 krb5_ldap_context *ctx = context->dal_handle->db_context;
209 for (; *db_args != NULL; db_args++) {
210 ret = get_db_opt(*db_args, &opt, &val);
214 /* Check for options which don't require values. */
215 if (!strcmp(opt, "temporary")) {
216 /* "temporary" is passed by kdb5_util load without -update,
217 * which we don't support. */
219 k5_setmsg(context, ret, _("KDB module requires -update argument"));
225 k5_setmsg(context, ret, _("'%s' value missing"), opt);
229 /* Check for options which do require arguments. */
230 if (!strcmp(opt, "binddn")) {
232 ctx->bind_dn = strdup(val);
233 if (ctx->bind_dn == NULL) {
237 } else if (!strcmp(opt, "nconns")) {
238 ctx->max_server_conns = atoi(val) ? atoi(val) :
239 DEFAULT_CONNS_PER_SERVER;
240 } else if (!strcmp(opt, "bindpwd")) {
242 ctx->bind_pwd = strdup(val);
243 if (ctx->bind_pwd == NULL) {
247 } else if (!strcmp(opt, "sasl_mech")) {
248 free(ctx->sasl_mech);
249 ctx->sasl_mech = strdup(val);
250 if (ctx->sasl_mech == NULL) {
254 } else if (!strcmp(opt, "sasl_authcid")) {
255 free(ctx->sasl_authcid);
256 ctx->sasl_authcid = strdup(val);
257 if (ctx->sasl_authcid == NULL) {
261 } else if (!strcmp(opt, "sasl_authzid")) {
262 free(ctx->sasl_authzid);
263 ctx->sasl_authzid = strdup(val);
264 if (ctx->sasl_authzid == NULL) {
268 } else if (!strcmp(opt, "sasl_realm")) {
269 free(ctx->sasl_realm);
270 ctx->sasl_realm = strdup(val);
271 if (ctx->sasl_realm == NULL) {
275 } else if (!strcmp(opt, "host")) {
276 ret = add_server_entry(context, val);
279 } else if (!strcmp(opt, "debug")) {
280 ctx->ldap_debug = atoi(val);
283 k5_setmsg(context, ret, _("unknown option '%s'"), opt);
298 /* Pick kdc_var or kadmind_var depending on the server type. */
299 static inline const char *
300 choose_var(int srv_type, const char *kdc_var, const char *kadmind_var)
302 return (srv_type == KRB5_KDB_SRV_TYPE_KDC) ? kdc_var : kadmind_var;
306 * This function reads the parameters from the krb5.conf file. The
307 * parameters read here are DAL-LDAP specific attributes. Some of
308 * these are ldap_server ....
311 krb5_ldap_read_server_params(krb5_context context, char *conf_section,
314 char *servers, *save_ptr, *item;
315 const char *delims = "\t\n\f\v\r ,", *name;
316 krb5_error_code ret = 0;
317 kdb5_dal_handle *dal_handle = context->dal_handle;
318 krb5_ldap_context *ldap_context = dal_handle->db_context;
320 /* copy the conf_section into ldap_context for later use */
321 if (conf_section != NULL) {
322 ldap_context->conf_section = strdup(conf_section);
323 if (ldap_context->conf_section == NULL)
327 /* This mutex is used in the LDAP connection pool. */
328 if (k5_mutex_init(&(ldap_context->hndl_lock)) != 0)
329 return KRB5_KDB_SERVER_INTERNAL_ERR;
331 /* Read the maximum number of LDAP connections per server. */
332 if (ldap_context->max_server_conns == 0) {
333 ret = prof_get_integer_def(context, conf_section,
334 KRB5_CONF_LDAP_CONNS_PER_SERVER,
335 DEFAULT_CONNS_PER_SERVER,
336 &ldap_context->max_server_conns);
341 if (ldap_context->max_server_conns < 2) {
342 k5_setmsg(context, EINVAL,
343 _("Minimum connections required per server is 2"));
347 /* Read the DN used to connect to the LDAP server. */
348 if (ldap_context->bind_dn == NULL) {
349 name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_DN,
350 KRB5_CONF_LDAP_KADMIND_DN);
351 ret = prof_get_string_def(context, conf_section, name,
352 &ldap_context->bind_dn);
357 /* Read the filename containing stashed DN passwords. */
358 if (ldap_context->service_password_file == NULL) {
359 ret = prof_get_string_def(context, conf_section,
360 KRB5_CONF_LDAP_SERVICE_PASSWORD_FILE,
361 &ldap_context->service_password_file);
366 if (ldap_context->sasl_mech == NULL) {
367 name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_MECH,
368 KRB5_CONF_LDAP_KADMIND_SASL_MECH);
369 ret = prof_get_string_def(context, conf_section, name,
370 &ldap_context->sasl_mech);
375 if (ldap_context->sasl_authcid == NULL) {
376 name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_AUTHCID,
377 KRB5_CONF_LDAP_KADMIND_SASL_AUTHCID);
378 ret = prof_get_string_def(context, conf_section, name,
379 &ldap_context->sasl_authcid);
384 if (ldap_context->sasl_authzid == NULL) {
385 name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_AUTHZID,
386 KRB5_CONF_LDAP_KADMIND_SASL_AUTHZID);
387 ret = prof_get_string_def(context, conf_section, name,
388 &ldap_context->sasl_authzid);
393 if (ldap_context->sasl_realm == NULL) {
394 name = choose_var(srv_type, KRB5_CONF_LDAP_KDC_SASL_REALM,
395 KRB5_CONF_LDAP_KADMIND_SASL_REALM);
396 ret = prof_get_string_def(context, conf_section, name,
397 &ldap_context->sasl_realm);
402 /* Read the LDAP server URL list. */
403 if (ldap_context->server_info_list == NULL) {
404 ret = profile_get_string(context->profile, KDB_MODULE_SECTION,
405 conf_section, KRB5_CONF_LDAP_SERVERS, NULL,
408 return attr_read_error(context, ret, KRB5_CONF_LDAP_SERVERS);
410 if (servers == NULL) {
411 ret = add_server_entry(context, "ldapi://");
415 item = strtok_r(servers, delims, &save_ptr);
416 while (item != NULL) {
417 ret = add_server_entry(context, item);
419 profile_release_string(servers);
422 item = strtok_r(NULL, delims, &save_ptr);
424 profile_release_string(servers);
428 ret = prof_get_boolean_def(context, conf_section,
429 KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE,
430 &ldap_context->disable_last_success);
434 return prof_get_boolean_def(context, conf_section,
435 KRB5_CONF_DISABLE_LOCKOUT, FALSE,
436 &ldap_context->disable_lockout);
440 krb5_ldap_free_server_context_params(krb5_ldap_context *ctx)
443 krb5_ldap_server_info **list;
444 krb5_ldap_server_handle *h, *next;
449 list = ctx->server_info_list;
450 for (i = 0; list != NULL && list[i] != NULL; i++) {
451 free(list[i]->server_name);
452 for (h = list[i]->ldap_server_handles; h != NULL; h = next) {
454 ldap_unbind_ext_s(h->ldap_handle, NULL, NULL);
460 ctx->server_info_list = NULL;
462 free(ctx->sasl_mech);
463 free(ctx->sasl_authcid);
464 free(ctx->sasl_authzid);
465 free(ctx->sasl_realm);
466 free(ctx->conf_section);
468 zapfreestr(ctx->bind_pwd);
469 free(ctx->service_password_file);
470 ctx->conf_section = ctx->bind_dn = ctx->bind_pwd = NULL;
471 ctx->service_password_file = NULL;
475 krb5_ldap_free_server_params(krb5_ldap_context *ctx)
479 krb5_ldap_free_server_context_params(ctx);
480 k5_mutex_destroy(&ctx->hndl_lock);
484 /* Return true if princ is in the default realm of ldap_context or is a
485 * cross-realm TGS principal for that realm. */
487 is_principal_in_realm(krb5_ldap_context *ldap_context,
488 krb5_const_principal princ)
490 const char *realm = ldap_context->lrparams->realm_name;
492 if (princ->length == 2 &&
493 data_eq_string(princ->data[0], "krbtgt") &&
494 data_eq_string(princ->data[1], realm))
496 return data_eq_string(princ->realm, realm);
500 * Deduce the subtree information from the context. A realm can have
502 * 1. the Realm container
503 * 2. the actual subtrees associated with the Realm
505 * However, there are some conditions to be considered to deduce the
506 * actual subtree/s associated with the realm. The conditions are as
508 * 1. If the subtree information of the Realm is [Root] or NULL (that
509 * is internal a [Root]) then the realm has only one subtree
510 * i.e [Root], i.e. whole of the tree.
511 * 2. If the subtree information of the Realm is missing/absent, then the
512 * realm has only one, i.e., the Realm container. NOTE: In all cases
513 * Realm container SHOULD be the one among the subtrees or the only
515 * 3. The subtree information of the realm is overlapping the realm
516 * container of the realm, then the realm has only one subtree and
517 * it is the subtree information associated with the realm.
520 krb5_get_subtree_info(krb5_ldap_context *ldap_context, char ***subtreearr,
524 int subtreecount, count = 0, search_scope;
525 char **subtree, *realm_cont_dn, *containerref;
526 char **subtarr = NULL;
528 containerref = ldap_context->lrparams->containerref;
529 subtree = ldap_context->lrparams->subtree;
530 realm_cont_dn = ldap_context->lrparams->realmdn;
531 subtreecount = ldap_context->lrparams->subtreecount;
532 search_scope = ldap_context->lrparams->search_scope;
534 /* Leave space for realm DN, containerref, and null terminator. */
535 subtarr = k5calloc(subtreecount + 3, sizeof(char *), &ret);
539 /* Get the complete subtree list. */
540 while (count < subtreecount && subtree[count] != NULL) {
541 subtarr[count] = strdup(subtree[count]);
542 if (subtarr[count++] == NULL) {
548 subtarr[count] = strdup(realm_cont_dn);
549 if (subtarr[count++] == NULL) {
554 if (containerref != NULL) {
555 subtarr[count] = strdup(containerref);
556 if (subtarr[count++] == NULL) {
562 remove_overlapping_subtrees(subtarr, &count, search_scope);
564 *subtreearr = subtarr;
570 free(subtarr[--count]);
575 /* Reallocate tl and return a pointer to the new space, or NULL on failure. */
576 static unsigned char *
577 expand_tl_data(krb5_tl_data *tl, uint16_t len)
579 unsigned char *newptr;
581 if (len > UINT16_MAX - tl->tl_data_length)
583 newptr = realloc(tl->tl_data_contents, tl->tl_data_length + len);
586 tl->tl_data_contents = newptr;
587 tl->tl_data_length += len;
588 return tl->tl_data_contents + tl->tl_data_length - len;
591 /* Append a one-byte type, a two-byte length, and a value to a KDB_TL_USER_INFO
592 * tl_data item. The length is inferred from type and value. */
594 store_tl_data(krb5_tl_data *tl, int type, void *value)
601 tl->tl_data_type = KDB_TL_USER_INFO;
603 case KDB_TL_PRINCCOUNT:
604 case KDB_TL_PRINCTYPE:
606 ival = *(int *)value;
607 if (ival > UINT16_MAX)
609 ptr = expand_tl_data(tl, 5);
613 store_16_be(2, ptr + 1);
614 store_16_be(ival, ptr + 3);
621 if (len > UINT16_MAX - 3)
623 ptr = expand_tl_data(tl, 3 + len);
627 store_16_be(len, ptr + 1);
628 memcpy(ptr + 3, str, len);
637 /* Scan tl for a value of the given type and return it in allocated memory.
638 * For KDB_TL_LINKDN, return a list of all values found. */
639 static krb5_error_code
640 decode_tl_data(krb5_tl_data *tl, int type, void **data_out)
643 const unsigned char *ptr, *end;
645 size_t linkcount = 0, i;
646 char **dnlist = NULL, **newlist;
651 /* Find the first matching subfield or return ENOENT. For KDB_TL_LINKDN,
652 * keep iterating after finding a match as it may be repeated. */
653 ptr = tl->tl_data_contents;
654 end = ptr + tl->tl_data_length;
658 len = load_16_be(ptr + 1);
659 if (len > (end - ptr) - 3)
668 case KDB_TL_PRINCCOUNT:
669 case KDB_TL_PRINCTYPE:
673 intptr = malloc(sizeof(int));
676 *intptr = load_16_be(ptr);
681 *data_out = k5memdup0(ptr, len, &ret);
685 newlist = realloc(dnlist, (linkcount + 2) * sizeof(char *));
689 dnlist[linkcount] = k5memdup0(ptr, len, &ret);
690 if (dnlist[linkcount] == NULL)
692 dnlist[linkcount + 1] = NULL;
700 if (type != KDB_TL_LINKDN || dnlist == NULL)
706 for (i = 0; i < linkcount; i++)
713 * wrapper routines for decode_tl_data
715 static krb5_error_code
716 get_int_from_tl_data(krb5_context context, krb5_db_entry *entry, int type,
720 krb5_tl_data tl_data;
724 tl_data.tl_data_type = KDB_TL_USER_INFO;
725 ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
726 if (ret || tl_data.tl_data_length == 0)
729 if (decode_tl_data(&tl_data, type, &ptr) == 0) {
739 * Get the mask representing the attributes set on the directory
740 * object (user, policy ...).
743 krb5_get_attributes_mask(krb5_context context, krb5_db_entry *entry,
746 return get_int_from_tl_data(context, entry, KDB_TL_MASK, mask);
750 krb5_get_princ_type(krb5_context context, krb5_db_entry *entry, int *ptype)
752 return get_int_from_tl_data(context, entry, KDB_TL_PRINCTYPE, ptype);
756 krb5_get_princ_count(krb5_context context, krb5_db_entry *entry, int *pcount)
758 return get_int_from_tl_data(context, entry, KDB_TL_PRINCCOUNT, pcount);
762 krb5_get_linkdn(krb5_context context, krb5_db_entry *entry, char ***link_dn)
765 krb5_tl_data tl_data;
769 tl_data.tl_data_type = KDB_TL_USER_INFO;
770 ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
771 if (ret || tl_data.tl_data_length == 0)
774 if (decode_tl_data(&tl_data, KDB_TL_LINKDN, &ptr) == 0)
780 static krb5_error_code
781 get_str_from_tl_data(krb5_context context, krb5_db_entry *entry, int type,
785 krb5_tl_data tl_data;
788 if (type != KDB_TL_USERDN)
791 tl_data.tl_data_type = KDB_TL_USER_INFO;
792 ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
793 if (ret || tl_data.tl_data_length == 0)
796 if (decode_tl_data(&tl_data, type, &ptr) == 0)
803 * Replace the relative DN component of dn with newrdn.
806 replace_rdn(krb5_context context, const char *dn, const char *newrdn,
816 ret = ldap_str2dn(dn, &ldn, LDAP_DN_FORMAT_LDAPV3);
817 if (ret != LDAP_SUCCESS || ldn[0] == NULL) {
822 ret = ldap_str2rdn(newrdn, &lrdn, &next, LDAP_DN_FORMAT_LDAPV3);
823 if (ret != LDAP_SUCCESS) {
828 ldap_rdnfree(ldn[0]);
832 ret = ldap_dn2str(ldn, newdn_out, LDAP_DN_FORMAT_LDAPV3);
833 if (ret != LDAP_SUCCESS)
834 ret = KRB5_KDB_SERVER_INTERNAL_ERR;
845 krb5_get_userdn(krb5_context context, krb5_db_entry *entry, char **userdn)
848 return get_str_from_tl_data(context, entry, KDB_TL_USERDN, userdn);
852 * If attribute or attrvalues is NULL, just check for the existence of dn.
853 * Otherwise, read values for attribute from dn; then set the bit 1<<n in mask
854 * for each attrvalues[n] which is present in the values read.
857 checkattributevalue(LDAP *ld, char *dn, char *attribute, char **attrvalues,
862 char **values = NULL, *attributes[2] = { NULL };
863 LDAPMessage *result = NULL, *entry;
866 return set_ldap_error(0, LDAP_NO_SUCH_OBJECT, OP_SEARCH);
868 attributes[0] = attribute;
870 /* Read values for attribute from the dn, or check for its existence. */
871 ret = ldap_search_ext_s(ld, dn, LDAP_SCOPE_BASE, 0, attributes, 0, NULL,
872 NULL, &timelimit, LDAP_NO_LIMIT, &result);
873 if (ret != LDAP_SUCCESS) {
874 ldap_msgfree(result);
875 return set_ldap_error(0, ret, OP_SEARCH);
878 /* Don't touch *mask if we are only checking for existence. */
879 if (attribute == NULL || attrvalues == NULL)
884 entry = ldap_first_entry(ld, result);
887 values = ldap_get_values(ld, entry, attribute);
891 /* Set bits in mask for each matching value we read. */
892 for (i = 0; attrvalues[i]; i++) {
893 for (j = 0; values[j]; j++) {
894 if (strcasecmp(attrvalues[i], values[j]) == 0) {
902 ldap_msgfree(result);
903 ldap_value_free(values);
907 static krb5_error_code
908 getepochtime(char *strtime, krb5_timestamp *epochtime)
912 memset(&tme, 0, sizeof(tme));
913 if (strptime(strtime,"%Y%m%d%H%M%SZ", &tme) == NULL) {
917 *epochtime = krb5int_gmt_mktime(&tme);
921 /* Get the integer value of attribute from int. If it is not found, return
922 * ENOENT and set *val_out to 0. */
924 krb5_ldap_get_value(LDAP *ld, LDAPMessage *ent, char *attribute, int *val_out)
929 values = ldap_get_values(ld, ent, attribute);
932 if (values[0] != NULL)
933 *val_out = atoi(values[0]);
934 ldap_value_free(values);
938 /* Return the first string value of attribute in ent. */
940 krb5_ldap_get_string(LDAP *ld, LDAPMessage *ent, char *attribute,
941 char **str_out, krb5_boolean *attr_present)
944 krb5_error_code ret = 0;
947 if (attr_present != NULL)
948 *attr_present = FALSE;
950 values = ldap_get_values(ld, ent, attribute);
953 if (values[0] != NULL) {
954 if (attr_present != NULL)
955 *attr_present = TRUE;
956 *str_out = strdup(values[0]);
957 if (*str_out == NULL)
960 ldap_value_free(values);
964 static krb5_error_code
965 get_time(LDAP *ld, LDAPMessage *ent, char *attribute, krb5_timestamp *time_out,
966 krb5_boolean *attr_present)
968 char **values = NULL;
969 krb5_error_code ret = 0;
972 *attr_present = FALSE;
974 values = ldap_get_values(ld, ent, attribute);
977 if (values[0] != NULL) {
978 *attr_present = TRUE;
979 ret = getepochtime(values[0], time_out);
981 ldap_value_free(values);
985 /* Add an entry to *list_inout and return it in *mod_out. */
986 static krb5_error_code
987 alloc_mod(LDAPMod ***list_inout, LDAPMod **mod_out)
990 LDAPMod **mods = *list_inout;
994 for (count = 0; mods != NULL && mods[count] != NULL; count++);
995 mods = realloc(mods, (count + 2) * sizeof(*mods));
1000 mods[count] = calloc(1, sizeof(LDAPMod));
1001 if (mods[count] == NULL)
1003 mods[count + 1] = NULL;
1004 *mod_out = mods[count];
1009 krb5_add_str_mem_ldap_mod(LDAPMod ***list, char *attribute, int op,
1012 krb5_error_code ret;
1016 ret = alloc_mod(list, &mod);
1020 mod->mod_type = strdup(attribute);
1021 if (mod->mod_type == NULL)
1025 mod->mod_values = NULL;
1029 for (count = 0; values[count] != NULL; count++);
1030 mod->mod_values = calloc(count + 1, sizeof(char *));
1031 if (mod->mod_values == NULL)
1034 for (i = 0; i < count; i++) {
1035 mod->mod_values[i] = strdup(values[i]);
1036 if (mod->mod_values[i] == NULL)
1039 mod->mod_values[i] = NULL;
1044 krb5_add_ber_mem_ldap_mod(LDAPMod ***list, char *attribute, int op,
1045 struct berval **ber_values)
1047 krb5_error_code ret;
1051 ret = alloc_mod(list, &mod);
1055 mod->mod_type = strdup(attribute);
1056 if (mod->mod_type == NULL)
1060 for (count = 0; ber_values[count] != NULL; count++);
1061 mod->mod_bvalues = calloc(count + 1, sizeof(struct berval *));
1062 if (mod->mod_bvalues == NULL)
1065 for (i = 0; i < count; i++) {
1066 mod->mod_bvalues[i] = calloc(1, sizeof(struct berval));
1067 if (mod->mod_bvalues[i] == NULL)
1070 mod->mod_bvalues[i]->bv_len = ber_values[i]->bv_len;
1071 mod->mod_bvalues[i]->bv_val = k5memdup(ber_values[i]->bv_val,
1072 ber_values[i]->bv_len, &ret);
1073 if (mod->mod_bvalues[i]->bv_val == NULL)
1076 mod->mod_bvalues[i] = NULL;
1080 static inline char *
1083 char tmpbuf[3 * sizeof(val) + 2];
1085 snprintf(tmpbuf, sizeof(tmpbuf), "%d", val);
1086 return strdup(tmpbuf);
1090 krb5_add_int_mem_ldap_mod(LDAPMod ***list, char *attribute, int op, int value)
1092 krb5_error_code ret;
1095 ret = alloc_mod(list, &mod);
1099 mod->mod_type = strdup(attribute);
1100 if (mod->mod_type == NULL)
1104 mod->mod_values = calloc(2, sizeof(char *));
1105 if (mod->mod_values == NULL)
1107 mod->mod_values[0] = format_d(value);
1108 if (mod->mod_values[0] == NULL)
1114 krb5_ldap_modify_ext(krb5_context context, LDAP *ld, const char *dn,
1115 LDAPMod **mods, int op)
1119 ret = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1120 return (ret == LDAP_SUCCESS) ? 0 : set_ldap_error(context, ret, op);
1124 krb5_ldap_lock(krb5_context kcontext, int mode)
1126 krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1128 k5_setmsg(kcontext, status, "LDAP %s", error_message(status));
1133 krb5_ldap_unlock(krb5_context kcontext)
1135 krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1137 k5_setmsg(kcontext, status, "LDAP %s", error_message(status));
1143 * Get the number of times an object has been referred to in a realm. This is
1144 * needed to find out if deleting the attribute will cause dangling links.
1146 * An LDAP handle may be optionally specified to prevent race condition - there
1147 * are a limited number of LDAP handles.
1150 krb5_ldap_get_reference_count(krb5_context context, char *dn, char *refattr,
1151 int *count, LDAP *ld)
1153 int n, st, tempst, gothandle = 0;
1154 unsigned int i, ntrees = 0;
1155 char *refcntattr[2];
1156 char *filter = NULL, *corrected = NULL, **subtree = NULL;
1157 kdb5_dal_handle *dal_handle = NULL;
1158 krb5_ldap_context *ldap_context = NULL;
1159 krb5_ldap_server_handle *ldap_server_handle = NULL;
1160 LDAPMessage *result = NULL;
1162 if (dn == NULL || refattr == NULL) {
1173 refcntattr[0] = refattr;
1174 refcntattr[1] = NULL;
1176 corrected = ldap_filter_correct(dn);
1177 if (corrected == NULL) {
1182 if (asprintf(&filter, "%s=%s", refattr, corrected) < 0) {
1188 st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees);
1192 for (i = 0, *count = 0; i < ntrees; i++) {
1193 LDAP_SEARCH(subtree[i], LDAP_SCOPE_SUBTREE, filter, refcntattr);
1194 n = ldap_count_entries(ld, result);
1196 int ret, errcode = 0;
1197 ret = ldap_parse_result(ld, result, &errcode, NULL, NULL, NULL,
1199 if (ret != LDAP_SUCCESS)
1201 st = translate_ldap_error(errcode, OP_SEARCH);
1205 ldap_msgfree(result);
1213 ldap_msgfree(result);
1214 for (i = 0; i < ntrees; i++)
1219 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1223 /* Extract a name from policy_dn, which must be directly under the realm
1226 krb5_ldap_policydn_to_name(krb5_context context, const char *policy_dn,
1229 size_t len1, len2, plen;
1230 krb5_error_code ret;
1231 kdb5_dal_handle *dal_handle;
1232 krb5_ldap_context *ldap_context;
1233 const char *realmdn;
1240 realmdn = ldap_context->lrparams->realmdn;
1241 if (realmdn == NULL)
1244 /* policyn_dn should be "cn=<policyname>,<realmdn>". */
1245 len1 = strlen(realmdn);
1246 len2 = strlen(policy_dn);
1247 if (len1 == 0 || len2 == 0 || len1 + 1 >= len2)
1249 plen = len2 - len1 - 1;
1250 if (policy_dn[plen] != ',' || strcmp(realmdn, policy_dn + plen + 1) != 0)
1253 rdn = k5memdup0(policy_dn, plen, &ret);
1256 ret = ldap_str2dn(rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC);
1260 if (dn[0] == NULL || dn[1] != NULL || dn[0][0]->la_attr.bv_len != 2 ||
1261 strncasecmp(dn[0][0]->la_attr.bv_val, "cn", 2) != 0) {
1264 *name_out = k5memdup0(dn[0][0]->la_value.bv_val,
1265 dn[0][0]->la_value.bv_len, &ret);
1271 /* Compute the policy DN for the given policy name. */
1273 krb5_ldap_name_to_policydn(krb5_context context, char *name, char **policy_dn)
1277 kdb5_dal_handle *dal_handle;
1278 krb5_ldap_context *ldap_context;
1282 /* Used for removing policy reference from an object */
1283 if (name[0] == '\0') {
1284 *policy_dn = strdup("");
1285 return (*policy_dn == NULL) ? ENOMEM : 0;
1290 if (ldap_context->lrparams->realmdn == NULL)
1293 corrected = ldap_filter_correct(name);
1294 if (corrected == NULL)
1297 st = asprintf(policy_dn, "cn=%s,%s", corrected,
1298 ldap_context->lrparams->realmdn);
1307 /* Return true if dn1 is a subtree of dn2. */
1308 static inline krb5_boolean
1309 is_subtree(const char *dn1, size_t len1, const char *dn2, size_t len2)
1311 return len1 > len2 && dn1[len1 - len2 - 1] == ',' &&
1312 strcasecmp(dn1 + (len1 - len2), dn2) == 0;
1315 /* Remove overlapping and repeated subtree entries from the list of subtrees.
1316 * If sscope is not 2 (sub), only remove repeated entries. */
1318 remove_overlapping_subtrees(char **list, int *subtcount, int sscope)
1322 int count = *subtcount;
1324 for (i = 0; i < count && list[i] != NULL; i++) {
1325 ilen = strlen(list[i]);
1326 for (j = i + 1; j < count && list[j] != NULL; j++) {
1327 jlen = strlen(list[j]);
1328 /* Remove list[j] if it is identical to list[i] or a subtree of it.
1329 * Remove list[i] if it is a subtree of list[j]. */
1330 if ((ilen == jlen && strcasecmp(list[j], list[i]) == 0) ||
1331 (sscope == 2 && is_subtree(list[j], jlen, list[i], ilen))) {
1333 list[j--] = list[count - 1];
1334 list[--count] = NULL;
1335 } else if (sscope == 2 &&
1336 is_subtree(list[i], ilen, list[j], jlen)) {
1338 list[i--] = list[count - 1];
1339 list[--count] = NULL;
1348 free_princ_ent_contents(osa_princ_ent_t princ_ent)
1352 for (i = 0; i < princ_ent->old_key_len; i++) {
1353 k5_free_key_data(princ_ent->old_keys[i].n_key_data,
1354 princ_ent->old_keys[i].key_data);
1355 princ_ent->old_keys[i].n_key_data = 0;
1356 princ_ent->old_keys[i].key_data = NULL;
1358 free(princ_ent->old_keys);
1359 princ_ent->old_keys = NULL;
1360 princ_ent->old_key_len = 0;
1363 /* Get any auth indicator values from LDAP and update the "require_auth"
1365 static krb5_error_code
1366 get_ldap_auth_ind(krb5_context context, LDAP *ld, LDAPMessage *ldap_ent,
1367 krb5_db_entry *entry, unsigned int *mask)
1369 krb5_error_code ret;
1371 char **auth_inds = NULL;
1372 struct k5buf buf = EMPTY_K5BUF;
1374 auth_inds = ldap_get_values(ld, ldap_ent, "krbPrincipalAuthInd");
1375 if (auth_inds == NULL)
1378 k5_buf_init_dynamic(&buf);
1380 /* Make a space seperated list of indicators. */
1381 for (i = 0; auth_inds[i] != NULL; i++) {
1382 k5_buf_add(&buf, auth_inds[i]);
1383 if (auth_inds[i + 1] != NULL)
1384 k5_buf_add(&buf, " ");
1387 ret = k5_buf_status(&buf);
1391 ret = krb5_dbe_set_string(context, entry, KRB5_KDB_SK_REQUIRE_AUTH,
1394 *mask |= KDB_AUTH_IND_ATTR;
1398 ldap_value_free(auth_inds);
1403 * Fill out a krb5_db_entry princ entry struct given a LDAP message containing
1404 * the results of a principal search of the directory.
1407 populate_krb5_db_entry(krb5_context context, krb5_ldap_context *ldap_context,
1408 LDAP *ld, LDAPMessage *ent, krb5_const_principal princ,
1409 krb5_db_entry *entry)
1411 krb5_error_code ret;
1412 unsigned int mask = 0;
1413 int val, i, pcount, objtype;
1414 krb5_boolean attr_present;
1415 krb5_kvno mkvno = 0;
1416 krb5_timestamp lastpwdchange, unlock_time;
1417 char *policydn = NULL, *pwdpolicydn = NULL, *polname = NULL, *user = NULL;
1418 char *tktpolname = NULL, *dn = NULL, **link_references = NULL;
1419 char **pnvalues = NULL, **ocvalues = NULL, **a2d2 = NULL;
1420 struct berval **ber_key_data = NULL, **ber_tl_data = NULL;
1421 krb5_tl_data userinfo_tl_data = { NULL }, **endp, *tl;
1422 osa_princ_ent_rec princ_ent;
1424 memset(&princ_ent, 0, sizeof(princ_ent));
1426 ret = krb5_copy_principal(context, princ, &entry->princ);
1430 /* get the associated directory user information */
1431 pnvalues = ldap_get_values(ld, ent, "krbprincipalname");
1432 if (pnvalues != NULL) {
1433 ret = krb5_unparse_name(context, princ, &user);
1438 for (i = 0; pnvalues[i] != NULL; i++) {
1439 if (strcasecmp(pnvalues[i], user) == 0) {
1440 pcount = ldap_count_values(pnvalues);
1445 dn = ldap_get_dn(ld, ent);
1447 ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &ret);
1448 ret = set_ldap_error(context, ret, 0);
1452 ocvalues = ldap_get_values(ld, ent, "objectclass");
1453 if (ocvalues != NULL) {
1454 for (i = 0; ocvalues[i] != NULL; i++) {
1455 if (strcasecmp(ocvalues[i], "krbprincipal") == 0) {
1456 objtype = KDB_STANDALONE_PRINCIPAL_OBJECT;
1457 ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE,
1466 /* Add principalcount, DN and principaltype user information to
1468 ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount);
1471 ret = store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, dn);
1476 ret = get_time(ld, ent, "krbLastSuccessfulAuth", &entry->last_success,
1481 mask |= KDB_LAST_SUCCESS_ATTR;
1483 ret = get_time(ld, ent, "krbLastFailedAuth", &entry->last_failed,
1488 mask |= KDB_LAST_FAILED_ATTR;
1490 if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount", &val) == 0) {
1491 entry->fail_auth_count = val;
1492 mask |= KDB_FAIL_AUTH_COUNT_ATTR;
1494 if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &val) == 0) {
1495 entry->max_life = val;
1496 mask |= KDB_MAX_LIFE_ATTR;
1498 if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage", &val) == 0) {
1499 entry->max_renewable_life = val;
1500 mask |= KDB_MAX_RLIFE_ATTR;
1502 if (krb5_ldap_get_value(ld, ent, "krbticketflags", &val) == 0) {
1503 entry->attributes = val;
1504 mask |= KDB_TKT_FLAGS_ATTR;
1506 ret = get_time(ld, ent, "krbprincipalexpiration", &entry->expiration,
1511 mask |= KDB_PRINC_EXPIRE_TIME_ATTR;
1513 ret = get_time(ld, ent, "krbpasswordexpiration", &entry->pw_expiration,
1518 mask |= KDB_PWD_EXPIRE_TIME_ATTR;
1520 ret = krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn,
1525 mask |= KDB_POL_REF_ATTR;
1526 /* Ensure that the policy is inside the realm container. */
1527 ret = krb5_ldap_policydn_to_name(context, policydn, &tktpolname);
1532 ret = krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn,
1537 mask |= KDB_PWD_POL_REF_ATTR;
1539 /* Ensure that the policy is inside the realm container. */
1540 ret = krb5_ldap_policydn_to_name(context, pwdpolicydn, &polname);
1543 princ_ent.policy = polname;
1544 princ_ent.aux_attributes |= KADM5_POLICY;
1547 ber_key_data = ldap_get_values_len(ld, ent, "krbpwdhistory");
1548 if (ber_key_data != NULL) {
1549 mask |= KDB_PWD_HISTORY_ATTR;
1550 ret = krb5_decode_histkey(context, ber_key_data, &princ_ent);
1553 ldap_value_free_len(ber_key_data);
1556 if (princ_ent.aux_attributes) {
1557 ret = krb5_update_tl_kadm_data(context, entry, &princ_ent);
1562 ber_key_data = ldap_get_values_len(ld, ent, "krbprincipalkey");
1563 if (ber_key_data != NULL) {
1564 mask |= KDB_SECRET_KEY_ATTR;
1565 ret = krb5_decode_krbsecretkey(context, entry, ber_key_data, &mkvno);
1569 ret = krb5_dbe_update_mkvno(context, entry, mkvno);
1575 ret = get_time(ld, ent, "krbLastPwdChange", &lastpwdchange, &attr_present);
1579 ret = krb5_dbe_update_last_pwd_change(context, entry, lastpwdchange);
1582 mask |= KDB_LAST_PWD_CHANGE_ATTR;
1585 ret = get_time(ld, ent, "krbLastAdminUnlock", &unlock_time, &attr_present);
1589 ret = krb5_dbe_update_last_admin_unlock(context, entry, unlock_time);
1592 mask |= KDB_LAST_ADMIN_UNLOCK_ATTR;
1595 a2d2 = ldap_get_values(ld, ent, "krbAllowedToDelegateTo");
1597 for (endp = &entry->tl_data; *endp; endp = &(*endp)->tl_data_next);
1598 for (i = 0; a2d2[i] != NULL; i++) {
1599 tl = k5alloc(sizeof(*tl), &ret);
1602 tl->tl_data_type = KRB5_TL_CONSTRAINED_DELEGATION_ACL;
1603 tl->tl_data_length = strlen(a2d2[i]);
1604 tl->tl_data_contents = (unsigned char *)strdup(a2d2[i]);
1605 if (tl->tl_data_contents == NULL) {
1610 tl->tl_data_next = NULL;
1612 endp = &tl->tl_data_next;
1616 link_references = ldap_get_values(ld, ent, "krbobjectreferences");
1617 if (link_references != NULL) {
1618 for (i = 0; link_references[i] != NULL; i++) {
1619 ret = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN,
1620 link_references[i]);
1626 ber_tl_data = ldap_get_values_len(ld, ent, "krbExtraData");
1627 if (ber_tl_data != NULL) {
1628 for (i = 0; ber_tl_data[i] != NULL; i++) {
1629 ret = berval2tl_data(ber_tl_data[i], &tl);
1632 ret = krb5_dbe_update_tl_data(context, entry, tl);
1633 free(tl->tl_data_contents);
1638 mask |= KDB_EXTRA_DATA_ATTR;
1641 /* Auth indicators from krbPrincipalAuthInd will replace those from
1643 ret = get_ldap_auth_ind(context, ld, ent, entry, &mask);
1647 /* Update the mask of attributes present on the directory object to the
1649 ret = store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask);
1652 ret = krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data);
1656 ret = krb5_read_tkt_policy(context, ldap_context, entry, tktpolname);
1660 /* For compatibility with DB2 principals. */
1661 entry->len = KRB5_KDB_V1_BASE_LENGTH;
1665 ldap_value_free_len(ber_key_data);
1666 ldap_value_free_len(ber_tl_data);
1667 ldap_value_free(pnvalues);
1668 ldap_value_free(ocvalues);
1669 ldap_value_free(link_references);
1670 ldap_value_free(a2d2);
1671 free(userinfo_tl_data.tl_data_contents);
1676 krb5_free_unparsed_name(context, user);
1677 free_princ_ent_contents(&princ_ent);