1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_ldap/ldap_misc.c */
3 /*
4 * Copyright (c) 2004-2005, Novell, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
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.
17 *
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.
29 */
30 /*
31 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
32 * Use is subject to license terms.
33 */
34 #include "kdb_ldap.h"
35 #include "ldap_misc.h"
36 #include "ldap_handle.h"
37 #include "ldap_err.h"
38 #include "ldap_principal.h"
39 #include "princ_xdr.h"
40 #include "ldap_pwd_policy.h"
41 #include <time.h>
42 #include <ctype.h>
43 #include <kadm5/admin.h>
44
45 #ifdef NEED_STRPTIME_PROTO
46 extern char *strptime(const char *, const char *, struct tm *);
47 #endif
48
49 static void remove_overlapping_subtrees(char **listin, size_t *subtcount,
50 int sscope);
51
52 /* Set an extended error message about being unable to read name. */
53 static krb5_error_code
attr_read_error(krb5_context ctx,krb5_error_code code,const char * name)54 attr_read_error(krb5_context ctx, krb5_error_code code, const char *name)
55 {
56 k5_setmsg(ctx, code, _("Error reading '%s' attribute: %s"), name,
57 error_message(code));
58 return code;
59 }
60
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
prof_get_integer_def(krb5_context ctx,const char * conf_section,const char * name,int dfl,krb5_ui_4 * out)64 prof_get_integer_def(krb5_context ctx, const char *conf_section,
65 const char *name, int dfl, krb5_ui_4 *out)
66 {
67 krb5_error_code ret;
68 int val;
69
70 ret = profile_get_integer(ctx->profile, KDB_MODULE_SECTION, conf_section,
71 name, 0, &val);
72 if (ret)
73 return attr_read_error(ctx, ret, name);
74 if (val != 0) {
75 *out = val;
76 return 0;
77 }
78 ret = profile_get_integer(ctx->profile, KDB_MODULE_DEF_SECTION, name, NULL,
79 dfl, &val);
80 if (ret)
81 return attr_read_error(ctx, ret, name);
82 *out = val;
83 return 0;
84 }
85
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
prof_get_boolean_def(krb5_context ctx,const char * conf_section,const char * name,krb5_boolean dfl,krb5_boolean * out)89 prof_get_boolean_def(krb5_context ctx, const char *conf_section,
90 const char *name, krb5_boolean dfl, krb5_boolean *out)
91 {
92 krb5_error_code ret;
93 int val = 0;
94
95 ret = profile_get_boolean(ctx->profile, KDB_MODULE_SECTION, conf_section,
96 name, -1, &val);
97 if (ret)
98 return attr_read_error(ctx, ret, name);
99 if (val != -1) {
100 *out = val;
101 return 0;
102 }
103 ret = profile_get_boolean(ctx->profile, KDB_MODULE_DEF_SECTION, name, NULL,
104 dfl, &val);
105 if (ret)
106 return attr_read_error(ctx, ret, name);
107 *out = val;
108 return 0;
109 }
110
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
prof_get_string_def(krb5_context ctx,const char * conf_section,const char * name,char ** out)114 prof_get_string_def(krb5_context ctx, const char *conf_section,
115 const char *name, char **out)
116 {
117 krb5_error_code ret;
118
119 ret = profile_get_string(ctx->profile, KDB_MODULE_SECTION, conf_section,
120 name, NULL, out);
121 if (ret)
122 return attr_read_error(ctx, ret, name);
123 if (*out != NULL)
124 return 0;
125 ret = profile_get_string(ctx->profile, KDB_MODULE_DEF_SECTION, name, NULL,
126 NULL, out);
127 if (ret)
128 return attr_read_error(ctx, ret, name);
129 return 0;
130 }
131
132 static krb5_error_code
get_db_opt(const char * input,char ** opt_out,char ** val_out)133 get_db_opt(const char *input, char **opt_out, char **val_out)
134 {
135 krb5_error_code ret;
136 const char *pos;
137 char *opt, *val = NULL;
138 size_t len;
139
140 *opt_out = *val_out = NULL;
141 pos = strchr(input, '=');
142 if (pos == NULL) {
143 opt = strdup(input);
144 if (opt == NULL)
145 return ENOMEM;
146 } else {
147 len = pos - input;
148 /* Ignore trailing spaces. */
149 while (len > 0 && isspace((unsigned char)input[len - 1]))
150 len--;
151 opt = k5memdup0(input, len, &ret);
152 if (opt == NULL)
153 return ret;
154
155 pos++; /* Move past '='. */
156 while (isspace(*pos)) /* Ignore leading spaces. */
157 pos++;
158 if (*pos != '\0') {
159 val = strdup(pos);
160 if (val == NULL) {
161 free(opt);
162 return ENOMEM;
163 }
164 }
165 }
166 *opt_out = opt;
167 *val_out = val;
168 return 0;
169 }
170
171 static krb5_error_code
add_server_entry(krb5_context context,const char * name)172 add_server_entry(krb5_context context, const char *name)
173 {
174 krb5_ldap_context *ctx = context->dal_handle->db_context;
175 krb5_ldap_server_info **sp, **list, *server;
176 size_t count = 0;
177
178 /* Allocate list space for the new entry and null terminator. */
179 for (sp = ctx->server_info_list; sp != NULL && *sp != NULL; sp++)
180 count++;
181 list = realloc(ctx->server_info_list, (count + 2) * sizeof(*list));
182 if (list == NULL)
183 return ENOMEM;
184 ctx->server_info_list = list;
185
186 server = calloc(1, sizeof(krb5_ldap_server_info));
187 if (server == NULL)
188 return ENOMEM;
189 server->server_status = NOTSET;
190 server->server_name = strdup(name);
191 if (server->server_name == NULL) {
192 free(server);
193 return ENOMEM;
194 }
195 list[count] = server;
196 list[count + 1] = NULL;
197 return 0;
198 }
199
200 krb5_error_code
krb5_ldap_parse_db_params(krb5_context context,char ** db_args)201 krb5_ldap_parse_db_params(krb5_context context, char **db_args)
202 {
203 char *opt = NULL, *val = NULL;
204 krb5_error_code ret = 0;
205 krb5_ldap_context *ctx = context->dal_handle->db_context;
206
207 if (db_args == NULL)
208 return 0;
209 for (; *db_args != NULL; db_args++) {
210 ret = get_db_opt(*db_args, &opt, &val);
211 if (ret)
212 goto cleanup;
213
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. */
218 ret = EINVAL;
219 k5_setmsg(context, ret, _("KDB module requires -update argument"));
220 goto cleanup;
221 }
222
223 if (val == NULL) {
224 ret = EINVAL;
225 k5_setmsg(context, ret, _("'%s' value missing"), opt);
226 goto cleanup;
227 }
228
229 /* Check for options which do require arguments. */
230 if (!strcmp(opt, "binddn")) {
231 free(ctx->bind_dn);
232 ctx->bind_dn = strdup(val);
233 if (ctx->bind_dn == NULL) {
234 ret = ENOMEM;
235 goto cleanup;
236 }
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")) {
241 free(ctx->bind_pwd);
242 ctx->bind_pwd = strdup(val);
243 if (ctx->bind_pwd == NULL) {
244 ret = ENOMEM;
245 goto cleanup;
246 }
247 } else if (!strcmp(opt, "sasl_mech")) {
248 free(ctx->sasl_mech);
249 ctx->sasl_mech = strdup(val);
250 if (ctx->sasl_mech == NULL) {
251 ret = ENOMEM;
252 goto cleanup;
253 }
254 } else if (!strcmp(opt, "sasl_authcid")) {
255 free(ctx->sasl_authcid);
256 ctx->sasl_authcid = strdup(val);
257 if (ctx->sasl_authcid == NULL) {
258 ret = ENOMEM;
259 goto cleanup;
260 }
261 } else if (!strcmp(opt, "sasl_authzid")) {
262 free(ctx->sasl_authzid);
263 ctx->sasl_authzid = strdup(val);
264 if (ctx->sasl_authzid == NULL) {
265 ret = ENOMEM;
266 goto cleanup;
267 }
268 } else if (!strcmp(opt, "sasl_realm")) {
269 free(ctx->sasl_realm);
270 ctx->sasl_realm = strdup(val);
271 if (ctx->sasl_realm == NULL) {
272 ret = ENOMEM;
273 goto cleanup;
274 }
275 } else if (!strcmp(opt, "host")) {
276 ret = add_server_entry(context, val);
277 if (ret)
278 goto cleanup;
279 } else if (!strcmp(opt, "debug")) {
280 ctx->ldap_debug = atoi(val);
281 } else {
282 ret = EINVAL;
283 k5_setmsg(context, ret, _("unknown option '%s'"), opt);
284 goto cleanup;
285 }
286
287 free(opt);
288 free(val);
289 opt = val = NULL;
290 }
291
292 cleanup:
293 free(opt);
294 free(val);
295 return ret;
296 }
297
298 /* Pick kdc_var or kadmind_var depending on the server type. */
299 static inline const char *
choose_var(int srv_type,const char * kdc_var,const char * kadmind_var)300 choose_var(int srv_type, const char *kdc_var, const char *kadmind_var)
301 {
302 return (srv_type == KRB5_KDB_SRV_TYPE_KDC) ? kdc_var : kadmind_var;
303 }
304
305 /*
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 ....
309 */
310 krb5_error_code
krb5_ldap_read_server_params(krb5_context context,char * conf_section,int srv_type)311 krb5_ldap_read_server_params(krb5_context context, char *conf_section,
312 int srv_type)
313 {
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;
319
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)
324 return ENOMEM;
325 }
326
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;
330
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);
337 if (ret)
338 return ret;
339 }
340
341 if (ldap_context->max_server_conns < 2) {
342 k5_setmsg(context, EINVAL,
343 _("Minimum connections required per server is 2"));
344 return EINVAL;
345 }
346
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);
353 if (ret)
354 return ret;
355 }
356
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);
362 if (ret)
363 return ret;
364 }
365
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);
371 if (ret)
372 return ret;
373 }
374
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);
380 if (ret)
381 return ret;
382 }
383
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);
389 if (ret)
390 return ret;
391 }
392
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);
398 if (ret)
399 return ret;
400 }
401
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,
406 &servers);
407 if (ret)
408 return attr_read_error(context, ret, KRB5_CONF_LDAP_SERVERS);
409
410 if (servers == NULL) {
411 ret = add_server_entry(context, "ldapi://");
412 if (ret)
413 return ret;
414 } else {
415 item = strtok_r(servers, delims, &save_ptr);
416 while (item != NULL) {
417 ret = add_server_entry(context, item);
418 if (ret) {
419 profile_release_string(servers);
420 return ret;
421 }
422 item = strtok_r(NULL, delims, &save_ptr);
423 }
424 profile_release_string(servers);
425 }
426 }
427
428 ret = prof_get_boolean_def(context, conf_section,
429 KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE,
430 &ldap_context->disable_last_success);
431 if (ret)
432 return ret;
433
434 return prof_get_boolean_def(context, conf_section,
435 KRB5_CONF_DISABLE_LOCKOUT, FALSE,
436 &ldap_context->disable_lockout);
437 }
438
439 void
krb5_ldap_free_server_context_params(krb5_ldap_context * ctx)440 krb5_ldap_free_server_context_params(krb5_ldap_context *ctx)
441 {
442 size_t i;
443 krb5_ldap_server_info **list;
444 krb5_ldap_server_handle *h, *next;
445
446 if (ctx == NULL)
447 return;
448
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) {
453 next = h->next;
454 ldap_unbind_ext_s(h->ldap_handle, NULL, NULL);
455 free(h);
456 }
457 free(list[i]);
458 }
459 free(list);
460 ctx->server_info_list = NULL;
461
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);
467 free(ctx->bind_dn);
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;
472 }
473
474 void
krb5_ldap_free_server_params(krb5_ldap_context * ctx)475 krb5_ldap_free_server_params(krb5_ldap_context *ctx)
476 {
477 if (ctx == NULL)
478 return;
479 krb5_ldap_free_server_context_params(ctx);
480 k5_mutex_destroy(&ctx->hndl_lock);
481 free(ctx);
482 }
483
484 /* Return true if princ is in the default realm of ldap_context or is a
485 * cross-realm TGS principal for that realm. */
486 krb5_boolean
is_principal_in_realm(krb5_ldap_context * ldap_context,krb5_const_principal princ)487 is_principal_in_realm(krb5_ldap_context *ldap_context,
488 krb5_const_principal princ)
489 {
490 const char *realm = ldap_context->lrparams->realm_name;
491
492 if (princ->length == 2 &&
493 data_eq_string(princ->data[0], "krbtgt") &&
494 data_eq_string(princ->data[1], realm))
495 return TRUE;
496 return data_eq_string(princ->realm, realm);
497 }
498
499 /*
500 * Deduce the subtree information from the context. A realm can have
501 * multiple subtrees.
502 * 1. the Realm container
503 * 2. the actual subtrees associated with the Realm
504 *
505 * However, there are some conditions to be considered to deduce the
506 * actual subtree/s associated with the realm. The conditions are as
507 * follows:
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
514 * one subtree.
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.
518 */
519 krb5_error_code
krb5_get_subtree_info(krb5_ldap_context * ldap_context,char *** subtreearr,size_t * ntree)520 krb5_get_subtree_info(krb5_ldap_context *ldap_context, char ***subtreearr,
521 size_t *ntree)
522 {
523 krb5_error_code ret;
524 size_t subtreecount, count = 0;
525 int search_scope;
526 char **subtree, *realm_cont_dn, *containerref;
527 char **subtarr = NULL;
528
529 containerref = ldap_context->lrparams->containerref;
530 subtree = ldap_context->lrparams->subtree;
531 realm_cont_dn = ldap_context->lrparams->realmdn;
532 subtreecount = ldap_context->lrparams->subtreecount;
533 search_scope = ldap_context->lrparams->search_scope;
534
535 /* Leave space for realm DN, containerref, and null terminator. */
536 subtarr = k5calloc(subtreecount + 3, sizeof(char *), &ret);
537 if (subtarr == NULL)
538 goto cleanup;
539
540 /* Get the complete subtree list. */
541 while (count < subtreecount && subtree[count] != NULL) {
542 subtarr[count] = strdup(subtree[count]);
543 if (subtarr[count++] == NULL) {
544 ret = ENOMEM;
545 goto cleanup;
546 }
547 }
548
549 subtarr[count] = strdup(realm_cont_dn);
550 if (subtarr[count++] == NULL) {
551 ret = ENOMEM;
552 goto cleanup;
553 }
554
555 if (containerref != NULL) {
556 subtarr[count] = strdup(containerref);
557 if (subtarr[count++] == NULL) {
558 ret = ENOMEM;
559 goto cleanup;
560 }
561 }
562
563 remove_overlapping_subtrees(subtarr, &count, search_scope);
564 *ntree = count;
565 *subtreearr = subtarr;
566 subtarr = NULL;
567 count = 0;
568
569 cleanup:
570 while (count > 0)
571 free(subtarr[--count]);
572 free(subtarr);
573 return ret;
574 }
575
576 /* Reallocate tl and return a pointer to the new space, or NULL on failure. */
577 static unsigned char *
expand_tl_data(krb5_tl_data * tl,uint16_t len)578 expand_tl_data(krb5_tl_data *tl, uint16_t len)
579 {
580 unsigned char *newptr;
581
582 if (len > UINT16_MAX - tl->tl_data_length)
583 return NULL;
584 newptr = realloc(tl->tl_data_contents, tl->tl_data_length + len);
585 if (newptr == NULL)
586 return NULL;
587 tl->tl_data_contents = newptr;
588 tl->tl_data_length += len;
589 return tl->tl_data_contents + tl->tl_data_length - len;
590 }
591
592 /* Append a one-byte type, a two-byte length, and a value to a KDB_TL_USER_INFO
593 * tl_data item. The length is inferred from type and value. */
594 krb5_error_code
store_tl_data(krb5_tl_data * tl,int type,void * value)595 store_tl_data(krb5_tl_data *tl, int type, void *value)
596 {
597 unsigned char *ptr;
598 int ival;
599 char *str;
600 size_t len;
601
602 tl->tl_data_type = KDB_TL_USER_INFO;
603 switch (type) {
604 case KDB_TL_PRINCCOUNT:
605 case KDB_TL_PRINCTYPE:
606 case KDB_TL_MASK:
607 ival = *(int *)value;
608 if (ival > UINT16_MAX)
609 return EINVAL;
610 ptr = expand_tl_data(tl, 5);
611 if (ptr == NULL)
612 return ENOMEM;
613 *ptr = type;
614 store_16_be(2, ptr + 1);
615 store_16_be(ival, ptr + 3);
616 break;
617
618 case KDB_TL_USERDN:
619 case KDB_TL_LINKDN:
620 str = value;
621 len = strlen(str);
622 if (len > UINT16_MAX - 3)
623 return ENOMEM;
624 ptr = expand_tl_data(tl, 3 + len);
625 if (ptr == NULL)
626 return ENOMEM;
627 *ptr = type;
628 store_16_be(len, ptr + 1);
629 memcpy(ptr + 3, str, len);
630 break;
631
632 default:
633 return EINVAL;
634 }
635 return 0;
636 }
637
638 /* Scan tl for a value of the given type and return it in allocated memory.
639 * For KDB_TL_LINKDN, return a list of all values found. */
640 static krb5_error_code
decode_tl_data(krb5_tl_data * tl,int type,void ** data_out)641 decode_tl_data(krb5_tl_data *tl, int type, void **data_out)
642 {
643 krb5_error_code ret;
644 const unsigned char *ptr, *end;
645 uint16_t len;
646 size_t linkcount = 0, i;
647 char **dnlist = NULL, **newlist;
648 int *intptr;
649
650 *data_out = NULL;
651
652 /* Find the first matching subfield or return ENOENT. For KDB_TL_LINKDN,
653 * keep iterating after finding a match as it may be repeated. */
654 ptr = tl->tl_data_contents;
655 end = ptr + tl->tl_data_length;
656 for (;;) {
657 if (end - ptr < 3)
658 break;
659 len = load_16_be(ptr + 1);
660 if (len > (end - ptr) - 3)
661 break;
662 if (*ptr != type) {
663 ptr += 3 + len;
664 continue;
665 }
666 ptr += 3;
667
668 switch (type) {
669 case KDB_TL_PRINCCOUNT:
670 case KDB_TL_PRINCTYPE:
671 case KDB_TL_MASK:
672 if (len != 2)
673 return EINVAL;
674 intptr = malloc(sizeof(int));
675 if (intptr == NULL)
676 return ENOMEM;
677 *intptr = load_16_be(ptr);
678 *data_out = intptr;
679 return 0;
680
681 case KDB_TL_USERDN:
682 *data_out = k5memdup0(ptr, len, &ret);
683 return ret;
684
685 case KDB_TL_LINKDN:
686 newlist = realloc(dnlist, (linkcount + 2) * sizeof(char *));
687 if (newlist == NULL)
688 goto oom;
689 dnlist = newlist;
690 dnlist[linkcount] = k5memdup0(ptr, len, &ret);
691 if (dnlist[linkcount] == NULL)
692 goto oom;
693 dnlist[linkcount + 1] = NULL;
694 linkcount++;
695 break;
696 }
697
698 ptr += len;
699 }
700
701 if (type != KDB_TL_LINKDN || dnlist == NULL)
702 return ENOENT;
703 *data_out = dnlist;
704 return 0;
705
706 oom:
707 for (i = 0; i < linkcount; i++)
708 free(dnlist[i]);
709 free(dnlist);
710 return ENOMEM;
711 }
712
713 /*
714 * wrapper routines for decode_tl_data
715 */
716 static krb5_error_code
get_int_from_tl_data(krb5_context context,krb5_db_entry * entry,int type,int * intval)717 get_int_from_tl_data(krb5_context context, krb5_db_entry *entry, int type,
718 int *intval)
719 {
720 krb5_error_code ret;
721 krb5_tl_data tl_data;
722 void *ptr;
723 int *intptr;
724
725 *intval = 0;
726
727 tl_data.tl_data_type = KDB_TL_USER_INFO;
728 ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
729 if (ret || tl_data.tl_data_length == 0)
730 return ret;
731
732 if (decode_tl_data(&tl_data, type, &ptr) == 0) {
733 intptr = ptr;
734 *intval = *intptr;
735 free(intptr);
736 }
737
738 return 0;
739 }
740
741 /*
742 * Get the mask representing the attributes set on the directory
743 * object (user, policy ...).
744 */
745 krb5_error_code
krb5_get_attributes_mask(krb5_context context,krb5_db_entry * entry,int * mask)746 krb5_get_attributes_mask(krb5_context context, krb5_db_entry *entry,
747 int *mask)
748 {
749 return get_int_from_tl_data(context, entry, KDB_TL_MASK, mask);
750 }
751
752 krb5_error_code
krb5_get_princ_type(krb5_context context,krb5_db_entry * entry,int * ptype)753 krb5_get_princ_type(krb5_context context, krb5_db_entry *entry, int *ptype)
754 {
755 return get_int_from_tl_data(context, entry, KDB_TL_PRINCTYPE, ptype);
756 }
757
758 krb5_error_code
krb5_get_princ_count(krb5_context context,krb5_db_entry * entry,int * pcount)759 krb5_get_princ_count(krb5_context context, krb5_db_entry *entry, int *pcount)
760 {
761 return get_int_from_tl_data(context, entry, KDB_TL_PRINCCOUNT, pcount);
762 }
763
764 krb5_error_code
krb5_get_linkdn(krb5_context context,krb5_db_entry * entry,char *** link_dn)765 krb5_get_linkdn(krb5_context context, krb5_db_entry *entry, char ***link_dn)
766 {
767 krb5_error_code ret;
768 krb5_tl_data tl_data;
769 void *ptr;
770
771 *link_dn = NULL;
772 tl_data.tl_data_type = KDB_TL_USER_INFO;
773 ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
774 if (ret || tl_data.tl_data_length == 0)
775 return ret;
776
777 if (decode_tl_data(&tl_data, KDB_TL_LINKDN, &ptr) == 0)
778 *link_dn = ptr;
779
780 return 0;
781 }
782
783 static krb5_error_code
get_str_from_tl_data(krb5_context context,krb5_db_entry * entry,int type,char ** strval)784 get_str_from_tl_data(krb5_context context, krb5_db_entry *entry, int type,
785 char **strval)
786 {
787 krb5_error_code ret;
788 krb5_tl_data tl_data;
789 void *ptr;
790
791 if (type != KDB_TL_USERDN)
792 return EINVAL;
793
794 tl_data.tl_data_type = KDB_TL_USER_INFO;
795 ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
796 if (ret || tl_data.tl_data_length == 0)
797 return ret;
798
799 if (decode_tl_data(&tl_data, type, &ptr) == 0)
800 *strval = ptr;
801
802 return 0;
803 }
804
805 /*
806 * Replace the relative DN component of dn with newrdn.
807 */
808 krb5_error_code
replace_rdn(krb5_context context,const char * dn,const char * newrdn,char ** newdn_out)809 replace_rdn(krb5_context context, const char *dn, const char *newrdn,
810 char **newdn_out)
811 {
812 krb5_error_code ret;
813 LDAPDN ldn = NULL;
814 LDAPRDN lrdn = NULL;
815 char *next;
816
817 *newdn_out = NULL;
818
819 ret = ldap_str2dn(dn, &ldn, LDAP_DN_FORMAT_LDAPV3);
820 if (ret != LDAP_SUCCESS || ldn[0] == NULL) {
821 ret = EINVAL;
822 goto cleanup;
823 }
824
825 ret = ldap_str2rdn(newrdn, &lrdn, &next, LDAP_DN_FORMAT_LDAPV3);
826 if (ret != LDAP_SUCCESS) {
827 ret = EINVAL;
828 goto cleanup;
829 }
830
831 ldap_rdnfree(ldn[0]);
832 ldn[0] = lrdn;
833 lrdn = NULL;
834
835 ret = ldap_dn2str(ldn, newdn_out, LDAP_DN_FORMAT_LDAPV3);
836 if (ret != LDAP_SUCCESS)
837 ret = KRB5_KDB_SERVER_INTERNAL_ERR;
838
839 cleanup:
840 if (ldn != NULL)
841 ldap_dnfree(ldn);
842 if (lrdn != NULL)
843 ldap_rdnfree(lrdn);
844 return ret;
845 }
846
847 krb5_error_code
krb5_get_userdn(krb5_context context,krb5_db_entry * entry,char ** userdn)848 krb5_get_userdn(krb5_context context, krb5_db_entry *entry, char **userdn)
849 {
850 *userdn = NULL;
851 return get_str_from_tl_data(context, entry, KDB_TL_USERDN, userdn);
852 }
853
854 /*
855 * If attribute or attrvalues is NULL, just check for the existence of dn.
856 * Otherwise, read values for attribute from dn; then set the bit 1<<n in mask
857 * for each attrvalues[n] which is present in the values read.
858 */
859 krb5_error_code
checkattributevalue(LDAP * ld,char * dn,char * attribute,char ** attrvalues,int * mask)860 checkattributevalue(LDAP *ld, char *dn, char *attribute, char **attrvalues,
861 int *mask)
862 {
863 krb5_error_code ret;
864 size_t i, j;
865 int one = 1;
866 char **values = NULL, *attributes[2] = { NULL };
867 LDAPMessage *result = NULL, *entry;
868
869 if (strlen(dn) == 0)
870 return set_ldap_error(0, LDAP_NO_SUCH_OBJECT, OP_SEARCH);
871
872 attributes[0] = attribute;
873
874 /* Read values for attribute from the dn, or check for its existence. */
875 ret = ldap_search_ext_s(ld, dn, LDAP_SCOPE_BASE, 0, attributes, 0, NULL,
876 NULL, &timelimit, LDAP_NO_LIMIT, &result);
877 if (ret != LDAP_SUCCESS) {
878 ldap_msgfree(result);
879 return set_ldap_error(0, ret, OP_SEARCH);
880 }
881
882 /* Don't touch *mask if we are only checking for existence. */
883 if (attribute == NULL || attrvalues == NULL)
884 goto done;
885
886 *mask = 0;
887
888 entry = ldap_first_entry(ld, result);
889 if (entry == NULL)
890 goto done;
891 values = ldap_get_values(ld, entry, attribute);
892 if (values == NULL)
893 goto done;
894
895 /* Set bits in mask for each matching value we read. */
896 for (i = 0; attrvalues[i]; i++) {
897 for (j = 0; values[j]; j++) {
898 if (strcasecmp(attrvalues[i], values[j]) == 0) {
899 *mask |= (one << i);
900 break;
901 }
902 }
903 }
904
905 done:
906 ldap_msgfree(result);
907 ldap_value_free(values);
908 return 0;
909 }
910
911 static krb5_error_code
getepochtime(char * strtime,krb5_timestamp * epochtime)912 getepochtime(char *strtime, krb5_timestamp *epochtime)
913 {
914 struct tm tme;
915
916 memset(&tme, 0, sizeof(tme));
917 if (strptime(strtime,"%Y%m%d%H%M%SZ", &tme) == NULL) {
918 *epochtime = 0;
919 return EINVAL;
920 }
921 *epochtime = krb5int_gmt_mktime(&tme);
922 return 0;
923 }
924
925 /* Get the integer value of attribute from int. If it is not found, return
926 * ENOENT and set *val_out to 0. */
927 krb5_error_code
krb5_ldap_get_value(LDAP * ld,LDAPMessage * ent,char * attribute,int * val_out)928 krb5_ldap_get_value(LDAP *ld, LDAPMessage *ent, char *attribute, int *val_out)
929 {
930 char **values;
931
932 *val_out = 0;
933 values = ldap_get_values(ld, ent, attribute);
934 if (values == NULL)
935 return ENOENT;
936 if (values[0] != NULL)
937 *val_out = atoi(values[0]);
938 ldap_value_free(values);
939 return 0;
940 }
941
942 /* Return the first string value of attribute in ent. */
943 krb5_error_code
krb5_ldap_get_string(LDAP * ld,LDAPMessage * ent,char * attribute,char ** str_out,krb5_boolean * attr_present)944 krb5_ldap_get_string(LDAP *ld, LDAPMessage *ent, char *attribute,
945 char **str_out, krb5_boolean *attr_present)
946 {
947 char **values;
948 krb5_error_code ret = 0;
949
950 *str_out = NULL;
951 if (attr_present != NULL)
952 *attr_present = FALSE;
953
954 values = ldap_get_values(ld, ent, attribute);
955 if (values == NULL)
956 return 0;
957 if (values[0] != NULL) {
958 if (attr_present != NULL)
959 *attr_present = TRUE;
960 *str_out = strdup(values[0]);
961 if (*str_out == NULL)
962 ret = ENOMEM;
963 }
964 ldap_value_free(values);
965 return ret;
966 }
967
968 static krb5_error_code
get_time(LDAP * ld,LDAPMessage * ent,char * attribute,krb5_timestamp * time_out,krb5_boolean * attr_present)969 get_time(LDAP *ld, LDAPMessage *ent, char *attribute, krb5_timestamp *time_out,
970 krb5_boolean *attr_present)
971 {
972 char **values = NULL;
973 krb5_error_code ret = 0;
974
975 *time_out = 0;
976 *attr_present = FALSE;
977
978 values = ldap_get_values(ld, ent, attribute);
979 if (values == NULL)
980 return 0;
981 if (values[0] != NULL) {
982 *attr_present = TRUE;
983 ret = getepochtime(values[0], time_out);
984 }
985 ldap_value_free(values);
986 return ret;
987 }
988
989 /* Add an entry to *list_inout and return it in *mod_out. */
990 static krb5_error_code
alloc_mod(LDAPMod *** list_inout,LDAPMod ** mod_out)991 alloc_mod(LDAPMod ***list_inout, LDAPMod **mod_out)
992 {
993 size_t count;
994 LDAPMod **mods = *list_inout;
995
996 *mod_out = NULL;
997
998 for (count = 0; mods != NULL && mods[count] != NULL; count++);
999 mods = realloc(mods, (count + 2) * sizeof(*mods));
1000 if (mods == NULL)
1001 return ENOMEM;
1002 *list_inout = mods;
1003
1004 mods[count] = calloc(1, sizeof(LDAPMod));
1005 if (mods[count] == NULL)
1006 return ENOMEM;
1007 mods[count + 1] = NULL;
1008 *mod_out = mods[count];
1009 return 0;
1010 }
1011
1012 krb5_error_code
krb5_add_str_mem_ldap_mod(LDAPMod *** list,char * attribute,int op,char ** values)1013 krb5_add_str_mem_ldap_mod(LDAPMod ***list, char *attribute, int op,
1014 char **values)
1015 {
1016 krb5_error_code ret;
1017 LDAPMod *mod;
1018 size_t count, i;
1019
1020 ret = alloc_mod(list, &mod);
1021 if (ret)
1022 return ret;
1023
1024 mod->mod_type = strdup(attribute);
1025 if (mod->mod_type == NULL)
1026 return ENOMEM;
1027 mod->mod_op = op;
1028
1029 mod->mod_values = NULL;
1030 if (values == NULL)
1031 return 0;
1032
1033 for (count = 0; values[count] != NULL; count++);
1034 mod->mod_values = calloc(count + 1, sizeof(char *));
1035 if (mod->mod_values == NULL)
1036 return ENOMEM;
1037
1038 for (i = 0; i < count; i++) {
1039 mod->mod_values[i] = strdup(values[i]);
1040 if (mod->mod_values[i] == NULL)
1041 return ENOMEM;
1042 }
1043 mod->mod_values[i] = NULL;
1044 return 0;
1045 }
1046
1047 krb5_error_code
krb5_add_ber_mem_ldap_mod(LDAPMod *** list,char * attribute,int op,struct berval ** ber_values)1048 krb5_add_ber_mem_ldap_mod(LDAPMod ***list, char *attribute, int op,
1049 struct berval **ber_values)
1050 {
1051 krb5_error_code ret;
1052 LDAPMod *mod;
1053 size_t count, i;
1054
1055 ret = alloc_mod(list, &mod);
1056 if (ret)
1057 return ret;
1058
1059 mod->mod_type = strdup(attribute);
1060 if (mod->mod_type == NULL)
1061 return ENOMEM;
1062 mod->mod_op = op;
1063
1064 for (count = 0; ber_values[count] != NULL; count++);
1065 mod->mod_bvalues = calloc(count + 1, sizeof(struct berval *));
1066 if (mod->mod_bvalues == NULL)
1067 return ENOMEM;
1068
1069 for (i = 0; i < count; i++) {
1070 mod->mod_bvalues[i] = calloc(1, sizeof(struct berval));
1071 if (mod->mod_bvalues[i] == NULL)
1072 return ENOMEM;
1073
1074 mod->mod_bvalues[i]->bv_len = ber_values[i]->bv_len;
1075 mod->mod_bvalues[i]->bv_val = k5memdup(ber_values[i]->bv_val,
1076 ber_values[i]->bv_len, &ret);
1077 if (mod->mod_bvalues[i]->bv_val == NULL)
1078 return ret;
1079 }
1080 mod->mod_bvalues[i] = NULL;
1081 return 0;
1082 }
1083
1084 static inline char *
format_d(int val)1085 format_d(int val)
1086 {
1087 char tmpbuf[3 * sizeof(val) + 2];
1088
1089 snprintf(tmpbuf, sizeof(tmpbuf), "%d", val);
1090 return strdup(tmpbuf);
1091 }
1092
1093 krb5_error_code
krb5_add_int_mem_ldap_mod(LDAPMod *** list,char * attribute,int op,int value)1094 krb5_add_int_mem_ldap_mod(LDAPMod ***list, char *attribute, int op, int value)
1095 {
1096 krb5_error_code ret;
1097 LDAPMod *mod;
1098
1099 ret = alloc_mod(list, &mod);
1100 if (ret)
1101 return ret;
1102
1103 mod->mod_type = strdup(attribute);
1104 if (mod->mod_type == NULL)
1105 return ENOMEM;
1106
1107 mod->mod_op = op;
1108 mod->mod_values = calloc(2, sizeof(char *));
1109 if (mod->mod_values == NULL)
1110 return ENOMEM;
1111 mod->mod_values[0] = format_d(value);
1112 if (mod->mod_values[0] == NULL)
1113 return ENOMEM;
1114 return 0;
1115 }
1116
1117 krb5_error_code
krb5_ldap_modify_ext(krb5_context context,LDAP * ld,const char * dn,LDAPMod ** mods,int op)1118 krb5_ldap_modify_ext(krb5_context context, LDAP *ld, const char *dn,
1119 LDAPMod **mods, int op)
1120 {
1121 int ret;
1122
1123 ret = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1124 return (ret == LDAP_SUCCESS) ? 0 : set_ldap_error(context, ret, op);
1125 }
1126
1127 krb5_error_code
krb5_ldap_lock(krb5_context kcontext,int mode)1128 krb5_ldap_lock(krb5_context kcontext, int mode)
1129 {
1130 krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1131
1132 k5_setmsg(kcontext, status, "LDAP %s", error_message(status));
1133 return status;
1134 }
1135
1136 krb5_error_code
krb5_ldap_unlock(krb5_context kcontext)1137 krb5_ldap_unlock(krb5_context kcontext)
1138 {
1139 krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1140
1141 k5_setmsg(kcontext, status, "LDAP %s", error_message(status));
1142 return status;
1143 }
1144
1145
1146 /*
1147 * Get the number of times an object has been referred to in a realm. This is
1148 * needed to find out if deleting the attribute will cause dangling links.
1149 *
1150 * An LDAP handle may be optionally specified to prevent race condition - there
1151 * are a limited number of LDAP handles.
1152 */
1153 krb5_error_code
krb5_ldap_get_reference_count(krb5_context context,char * dn,char * refattr,int * count,LDAP * ld)1154 krb5_ldap_get_reference_count(krb5_context context, char *dn, char *refattr,
1155 int *count, LDAP *ld)
1156 {
1157 int n, st, tempst, gothandle = 0;
1158 size_t i, ntrees = 0;
1159 char *refcntattr[2];
1160 char *filter = NULL, *corrected = NULL, **subtree = NULL;
1161 kdb5_dal_handle *dal_handle = NULL;
1162 krb5_ldap_context *ldap_context = NULL;
1163 krb5_ldap_server_handle *ldap_server_handle = NULL;
1164 LDAPMessage *result = NULL;
1165
1166 if (dn == NULL || refattr == NULL) {
1167 st = EINVAL;
1168 goto cleanup;
1169 }
1170
1171 SETUP_CONTEXT();
1172 if (ld == NULL) {
1173 GET_HANDLE();
1174 gothandle = 1;
1175 }
1176
1177 refcntattr[0] = refattr;
1178 refcntattr[1] = NULL;
1179
1180 corrected = ldap_filter_correct(dn);
1181 if (corrected == NULL) {
1182 st = ENOMEM;
1183 goto cleanup;
1184 }
1185
1186 if (asprintf(&filter, "%s=%s", refattr, corrected) < 0) {
1187 filter = NULL;
1188 st = ENOMEM;
1189 goto cleanup;
1190 }
1191
1192 st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees);
1193 if (st)
1194 goto cleanup;
1195
1196 for (i = 0, *count = 0; i < ntrees; i++) {
1197 LDAP_SEARCH(subtree[i], LDAP_SCOPE_SUBTREE, filter, refcntattr);
1198 n = ldap_count_entries(ld, result);
1199 if (n == -1) {
1200 int ret, errcode = 0;
1201 ret = ldap_parse_result(ld, result, &errcode, NULL, NULL, NULL,
1202 NULL, 0);
1203 if (ret != LDAP_SUCCESS)
1204 errcode = ret;
1205 st = translate_ldap_error(errcode, OP_SEARCH);
1206 goto cleanup;
1207 }
1208
1209 ldap_msgfree(result);
1210 result = NULL;
1211
1212 *count += n;
1213 }
1214
1215 cleanup:
1216 free(filter);
1217 ldap_msgfree(result);
1218 for (i = 0; i < ntrees; i++)
1219 free(subtree[i]);
1220 free(subtree);
1221 free(corrected);
1222 if (gothandle)
1223 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1224 return st;
1225 }
1226
1227 /* Extract a name from policy_dn, which must be directly under the realm
1228 * container. */
1229 krb5_error_code
krb5_ldap_policydn_to_name(krb5_context context,const char * policy_dn,char ** name_out)1230 krb5_ldap_policydn_to_name(krb5_context context, const char *policy_dn,
1231 char **name_out)
1232 {
1233 size_t len1, len2, plen;
1234 krb5_error_code ret;
1235 kdb5_dal_handle *dal_handle;
1236 krb5_ldap_context *ldap_context;
1237 const char *realmdn;
1238 char *rdn;
1239 LDAPDN dn;
1240
1241 *name_out = NULL;
1242 SETUP_CONTEXT();
1243
1244 realmdn = ldap_context->lrparams->realmdn;
1245 if (realmdn == NULL)
1246 return EINVAL;
1247
1248 /* policyn_dn should be "cn=<policyname>,<realmdn>". */
1249 len1 = strlen(realmdn);
1250 len2 = strlen(policy_dn);
1251 if (len1 == 0 || len2 == 0 || len1 + 1 >= len2)
1252 return EINVAL;
1253 plen = len2 - len1 - 1;
1254 if (policy_dn[plen] != ',' || strcmp(realmdn, policy_dn + plen + 1) != 0)
1255 return EINVAL;
1256
1257 rdn = k5memdup0(policy_dn, plen, &ret);
1258 if (rdn == NULL)
1259 return ret;
1260 ret = ldap_str2dn(rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC);
1261 free(rdn);
1262 if (ret)
1263 return EINVAL;
1264 if (dn[0] == NULL || dn[1] != NULL || dn[0][0]->la_attr.bv_len != 2 ||
1265 strncasecmp(dn[0][0]->la_attr.bv_val, "cn", 2) != 0) {
1266 ret = EINVAL;
1267 } else {
1268 *name_out = k5memdup0(dn[0][0]->la_value.bv_val,
1269 dn[0][0]->la_value.bv_len, &ret);
1270 }
1271 ldap_dnfree(dn);
1272 return ret;
1273 }
1274
1275 /* Compute the policy DN for the given policy name. */
1276 krb5_error_code
krb5_ldap_name_to_policydn(krb5_context context,char * name,char ** policy_dn)1277 krb5_ldap_name_to_policydn(krb5_context context, char *name, char **policy_dn)
1278 {
1279 int st;
1280 char *corrected;
1281 kdb5_dal_handle *dal_handle;
1282 krb5_ldap_context *ldap_context;
1283
1284 *policy_dn = NULL;
1285
1286 /* Used for removing policy reference from an object */
1287 if (name[0] == '\0') {
1288 *policy_dn = strdup("");
1289 return (*policy_dn == NULL) ? ENOMEM : 0;
1290 }
1291
1292 SETUP_CONTEXT();
1293
1294 if (ldap_context->lrparams->realmdn == NULL)
1295 return EINVAL;
1296
1297 corrected = ldap_filter_correct(name);
1298 if (corrected == NULL)
1299 return ENOMEM;
1300
1301 st = asprintf(policy_dn, "cn=%s,%s", corrected,
1302 ldap_context->lrparams->realmdn);
1303 free(corrected);
1304 if (st == -1) {
1305 *policy_dn = NULL;
1306 return ENOMEM;
1307 }
1308 return 0;
1309 }
1310
1311 /* Return true if dn1 is a subtree of dn2. */
1312 static inline krb5_boolean
is_subtree(const char * dn1,size_t len1,const char * dn2,size_t len2)1313 is_subtree(const char *dn1, size_t len1, const char *dn2, size_t len2)
1314 {
1315 return len1 > len2 && dn1[len1 - len2 - 1] == ',' &&
1316 strcasecmp(dn1 + (len1 - len2), dn2) == 0;
1317 }
1318
1319 /* Remove overlapping and repeated subtree entries from the list of subtrees.
1320 * If sscope is not 2 (sub), only remove repeated entries. */
1321 static void
remove_overlapping_subtrees(char ** list,size_t * subtcount,int sscope)1322 remove_overlapping_subtrees(char **list, size_t *subtcount, int sscope)
1323 {
1324 size_t ilen, jlen, i, j, count = *subtcount;
1325
1326 for (i = 0; i < count && list[i] != NULL; i++) {
1327 ilen = strlen(list[i]);
1328 for (j = i + 1; j < count && list[j] != NULL; j++) {
1329 jlen = strlen(list[j]);
1330 /* Remove list[j] if it is identical to list[i] or a subtree of it.
1331 * Remove list[i] if it is a subtree of list[j]. */
1332 if ((ilen == jlen && strcasecmp(list[j], list[i]) == 0) ||
1333 (sscope == 2 && is_subtree(list[j], jlen, list[i], ilen))) {
1334 free(list[j]);
1335 list[j--] = list[count - 1];
1336 list[--count] = NULL;
1337 } else if (sscope == 2 &&
1338 is_subtree(list[i], ilen, list[j], jlen)) {
1339 free(list[i]);
1340 list[i--] = list[count - 1];
1341 list[--count] = NULL;
1342 break;
1343 }
1344 }
1345 }
1346 *subtcount = count;
1347 }
1348
1349 static void
free_princ_ent_contents(osa_princ_ent_t princ_ent)1350 free_princ_ent_contents(osa_princ_ent_t princ_ent)
1351 {
1352 unsigned int i;
1353
1354 for (i = 0; i < princ_ent->old_key_len; i++) {
1355 k5_free_key_data(princ_ent->old_keys[i].n_key_data,
1356 princ_ent->old_keys[i].key_data);
1357 princ_ent->old_keys[i].n_key_data = 0;
1358 princ_ent->old_keys[i].key_data = NULL;
1359 }
1360 free(princ_ent->old_keys);
1361 princ_ent->old_keys = NULL;
1362 princ_ent->old_key_len = 0;
1363 }
1364
1365 /* Get any auth indicator values from LDAP and update the "require_auth"
1366 * string. */
1367 static krb5_error_code
get_ldap_auth_ind(krb5_context context,LDAP * ld,LDAPMessage * ldap_ent,krb5_db_entry * entry,unsigned int * mask)1368 get_ldap_auth_ind(krb5_context context, LDAP *ld, LDAPMessage *ldap_ent,
1369 krb5_db_entry *entry, unsigned int *mask)
1370 {
1371 krb5_error_code ret;
1372 size_t i;
1373 char **auth_inds = NULL, *indstr;
1374 struct k5buf buf = EMPTY_K5BUF;
1375
1376 auth_inds = ldap_get_values(ld, ldap_ent, "krbPrincipalAuthInd");
1377 if (auth_inds == NULL)
1378 return 0;
1379
1380 k5_buf_init_dynamic(&buf);
1381
1382 /* Make a space-separated list of indicators. */
1383 for (i = 0; auth_inds[i] != NULL; i++) {
1384 k5_buf_add(&buf, auth_inds[i]);
1385 if (auth_inds[i + 1] != NULL)
1386 k5_buf_add(&buf, " ");
1387 }
1388
1389 indstr = k5_buf_cstring(&buf);
1390 if (indstr == NULL) {
1391 ret = ENOMEM;
1392 goto cleanup;
1393 }
1394
1395 ret = krb5_dbe_set_string(context, entry, KRB5_KDB_SK_REQUIRE_AUTH,
1396 indstr);
1397 if (!ret)
1398 *mask |= KDB_AUTH_IND_ATTR;
1399
1400 cleanup:
1401 k5_buf_free(&buf);
1402 ldap_value_free(auth_inds);
1403 return ret;
1404 }
1405
1406 /*
1407 * Fill out a krb5_db_entry princ entry struct given a LDAP message containing
1408 * the results of a principal search of the directory.
1409 */
1410 krb5_error_code
populate_krb5_db_entry(krb5_context context,krb5_ldap_context * ldap_context,LDAP * ld,LDAPMessage * ent,krb5_const_principal princ,krb5_db_entry * entry)1411 populate_krb5_db_entry(krb5_context context, krb5_ldap_context *ldap_context,
1412 LDAP *ld, LDAPMessage *ent, krb5_const_principal princ,
1413 krb5_db_entry *entry)
1414 {
1415 krb5_error_code ret;
1416 unsigned int mask = 0;
1417 size_t i;
1418 int val, pcount, objtype;
1419 krb5_boolean attr_present;
1420 krb5_kvno mkvno = 0;
1421 krb5_timestamp lastpwdchange, unlock_time;
1422 char *policydn = NULL, *pwdpolicydn = NULL, *polname = NULL, *user = NULL;
1423 char *tktpolname = NULL, *dn = NULL, **link_references = NULL;
1424 char **pnvalues = NULL, **ocvalues = NULL, **a2d2 = NULL;
1425 struct berval **ber_key_data = NULL, **ber_tl_data = NULL;
1426 krb5_tl_data userinfo_tl_data = { NULL }, **endp, *tl;
1427 osa_princ_ent_rec princ_ent;
1428 char *is_login_disabled = NULL;
1429
1430 memset(&princ_ent, 0, sizeof(princ_ent));
1431
1432 ret = krb5_copy_principal(context, princ, &entry->princ);
1433 if (ret)
1434 goto cleanup;
1435
1436 /* get the associated directory user information */
1437 pnvalues = ldap_get_values(ld, ent, "krbprincipalname");
1438 if (pnvalues != NULL) {
1439 ret = krb5_unparse_name(context, princ, &user);
1440 if (ret)
1441 goto cleanup;
1442
1443 pcount = 0;
1444 for (i = 0; pnvalues[i] != NULL; i++) {
1445 if (strcasecmp(pnvalues[i], user) == 0) {
1446 pcount = ldap_count_values(pnvalues);
1447 break;
1448 }
1449 }
1450
1451 dn = ldap_get_dn(ld, ent);
1452 if (dn == NULL) {
1453 ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &ret);
1454 ret = set_ldap_error(context, ret, 0);
1455 goto cleanup;
1456 }
1457
1458 ocvalues = ldap_get_values(ld, ent, "objectclass");
1459 if (ocvalues != NULL) {
1460 for (i = 0; ocvalues[i] != NULL; i++) {
1461 if (strcasecmp(ocvalues[i], "krbprincipal") == 0) {
1462 objtype = KDB_STANDALONE_PRINCIPAL_OBJECT;
1463 ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE,
1464 &objtype);
1465 if (ret)
1466 goto cleanup;
1467 break;
1468 }
1469 }
1470 }
1471
1472 /* Add principalcount, DN and principaltype user information to
1473 * tl_data */
1474 ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount);
1475 if (ret)
1476 goto cleanup;
1477 ret = store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, dn);
1478 if (ret)
1479 goto cleanup;
1480 }
1481
1482 ret = get_time(ld, ent, "krbLastSuccessfulAuth", &entry->last_success,
1483 &attr_present);
1484 if (ret)
1485 goto cleanup;
1486 if (attr_present)
1487 mask |= KDB_LAST_SUCCESS_ATTR;
1488
1489 ret = get_time(ld, ent, "krbLastFailedAuth", &entry->last_failed,
1490 &attr_present);
1491 if (ret)
1492 goto cleanup;
1493 if (attr_present)
1494 mask |= KDB_LAST_FAILED_ATTR;
1495
1496 if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount", &val) == 0) {
1497 entry->fail_auth_count = val;
1498 mask |= KDB_FAIL_AUTH_COUNT_ATTR;
1499 }
1500 if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &val) == 0) {
1501 entry->max_life = val;
1502 mask |= KDB_MAX_LIFE_ATTR;
1503 }
1504 if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage", &val) == 0) {
1505 entry->max_renewable_life = val;
1506 mask |= KDB_MAX_RLIFE_ATTR;
1507 }
1508 if (krb5_ldap_get_value(ld, ent, "krbticketflags", &val) == 0) {
1509 entry->attributes = val;
1510 mask |= KDB_TKT_FLAGS_ATTR;
1511 }
1512 ret = get_time(ld, ent, "krbprincipalexpiration", &entry->expiration,
1513 &attr_present);
1514 if (ret)
1515 goto cleanup;
1516 if (attr_present)
1517 mask |= KDB_PRINC_EXPIRE_TIME_ATTR;
1518
1519 ret = get_time(ld, ent, "krbpasswordexpiration", &entry->pw_expiration,
1520 &attr_present);
1521 if (ret)
1522 goto cleanup;
1523 if (attr_present)
1524 mask |= KDB_PWD_EXPIRE_TIME_ATTR;
1525
1526 ret = krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn,
1527 &attr_present);
1528 if (ret)
1529 goto cleanup;
1530 if (attr_present) {
1531 mask |= KDB_POL_REF_ATTR;
1532 /* Ensure that the policy is inside the realm container. */
1533 ret = krb5_ldap_policydn_to_name(context, policydn, &tktpolname);
1534 if (ret)
1535 goto cleanup;
1536 }
1537
1538 ret = krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn,
1539 &attr_present);
1540 if (ret)
1541 goto cleanup;
1542 if (attr_present) {
1543 mask |= KDB_PWD_POL_REF_ATTR;
1544
1545 /* Ensure that the policy is inside the realm container. */
1546 ret = krb5_ldap_policydn_to_name(context, pwdpolicydn, &polname);
1547 if (ret)
1548 goto cleanup;
1549 princ_ent.policy = polname;
1550 princ_ent.aux_attributes |= KADM5_POLICY;
1551 }
1552
1553 ber_key_data = ldap_get_values_len(ld, ent, "krbpwdhistory");
1554 if (ber_key_data != NULL) {
1555 mask |= KDB_PWD_HISTORY_ATTR;
1556 ret = krb5_decode_histkey(context, ber_key_data, &princ_ent);
1557 if (ret)
1558 goto cleanup;
1559 ldap_value_free_len(ber_key_data);
1560 }
1561
1562 if (princ_ent.aux_attributes) {
1563 ret = krb5_update_tl_kadm_data(context, entry, &princ_ent);
1564 if (ret)
1565 goto cleanup;
1566 }
1567
1568 ber_key_data = ldap_get_values_len(ld, ent, "krbprincipalkey");
1569 if (ber_key_data != NULL) {
1570 mask |= KDB_SECRET_KEY_ATTR;
1571 ret = krb5_decode_krbsecretkey(context, entry, ber_key_data, &mkvno);
1572 if (ret)
1573 goto cleanup;
1574 if (mkvno != 0) {
1575 ret = krb5_dbe_update_mkvno(context, entry, mkvno);
1576 if (ret)
1577 goto cleanup;
1578 }
1579 }
1580
1581 ret = get_time(ld, ent, "krbLastPwdChange", &lastpwdchange, &attr_present);
1582 if (ret)
1583 goto cleanup;
1584 if (attr_present) {
1585 ret = krb5_dbe_update_last_pwd_change(context, entry, lastpwdchange);
1586 if (ret)
1587 goto cleanup;
1588 mask |= KDB_LAST_PWD_CHANGE_ATTR;
1589 }
1590
1591 ret = get_time(ld, ent, "krbLastAdminUnlock", &unlock_time, &attr_present);
1592 if (ret)
1593 goto cleanup;
1594 if (attr_present) {
1595 ret = krb5_dbe_update_last_admin_unlock(context, entry, unlock_time);
1596 if (ret)
1597 goto cleanup;
1598 mask |= KDB_LAST_ADMIN_UNLOCK_ATTR;
1599 }
1600
1601 a2d2 = ldap_get_values(ld, ent, "krbAllowedToDelegateTo");
1602 if (a2d2 != NULL) {
1603 for (endp = &entry->tl_data; *endp; endp = &(*endp)->tl_data_next);
1604 for (i = 0; a2d2[i] != NULL; i++) {
1605 tl = k5alloc(sizeof(*tl), &ret);
1606 if (tl == NULL)
1607 goto cleanup;
1608 tl->tl_data_type = KRB5_TL_CONSTRAINED_DELEGATION_ACL;
1609 tl->tl_data_length = strlen(a2d2[i]) + 1;
1610 tl->tl_data_contents = (unsigned char *)strdup(a2d2[i]);
1611 if (tl->tl_data_contents == NULL) {
1612 ret = ENOMEM;
1613 free(tl);
1614 goto cleanup;
1615 }
1616 tl->tl_data_next = NULL;
1617 *endp = tl;
1618 endp = &tl->tl_data_next;
1619 }
1620 }
1621
1622 link_references = ldap_get_values(ld, ent, "krbobjectreferences");
1623 if (link_references != NULL) {
1624 for (i = 0; link_references[i] != NULL; i++) {
1625 ret = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN,
1626 link_references[i]);
1627 if (ret)
1628 goto cleanup;
1629 }
1630 }
1631
1632 ber_tl_data = ldap_get_values_len(ld, ent, "krbExtraData");
1633 if (ber_tl_data != NULL) {
1634 for (i = 0; ber_tl_data[i] != NULL; i++) {
1635 ret = berval2tl_data(ber_tl_data[i], &tl);
1636 if (ret)
1637 goto cleanup;
1638 ret = krb5_dbe_update_tl_data(context, entry, tl);
1639 free(tl->tl_data_contents);
1640 free(tl);
1641 if (ret)
1642 goto cleanup;
1643 }
1644 mask |= KDB_EXTRA_DATA_ATTR;
1645 }
1646
1647 /* Auth indicators from krbPrincipalAuthInd will replace those from
1648 * krbExtraData. */
1649 ret = get_ldap_auth_ind(context, ld, ent, entry, &mask);
1650 if (ret)
1651 goto cleanup;
1652
1653 /* Update the mask of attributes present on the directory object to the
1654 * tl_data. */
1655 ret = store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask);
1656 if (ret)
1657 goto cleanup;
1658 ret = krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data);
1659 if (ret)
1660 goto cleanup;
1661
1662 /*
1663 * 389ds and other Netscape directory server derivatives support an
1664 * attribute "nsAccountLock" which functions similarly to eDirectory's
1665 * "loginDisabled". When the user's account object is also a
1666 * krbPrincipalAux object, the kdb entry should be treated as if
1667 * DISALLOW_ALL_TIX has been set.
1668 */
1669 ret = krb5_ldap_get_string(ld, ent, "nsAccountLock", &is_login_disabled,
1670 &attr_present);
1671 if (ret)
1672 goto cleanup;
1673 if (attr_present == TRUE) {
1674 if (strcasecmp(is_login_disabled, "TRUE") == 0)
1675 entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
1676 free(is_login_disabled);
1677 }
1678
1679 ret = krb5_read_tkt_policy(context, ldap_context, entry, tktpolname);
1680 if (ret)
1681 goto cleanup;
1682
1683 /* For compatibility with DB2 principals. */
1684 entry->len = KRB5_KDB_V1_BASE_LENGTH;
1685
1686 cleanup:
1687 ldap_memfree(dn);
1688 ldap_value_free_len(ber_key_data);
1689 ldap_value_free_len(ber_tl_data);
1690 ldap_value_free(pnvalues);
1691 ldap_value_free(ocvalues);
1692 ldap_value_free(link_references);
1693 ldap_value_free(a2d2);
1694 free(userinfo_tl_data.tl_data_contents);
1695 free(pwdpolicydn);
1696 free(polname);
1697 free(tktpolname);
1698 free(policydn);
1699 krb5_free_unparsed_name(context, user);
1700 free_princ_ent_contents(&princ_ent);
1701 return ret;
1702 }
1703