xref: /freebsd/crypto/krb5/src/plugins/kdb/ldap/libkdb_ldap/ldap_misc.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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, int *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     int 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,unsigned int * ntree)520 krb5_get_subtree_info(krb5_ldap_context *ldap_context, char ***subtreearr,
521                       unsigned int *ntree)
522 {
523     krb5_error_code ret;
524     int subtreecount, count = 0, search_scope;
525     char **subtree, *realm_cont_dn, *containerref;
526     char **subtarr = NULL;
527 
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;
533 
534     /* Leave space for realm DN, containerref, and null terminator. */
535     subtarr = k5calloc(subtreecount + 3, sizeof(char *), &ret);
536     if (subtarr == NULL)
537         goto cleanup;
538 
539     /* Get the complete subtree list. */
540     while (count < subtreecount && subtree[count] != NULL) {
541         subtarr[count] = strdup(subtree[count]);
542         if (subtarr[count++] == NULL) {
543             ret = ENOMEM;
544             goto cleanup;
545         }
546     }
547 
548     subtarr[count] = strdup(realm_cont_dn);
549     if (subtarr[count++] == NULL) {
550         ret = ENOMEM;
551         goto cleanup;
552     }
553 
554     if (containerref != NULL) {
555         subtarr[count] = strdup(containerref);
556         if (subtarr[count++] == NULL) {
557             ret = ENOMEM;
558             goto cleanup;
559         }
560     }
561 
562     remove_overlapping_subtrees(subtarr, &count, search_scope);
563     *ntree = count;
564     *subtreearr = subtarr;
565     subtarr = NULL;
566     count = 0;
567 
568 cleanup:
569     while (count > 0)
570         free(subtarr[--count]);
571     free(subtarr);
572     return ret;
573 }
574 
575 /* Reallocate tl and return a pointer to the new space, or NULL on failure. */
576 static unsigned char *
expand_tl_data(krb5_tl_data * tl,uint16_t len)577 expand_tl_data(krb5_tl_data *tl, uint16_t len)
578 {
579     unsigned char *newptr;
580 
581     if (len > UINT16_MAX - tl->tl_data_length)
582         return NULL;
583     newptr = realloc(tl->tl_data_contents, tl->tl_data_length + len);
584     if (newptr == NULL)
585         return NULL;
586     tl->tl_data_contents = newptr;
587     tl->tl_data_length += len;
588     return tl->tl_data_contents + tl->tl_data_length - len;
589 }
590 
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. */
593 krb5_error_code
store_tl_data(krb5_tl_data * tl,int type,void * value)594 store_tl_data(krb5_tl_data *tl, int type, void *value)
595 {
596     unsigned char *ptr;
597     int ival;
598     char *str;
599     size_t len;
600 
601     tl->tl_data_type = KDB_TL_USER_INFO;
602     switch (type) {
603     case KDB_TL_PRINCCOUNT:
604     case KDB_TL_PRINCTYPE:
605     case KDB_TL_MASK:
606         ival = *(int *)value;
607         if (ival > UINT16_MAX)
608             return EINVAL;
609         ptr = expand_tl_data(tl, 5);
610         if (ptr == NULL)
611             return ENOMEM;
612         *ptr = type;
613         store_16_be(2, ptr + 1);
614         store_16_be(ival, ptr + 3);
615         break;
616 
617     case KDB_TL_USERDN:
618     case KDB_TL_LINKDN:
619         str = value;
620         len = strlen(str);
621         if (len > UINT16_MAX - 3)
622             return ENOMEM;
623         ptr = expand_tl_data(tl, 3 + len);
624         if (ptr == NULL)
625             return ENOMEM;
626         *ptr = type;
627         store_16_be(len, ptr + 1);
628         memcpy(ptr + 3, str, len);
629         break;
630 
631     default:
632         return EINVAL;
633     }
634     return 0;
635 }
636 
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
decode_tl_data(krb5_tl_data * tl,int type,void ** data_out)640 decode_tl_data(krb5_tl_data *tl, int type, void **data_out)
641 {
642     krb5_error_code ret;
643     const unsigned char *ptr, *end;
644     uint16_t len;
645     size_t linkcount = 0, i;
646     char **dnlist = NULL, **newlist;
647     int *intptr;
648 
649     *data_out = NULL;
650 
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;
655     for (;;) {
656         if (end - ptr < 3)
657             break;
658         len = load_16_be(ptr + 1);
659         if (len > (end - ptr) - 3)
660             break;
661         if (*ptr != type) {
662             ptr += 3 + len;
663             continue;
664         }
665         ptr += 3;
666 
667         switch (type) {
668         case KDB_TL_PRINCCOUNT:
669         case KDB_TL_PRINCTYPE:
670         case KDB_TL_MASK:
671             if (len != 2)
672                 return EINVAL;
673             intptr = malloc(sizeof(int));
674             if (intptr == NULL)
675                 return ENOMEM;
676             *intptr = load_16_be(ptr);
677             *data_out = intptr;
678             return 0;
679 
680         case KDB_TL_USERDN:
681             *data_out = k5memdup0(ptr, len, &ret);
682             return ret;
683 
684         case KDB_TL_LINKDN:
685             newlist = realloc(dnlist, (linkcount + 2) * sizeof(char *));
686             if (newlist == NULL)
687                 goto oom;
688             dnlist = newlist;
689             dnlist[linkcount] = k5memdup0(ptr, len, &ret);
690             if (dnlist[linkcount] == NULL)
691                 goto oom;
692             dnlist[linkcount + 1] = NULL;
693             linkcount++;
694             break;
695         }
696 
697         ptr += len;
698     }
699 
700     if (type != KDB_TL_LINKDN || dnlist == NULL)
701         return ENOENT;
702     *data_out = dnlist;
703     return 0;
704 
705 oom:
706     for (i = 0; i < linkcount; i++)
707         free(dnlist[i]);
708     free(dnlist);
709     return ENOMEM;
710 }
711 
712 /*
713  * wrapper routines for decode_tl_data
714  */
715 static krb5_error_code
get_int_from_tl_data(krb5_context context,krb5_db_entry * entry,int type,int * intval)716 get_int_from_tl_data(krb5_context context, krb5_db_entry *entry, int type,
717                      int *intval)
718 {
719     krb5_error_code ret;
720     krb5_tl_data tl_data;
721     void *ptr;
722     int *intptr;
723 
724     *intval = 0;
725 
726     tl_data.tl_data_type = KDB_TL_USER_INFO;
727     ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
728     if (ret || tl_data.tl_data_length == 0)
729         return ret;
730 
731     if (decode_tl_data(&tl_data, type, &ptr) == 0) {
732         intptr = ptr;
733         *intval = *intptr;
734         free(intptr);
735     }
736 
737     return 0;
738 }
739 
740 /*
741  * Get the mask representing the attributes set on the directory
742  * object (user, policy ...).
743  */
744 krb5_error_code
krb5_get_attributes_mask(krb5_context context,krb5_db_entry * entry,int * mask)745 krb5_get_attributes_mask(krb5_context context, krb5_db_entry *entry,
746                          int *mask)
747 {
748     return get_int_from_tl_data(context, entry, KDB_TL_MASK, mask);
749 }
750 
751 krb5_error_code
krb5_get_princ_type(krb5_context context,krb5_db_entry * entry,int * ptype)752 krb5_get_princ_type(krb5_context context, krb5_db_entry *entry, int *ptype)
753 {
754     return get_int_from_tl_data(context, entry, KDB_TL_PRINCTYPE, ptype);
755 }
756 
757 krb5_error_code
krb5_get_princ_count(krb5_context context,krb5_db_entry * entry,int * pcount)758 krb5_get_princ_count(krb5_context context, krb5_db_entry *entry, int *pcount)
759 {
760     return get_int_from_tl_data(context, entry, KDB_TL_PRINCCOUNT, pcount);
761 }
762 
763 krb5_error_code
krb5_get_linkdn(krb5_context context,krb5_db_entry * entry,char *** link_dn)764 krb5_get_linkdn(krb5_context context, krb5_db_entry *entry, char ***link_dn)
765 {
766     krb5_error_code ret;
767     krb5_tl_data tl_data;
768     void *ptr;
769 
770     *link_dn = NULL;
771     tl_data.tl_data_type = KDB_TL_USER_INFO;
772     ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
773     if (ret || tl_data.tl_data_length == 0)
774         return ret;
775 
776     if (decode_tl_data(&tl_data, KDB_TL_LINKDN, &ptr) == 0)
777         *link_dn = ptr;
778 
779     return 0;
780 }
781 
782 static krb5_error_code
get_str_from_tl_data(krb5_context context,krb5_db_entry * entry,int type,char ** strval)783 get_str_from_tl_data(krb5_context context, krb5_db_entry *entry, int type,
784                      char **strval)
785 {
786     krb5_error_code ret;
787     krb5_tl_data tl_data;
788     void *ptr;
789 
790     if (type != KDB_TL_USERDN)
791         return EINVAL;
792 
793     tl_data.tl_data_type = KDB_TL_USER_INFO;
794     ret = krb5_dbe_lookup_tl_data(context, entry, &tl_data);
795     if (ret || tl_data.tl_data_length == 0)
796         return ret;
797 
798     if (decode_tl_data(&tl_data, type, &ptr) == 0)
799         *strval = ptr;
800 
801     return 0;
802 }
803 
804 /*
805  * Replace the relative DN component of dn with newrdn.
806  */
807 krb5_error_code
replace_rdn(krb5_context context,const char * dn,const char * newrdn,char ** newdn_out)808 replace_rdn(krb5_context context, const char *dn, const char *newrdn,
809             char **newdn_out)
810 {
811     krb5_error_code ret;
812     LDAPDN ldn = NULL;
813     LDAPRDN lrdn = NULL;
814     char *next;
815 
816     *newdn_out = NULL;
817 
818     ret = ldap_str2dn(dn, &ldn, LDAP_DN_FORMAT_LDAPV3);
819     if (ret != LDAP_SUCCESS || ldn[0] == NULL) {
820         ret = EINVAL;
821         goto cleanup;
822     }
823 
824     ret = ldap_str2rdn(newrdn, &lrdn, &next, LDAP_DN_FORMAT_LDAPV3);
825     if (ret != LDAP_SUCCESS) {
826         ret = EINVAL;
827         goto cleanup;
828     }
829 
830     ldap_rdnfree(ldn[0]);
831     ldn[0] = lrdn;
832     lrdn = NULL;
833 
834     ret = ldap_dn2str(ldn, newdn_out, LDAP_DN_FORMAT_LDAPV3);
835     if (ret != LDAP_SUCCESS)
836         ret = KRB5_KDB_SERVER_INTERNAL_ERR;
837 
838 cleanup:
839     if (ldn != NULL)
840         ldap_dnfree(ldn);
841     if (lrdn != NULL)
842         ldap_rdnfree(lrdn);
843     return ret;
844 }
845 
846 krb5_error_code
krb5_get_userdn(krb5_context context,krb5_db_entry * entry,char ** userdn)847 krb5_get_userdn(krb5_context context, krb5_db_entry *entry, char **userdn)
848 {
849     *userdn = NULL;
850     return get_str_from_tl_data(context, entry, KDB_TL_USERDN, userdn);
851 }
852 
853 /*
854  * If attribute or attrvalues is NULL, just check for the existence of dn.
855  * Otherwise, read values for attribute from dn; then set the bit 1<<n in mask
856  * for each attrvalues[n] which is present in the values read.
857  */
858 krb5_error_code
checkattributevalue(LDAP * ld,char * dn,char * attribute,char ** attrvalues,int * mask)859 checkattributevalue(LDAP *ld, char *dn, char *attribute, char **attrvalues,
860                     int *mask)
861 {
862     krb5_error_code ret;
863     int one = 1, i, j;
864     char **values = NULL, *attributes[2] = { NULL };
865     LDAPMessage *result = NULL, *entry;
866 
867     if (strlen(dn) == 0)
868         return set_ldap_error(0, LDAP_NO_SUCH_OBJECT, OP_SEARCH);
869 
870     attributes[0] = attribute;
871 
872     /* Read values for attribute from the dn, or check for its existence. */
873     ret = ldap_search_ext_s(ld, dn, LDAP_SCOPE_BASE, 0, attributes, 0, NULL,
874                             NULL, &timelimit, LDAP_NO_LIMIT, &result);
875     if (ret != LDAP_SUCCESS) {
876         ldap_msgfree(result);
877         return set_ldap_error(0, ret, OP_SEARCH);
878     }
879 
880     /* Don't touch *mask if we are only checking for existence. */
881     if (attribute == NULL || attrvalues == NULL)
882         goto done;
883 
884     *mask = 0;
885 
886     entry = ldap_first_entry(ld, result);
887     if (entry == NULL)
888         goto done;
889     values = ldap_get_values(ld, entry, attribute);
890     if (values == NULL)
891         goto done;
892 
893     /* Set bits in mask for each matching value we read. */
894     for (i = 0; attrvalues[i]; i++) {
895         for (j = 0; values[j]; j++) {
896             if (strcasecmp(attrvalues[i], values[j]) == 0) {
897                 *mask |= (one << i);
898                 break;
899             }
900         }
901     }
902 
903 done:
904     ldap_msgfree(result);
905     ldap_value_free(values);
906     return 0;
907 }
908 
909 static krb5_error_code
getepochtime(char * strtime,krb5_timestamp * epochtime)910 getepochtime(char *strtime, krb5_timestamp *epochtime)
911 {
912     struct tm tme;
913 
914     memset(&tme, 0, sizeof(tme));
915     if (strptime(strtime,"%Y%m%d%H%M%SZ", &tme) == NULL) {
916         *epochtime = 0;
917         return EINVAL;
918     }
919     *epochtime = krb5int_gmt_mktime(&tme);
920     return 0;
921 }
922 
923 /* Get the integer value of attribute from int.  If it is not found, return
924  * ENOENT and set *val_out to 0. */
925 krb5_error_code
krb5_ldap_get_value(LDAP * ld,LDAPMessage * ent,char * attribute,int * val_out)926 krb5_ldap_get_value(LDAP *ld, LDAPMessage *ent, char *attribute, int *val_out)
927 {
928     char **values;
929 
930     *val_out = 0;
931     values = ldap_get_values(ld, ent, attribute);
932     if (values == NULL)
933         return ENOENT;
934     if (values[0] != NULL)
935         *val_out = atoi(values[0]);
936     ldap_value_free(values);
937     return 0;
938 }
939 
940 /* Return the first string value of attribute in ent. */
941 krb5_error_code
krb5_ldap_get_string(LDAP * ld,LDAPMessage * ent,char * attribute,char ** str_out,krb5_boolean * attr_present)942 krb5_ldap_get_string(LDAP *ld, LDAPMessage *ent, char *attribute,
943                      char **str_out, krb5_boolean *attr_present)
944 {
945     char **values;
946     krb5_error_code ret = 0;
947 
948     *str_out = NULL;
949     if (attr_present != NULL)
950         *attr_present = FALSE;
951 
952     values = ldap_get_values(ld, ent, attribute);
953     if (values == NULL)
954         return 0;
955     if (values[0] != NULL) {
956         if (attr_present != NULL)
957             *attr_present = TRUE;
958         *str_out = strdup(values[0]);
959         if (*str_out == NULL)
960             ret = ENOMEM;
961     }
962     ldap_value_free(values);
963     return ret;
964 }
965 
966 static krb5_error_code
get_time(LDAP * ld,LDAPMessage * ent,char * attribute,krb5_timestamp * time_out,krb5_boolean * attr_present)967 get_time(LDAP *ld, LDAPMessage *ent, char *attribute, krb5_timestamp *time_out,
968          krb5_boolean *attr_present)
969 {
970     char **values = NULL;
971     krb5_error_code ret = 0;
972 
973     *time_out = 0;
974     *attr_present = FALSE;
975 
976     values = ldap_get_values(ld, ent, attribute);
977     if (values == NULL)
978         return 0;
979     if (values[0] != NULL) {
980         *attr_present = TRUE;
981         ret = getepochtime(values[0], time_out);
982     }
983     ldap_value_free(values);
984     return ret;
985 }
986 
987 /* Add an entry to *list_inout and return it in *mod_out. */
988 static krb5_error_code
alloc_mod(LDAPMod *** list_inout,LDAPMod ** mod_out)989 alloc_mod(LDAPMod ***list_inout, LDAPMod **mod_out)
990 {
991     size_t count;
992     LDAPMod **mods = *list_inout;
993 
994     *mod_out = NULL;
995 
996     for (count = 0; mods != NULL && mods[count] != NULL; count++);
997     mods = realloc(mods, (count + 2) * sizeof(*mods));
998     if (mods == NULL)
999         return ENOMEM;
1000     *list_inout = mods;
1001 
1002     mods[count] = calloc(1, sizeof(LDAPMod));
1003     if (mods[count] == NULL)
1004         return ENOMEM;
1005     mods[count + 1] = NULL;
1006     *mod_out = mods[count];
1007     return 0;
1008 }
1009 
1010 krb5_error_code
krb5_add_str_mem_ldap_mod(LDAPMod *** list,char * attribute,int op,char ** values)1011 krb5_add_str_mem_ldap_mod(LDAPMod ***list, char *attribute, int op,
1012                           char **values)
1013 {
1014     krb5_error_code ret;
1015     LDAPMod *mod;
1016     size_t count, i;
1017 
1018     ret = alloc_mod(list, &mod);
1019     if (ret)
1020         return ret;
1021 
1022     mod->mod_type = strdup(attribute);
1023     if (mod->mod_type == NULL)
1024         return ENOMEM;
1025     mod->mod_op = op;
1026 
1027     mod->mod_values = NULL;
1028     if (values == NULL)
1029         return 0;
1030 
1031     for (count = 0; values[count] != NULL; count++);
1032     mod->mod_values = calloc(count + 1, sizeof(char *));
1033     if (mod->mod_values == NULL)
1034         return ENOMEM;
1035 
1036     for (i = 0; i < count; i++) {
1037         mod->mod_values[i] = strdup(values[i]);
1038         if (mod->mod_values[i] == NULL)
1039             return ENOMEM;
1040     }
1041     mod->mod_values[i] = NULL;
1042     return 0;
1043 }
1044 
1045 krb5_error_code
krb5_add_ber_mem_ldap_mod(LDAPMod *** list,char * attribute,int op,struct berval ** ber_values)1046 krb5_add_ber_mem_ldap_mod(LDAPMod ***list, char *attribute, int op,
1047                           struct berval **ber_values)
1048 {
1049     krb5_error_code ret;
1050     LDAPMod *mod;
1051     size_t count, i;
1052 
1053     ret = alloc_mod(list, &mod);
1054     if (ret)
1055         return ret;
1056 
1057     mod->mod_type = strdup(attribute);
1058     if (mod->mod_type == NULL)
1059         return ENOMEM;
1060     mod->mod_op = op;
1061 
1062     for (count = 0; ber_values[count] != NULL; count++);
1063     mod->mod_bvalues = calloc(count + 1, sizeof(struct berval *));
1064     if (mod->mod_bvalues == NULL)
1065         return ENOMEM;
1066 
1067     for (i = 0; i < count; i++) {
1068         mod->mod_bvalues[i] = calloc(1, sizeof(struct berval));
1069         if (mod->mod_bvalues[i] == NULL)
1070             return ENOMEM;
1071 
1072         mod->mod_bvalues[i]->bv_len = ber_values[i]->bv_len;
1073         mod->mod_bvalues[i]->bv_val = k5memdup(ber_values[i]->bv_val,
1074                                                ber_values[i]->bv_len, &ret);
1075         if (mod->mod_bvalues[i]->bv_val == NULL)
1076             return ret;
1077     }
1078     mod->mod_bvalues[i] = NULL;
1079     return 0;
1080 }
1081 
1082 static inline char *
format_d(int val)1083 format_d(int val)
1084 {
1085     char tmpbuf[3 * sizeof(val) + 2];
1086 
1087     snprintf(tmpbuf, sizeof(tmpbuf), "%d", val);
1088     return strdup(tmpbuf);
1089 }
1090 
1091 krb5_error_code
krb5_add_int_mem_ldap_mod(LDAPMod *** list,char * attribute,int op,int value)1092 krb5_add_int_mem_ldap_mod(LDAPMod ***list, char *attribute, int op, int value)
1093 {
1094     krb5_error_code ret;
1095     LDAPMod *mod;
1096 
1097     ret = alloc_mod(list, &mod);
1098     if (ret)
1099         return ret;
1100 
1101     mod->mod_type = strdup(attribute);
1102     if (mod->mod_type == NULL)
1103         return ENOMEM;
1104 
1105     mod->mod_op = op;
1106     mod->mod_values = calloc(2, sizeof(char *));
1107     if (mod->mod_values == NULL)
1108         return ENOMEM;
1109     mod->mod_values[0] = format_d(value);
1110     if (mod->mod_values[0] == NULL)
1111         return ENOMEM;
1112     return 0;
1113 }
1114 
1115 krb5_error_code
krb5_ldap_modify_ext(krb5_context context,LDAP * ld,const char * dn,LDAPMod ** mods,int op)1116 krb5_ldap_modify_ext(krb5_context context, LDAP *ld, const char *dn,
1117                      LDAPMod **mods, int op)
1118 {
1119     int ret;
1120 
1121     ret = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1122     return (ret == LDAP_SUCCESS) ? 0 : set_ldap_error(context, ret, op);
1123 }
1124 
1125 krb5_error_code
krb5_ldap_lock(krb5_context kcontext,int mode)1126 krb5_ldap_lock(krb5_context kcontext, int mode)
1127 {
1128     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1129 
1130     k5_setmsg(kcontext, status, "LDAP %s", error_message(status));
1131     return status;
1132 }
1133 
1134 krb5_error_code
krb5_ldap_unlock(krb5_context kcontext)1135 krb5_ldap_unlock(krb5_context kcontext)
1136 {
1137     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1138 
1139     k5_setmsg(kcontext, status, "LDAP %s", error_message(status));
1140     return status;
1141 }
1142 
1143 
1144 /*
1145  * Get the number of times an object has been referred to in a realm.  This is
1146  * needed to find out if deleting the attribute will cause dangling links.
1147  *
1148  * An LDAP handle may be optionally specified to prevent race condition - there
1149  * are a limited number of LDAP handles.
1150  */
1151 krb5_error_code
krb5_ldap_get_reference_count(krb5_context context,char * dn,char * refattr,int * count,LDAP * ld)1152 krb5_ldap_get_reference_count(krb5_context context, char *dn, char *refattr,
1153                               int *count, LDAP *ld)
1154 {
1155     int n, st, tempst, gothandle = 0;
1156     unsigned int i, ntrees = 0;
1157     char *refcntattr[2];
1158     char *filter = NULL, *corrected = NULL, **subtree = NULL;
1159     kdb5_dal_handle *dal_handle = NULL;
1160     krb5_ldap_context *ldap_context = NULL;
1161     krb5_ldap_server_handle *ldap_server_handle = NULL;
1162     LDAPMessage *result = NULL;
1163 
1164     if (dn == NULL || refattr == NULL) {
1165         st = EINVAL;
1166         goto cleanup;
1167     }
1168 
1169     SETUP_CONTEXT();
1170     if (ld == NULL) {
1171         GET_HANDLE();
1172         gothandle = 1;
1173     }
1174 
1175     refcntattr[0] = refattr;
1176     refcntattr[1] = NULL;
1177 
1178     corrected = ldap_filter_correct(dn);
1179     if (corrected == NULL) {
1180         st = ENOMEM;
1181         goto cleanup;
1182     }
1183 
1184     if (asprintf(&filter, "%s=%s", refattr, corrected) < 0) {
1185         filter = NULL;
1186         st = ENOMEM;
1187         goto cleanup;
1188     }
1189 
1190     st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees);
1191     if (st)
1192         goto cleanup;
1193 
1194     for (i = 0, *count = 0; i < ntrees; i++) {
1195         LDAP_SEARCH(subtree[i], LDAP_SCOPE_SUBTREE, filter, refcntattr);
1196         n = ldap_count_entries(ld, result);
1197         if (n == -1) {
1198             int ret, errcode = 0;
1199             ret = ldap_parse_result(ld, result, &errcode, NULL, NULL, NULL,
1200                                     NULL, 0);
1201             if (ret != LDAP_SUCCESS)
1202                 errcode = ret;
1203             st = translate_ldap_error(errcode, OP_SEARCH);
1204             goto cleanup;
1205         }
1206 
1207         ldap_msgfree(result);
1208         result = NULL;
1209 
1210         *count += n;
1211     }
1212 
1213 cleanup:
1214     free(filter);
1215     ldap_msgfree(result);
1216     for (i = 0; i < ntrees; i++)
1217         free(subtree[i]);
1218     free(subtree);
1219     free(corrected);
1220     if (gothandle)
1221         krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1222     return st;
1223 }
1224 
1225 /* Extract a name from policy_dn, which must be directly under the realm
1226  * container. */
1227 krb5_error_code
krb5_ldap_policydn_to_name(krb5_context context,const char * policy_dn,char ** name_out)1228 krb5_ldap_policydn_to_name(krb5_context context, const char *policy_dn,
1229                            char **name_out)
1230 {
1231     size_t len1, len2, plen;
1232     krb5_error_code ret;
1233     kdb5_dal_handle *dal_handle;
1234     krb5_ldap_context *ldap_context;
1235     const char *realmdn;
1236     char *rdn;
1237     LDAPDN dn;
1238 
1239     *name_out = NULL;
1240     SETUP_CONTEXT();
1241 
1242     realmdn = ldap_context->lrparams->realmdn;
1243     if (realmdn == NULL)
1244         return EINVAL;
1245 
1246     /* policyn_dn should be "cn=<policyname>,<realmdn>". */
1247     len1 = strlen(realmdn);
1248     len2 = strlen(policy_dn);
1249     if (len1 == 0 || len2 == 0 || len1 + 1 >= len2)
1250         return EINVAL;
1251     plen = len2 - len1 - 1;
1252     if (policy_dn[plen] != ',' || strcmp(realmdn, policy_dn + plen + 1) != 0)
1253         return EINVAL;
1254 
1255     rdn = k5memdup0(policy_dn, plen, &ret);
1256     if (rdn == NULL)
1257         return ret;
1258     ret = ldap_str2dn(rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC);
1259     free(rdn);
1260     if (ret)
1261         return EINVAL;
1262     if (dn[0] == NULL || dn[1] != NULL || dn[0][0]->la_attr.bv_len != 2 ||
1263         strncasecmp(dn[0][0]->la_attr.bv_val, "cn", 2) != 0) {
1264         ret = EINVAL;
1265     } else {
1266         *name_out = k5memdup0(dn[0][0]->la_value.bv_val,
1267                               dn[0][0]->la_value.bv_len, &ret);
1268     }
1269     ldap_dnfree(dn);
1270     return ret;
1271 }
1272 
1273 /* Compute the policy DN for the given policy name. */
1274 krb5_error_code
krb5_ldap_name_to_policydn(krb5_context context,char * name,char ** policy_dn)1275 krb5_ldap_name_to_policydn(krb5_context context, char *name, char **policy_dn)
1276 {
1277     int st;
1278     char *corrected;
1279     kdb5_dal_handle *dal_handle;
1280     krb5_ldap_context *ldap_context;
1281 
1282     *policy_dn = NULL;
1283 
1284     /* Used for removing policy reference from an object */
1285     if (name[0] == '\0') {
1286         *policy_dn = strdup("");
1287         return (*policy_dn == NULL) ? ENOMEM : 0;
1288     }
1289 
1290     SETUP_CONTEXT();
1291 
1292     if (ldap_context->lrparams->realmdn == NULL)
1293         return EINVAL;
1294 
1295     corrected = ldap_filter_correct(name);
1296     if (corrected == NULL)
1297         return ENOMEM;
1298 
1299     st = asprintf(policy_dn, "cn=%s,%s", corrected,
1300                   ldap_context->lrparams->realmdn);
1301     free(corrected);
1302     if (st == -1) {
1303         *policy_dn = NULL;
1304         return ENOMEM;
1305     }
1306     return 0;
1307 }
1308 
1309 /* Return true if dn1 is a subtree of dn2. */
1310 static inline krb5_boolean
is_subtree(const char * dn1,size_t len1,const char * dn2,size_t len2)1311 is_subtree(const char *dn1, size_t len1, const char *dn2, size_t len2)
1312 {
1313     return len1 > len2 && dn1[len1 - len2 - 1] == ',' &&
1314         strcasecmp(dn1 + (len1 - len2), dn2) == 0;
1315 }
1316 
1317 /* Remove overlapping and repeated subtree entries from the list of subtrees.
1318  * If sscope is not 2 (sub), only remove repeated entries. */
1319 static void
remove_overlapping_subtrees(char ** list,int * subtcount,int sscope)1320 remove_overlapping_subtrees(char **list, int *subtcount, int sscope)
1321 {
1322     size_t ilen, jlen;
1323     int i, j;
1324     int 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     int 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     int val, i, pcount, objtype;
1418     krb5_boolean attr_present;
1419     krb5_kvno mkvno = 0;
1420     krb5_timestamp lastpwdchange, unlock_time;
1421     char *policydn = NULL, *pwdpolicydn = NULL, *polname = NULL, *user = NULL;
1422     char *tktpolname = NULL, *dn = NULL, **link_references = NULL;
1423     char **pnvalues = NULL, **ocvalues = NULL, **a2d2 = NULL;
1424     struct berval **ber_key_data = NULL, **ber_tl_data = NULL;
1425     krb5_tl_data userinfo_tl_data = { NULL }, **endp, *tl;
1426     osa_princ_ent_rec princ_ent;
1427     char *is_login_disabled = NULL;
1428 
1429     memset(&princ_ent, 0, sizeof(princ_ent));
1430 
1431     ret = krb5_copy_principal(context, princ, &entry->princ);
1432     if (ret)
1433         goto cleanup;
1434 
1435     /* get the associated directory user information */
1436     pnvalues = ldap_get_values(ld, ent, "krbprincipalname");
1437     if (pnvalues != NULL) {
1438         ret = krb5_unparse_name(context, princ, &user);
1439         if (ret)
1440             goto cleanup;
1441 
1442         pcount = 0;
1443         for (i = 0; pnvalues[i] != NULL; i++) {
1444             if (strcasecmp(pnvalues[i], user) == 0) {
1445                 pcount = ldap_count_values(pnvalues);
1446                 break;
1447             }
1448         }
1449 
1450         dn = ldap_get_dn(ld, ent);
1451         if (dn == NULL) {
1452             ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &ret);
1453             ret = set_ldap_error(context, ret, 0);
1454             goto cleanup;
1455         }
1456 
1457         ocvalues = ldap_get_values(ld, ent, "objectclass");
1458         if (ocvalues != NULL) {
1459             for (i = 0; ocvalues[i] != NULL; i++) {
1460                 if (strcasecmp(ocvalues[i], "krbprincipal") == 0) {
1461                     objtype = KDB_STANDALONE_PRINCIPAL_OBJECT;
1462                     ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE,
1463                                         &objtype);
1464                     if (ret)
1465                         goto cleanup;
1466                     break;
1467                 }
1468             }
1469         }
1470 
1471         /* Add principalcount, DN and principaltype user information to
1472          * tl_data */
1473         ret = store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount);
1474         if (ret)
1475             goto cleanup;
1476         ret = store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, dn);
1477         if (ret)
1478             goto cleanup;
1479     }
1480 
1481     ret = get_time(ld, ent, "krbLastSuccessfulAuth", &entry->last_success,
1482                    &attr_present);
1483     if (ret)
1484         goto cleanup;
1485     if (attr_present)
1486         mask |= KDB_LAST_SUCCESS_ATTR;
1487 
1488     ret = get_time(ld, ent, "krbLastFailedAuth", &entry->last_failed,
1489                    &attr_present);
1490     if (ret)
1491         goto cleanup;
1492     if (attr_present)
1493         mask |= KDB_LAST_FAILED_ATTR;
1494 
1495     if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount", &val) == 0) {
1496         entry->fail_auth_count = val;
1497         mask |= KDB_FAIL_AUTH_COUNT_ATTR;
1498     }
1499     if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &val) == 0) {
1500         entry->max_life = val;
1501         mask |= KDB_MAX_LIFE_ATTR;
1502     }
1503     if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage", &val) == 0) {
1504         entry->max_renewable_life = val;
1505         mask |= KDB_MAX_RLIFE_ATTR;
1506     }
1507     if (krb5_ldap_get_value(ld, ent, "krbticketflags", &val) == 0) {
1508         entry->attributes = val;
1509         mask |= KDB_TKT_FLAGS_ATTR;
1510     }
1511     ret = get_time(ld, ent, "krbprincipalexpiration", &entry->expiration,
1512                    &attr_present);
1513     if (ret)
1514         goto cleanup;
1515     if (attr_present)
1516         mask |= KDB_PRINC_EXPIRE_TIME_ATTR;
1517 
1518     ret = get_time(ld, ent, "krbpasswordexpiration", &entry->pw_expiration,
1519                    &attr_present);
1520     if (ret)
1521         goto cleanup;
1522     if (attr_present)
1523         mask |= KDB_PWD_EXPIRE_TIME_ATTR;
1524 
1525     ret = krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn,
1526                                &attr_present);
1527     if (ret)
1528         goto cleanup;
1529     if (attr_present) {
1530         mask |= KDB_POL_REF_ATTR;
1531         /* Ensure that the policy is inside the realm container. */
1532         ret = krb5_ldap_policydn_to_name(context, policydn, &tktpolname);
1533         if (ret)
1534             goto cleanup;
1535     }
1536 
1537     ret = krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn,
1538                                &attr_present);
1539     if (ret)
1540         goto cleanup;
1541     if (attr_present) {
1542         mask |= KDB_PWD_POL_REF_ATTR;
1543 
1544         /* Ensure that the policy is inside the realm container. */
1545         ret = krb5_ldap_policydn_to_name(context, pwdpolicydn, &polname);
1546         if (ret)
1547             goto cleanup;
1548         princ_ent.policy = polname;
1549         princ_ent.aux_attributes |= KADM5_POLICY;
1550     }
1551 
1552     ber_key_data = ldap_get_values_len(ld, ent, "krbpwdhistory");
1553     if (ber_key_data != NULL) {
1554         mask |= KDB_PWD_HISTORY_ATTR;
1555         ret = krb5_decode_histkey(context, ber_key_data, &princ_ent);
1556         if (ret)
1557             goto cleanup;
1558         ldap_value_free_len(ber_key_data);
1559     }
1560 
1561     if (princ_ent.aux_attributes) {
1562         ret = krb5_update_tl_kadm_data(context, entry, &princ_ent);
1563         if (ret)
1564             goto cleanup;
1565     }
1566 
1567     ber_key_data = ldap_get_values_len(ld, ent, "krbprincipalkey");
1568     if (ber_key_data != NULL) {
1569         mask |= KDB_SECRET_KEY_ATTR;
1570         ret = krb5_decode_krbsecretkey(context, entry, ber_key_data, &mkvno);
1571         if (ret)
1572             goto cleanup;
1573         if (mkvno != 0) {
1574             ret = krb5_dbe_update_mkvno(context, entry, mkvno);
1575             if (ret)
1576                 goto cleanup;
1577         }
1578     }
1579 
1580     ret = get_time(ld, ent, "krbLastPwdChange", &lastpwdchange, &attr_present);
1581     if (ret)
1582         goto cleanup;
1583     if (attr_present) {
1584         ret = krb5_dbe_update_last_pwd_change(context, entry, lastpwdchange);
1585         if (ret)
1586             goto cleanup;
1587         mask |= KDB_LAST_PWD_CHANGE_ATTR;
1588     }
1589 
1590     ret = get_time(ld, ent, "krbLastAdminUnlock", &unlock_time, &attr_present);
1591     if (ret)
1592         goto cleanup;
1593     if (attr_present) {
1594         ret = krb5_dbe_update_last_admin_unlock(context, entry, unlock_time);
1595         if (ret)
1596             goto cleanup;
1597         mask |= KDB_LAST_ADMIN_UNLOCK_ATTR;
1598     }
1599 
1600     a2d2 = ldap_get_values(ld, ent, "krbAllowedToDelegateTo");
1601     if (a2d2 != NULL) {
1602         for (endp = &entry->tl_data; *endp; endp = &(*endp)->tl_data_next);
1603         for (i = 0; a2d2[i] != NULL; i++) {
1604             tl = k5alloc(sizeof(*tl), &ret);
1605             if (tl == NULL)
1606                 goto cleanup;
1607             tl->tl_data_type = KRB5_TL_CONSTRAINED_DELEGATION_ACL;
1608             tl->tl_data_length = strlen(a2d2[i]);
1609             tl->tl_data_contents = (unsigned char *)strdup(a2d2[i]);
1610             if (tl->tl_data_contents == NULL) {
1611                 ret = ENOMEM;
1612                 free(tl);
1613                 goto cleanup;
1614             }
1615             tl->tl_data_next = NULL;
1616             *endp = tl;
1617             endp = &tl->tl_data_next;
1618         }
1619     }
1620 
1621     link_references = ldap_get_values(ld, ent, "krbobjectreferences");
1622     if (link_references != NULL) {
1623         for (i = 0; link_references[i] != NULL; i++) {
1624             ret = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN,
1625                                 link_references[i]);
1626             if (ret)
1627                 goto cleanup;
1628         }
1629     }
1630 
1631     ber_tl_data = ldap_get_values_len(ld, ent, "krbExtraData");
1632     if (ber_tl_data != NULL) {
1633         for (i = 0; ber_tl_data[i] != NULL; i++) {
1634             ret = berval2tl_data(ber_tl_data[i], &tl);
1635             if (ret)
1636                 goto cleanup;
1637             ret = krb5_dbe_update_tl_data(context, entry, tl);
1638             free(tl->tl_data_contents);
1639             free(tl);
1640             if (ret)
1641                 goto cleanup;
1642         }
1643         mask |= KDB_EXTRA_DATA_ATTR;
1644     }
1645 
1646     /* Auth indicators from krbPrincipalAuthInd will replace those from
1647      * krbExtraData. */
1648     ret = get_ldap_auth_ind(context, ld, ent, entry, &mask);
1649     if (ret)
1650         goto cleanup;
1651 
1652     /* Update the mask of attributes present on the directory object to the
1653      * tl_data. */
1654     ret = store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask);
1655     if (ret)
1656         goto cleanup;
1657     ret = krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data);
1658     if (ret)
1659         goto cleanup;
1660 
1661     /*
1662      * 389ds and other Netscape directory server derivatives support an
1663      * attribute "nsAccountLock" which functions similarly to eDirectory's
1664      * "loginDisabled".  When the user's account object is also a
1665      * krbPrincipalAux object, the kdb entry should be treated as if
1666      * DISALLOW_ALL_TIX has been set.
1667      */
1668     ret = krb5_ldap_get_string(ld, ent, "nsAccountLock", &is_login_disabled,
1669                                &attr_present);
1670     if (ret)
1671         goto cleanup;
1672     if (attr_present == TRUE) {
1673         if (strcasecmp(is_login_disabled, "TRUE") == 0)
1674             entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
1675         free(is_login_disabled);
1676     }
1677 
1678     ret = krb5_read_tkt_policy(context, ldap_context, entry, tktpolname);
1679     if (ret)
1680         goto cleanup;
1681 
1682     /* For compatibility with DB2 principals. */
1683     entry->len = KRB5_KDB_V1_BASE_LENGTH;
1684 
1685 cleanup:
1686     ldap_memfree(dn);
1687     ldap_value_free_len(ber_key_data);
1688     ldap_value_free_len(ber_tl_data);
1689     ldap_value_free(pnvalues);
1690     ldap_value_free(ocvalues);
1691     ldap_value_free(link_references);
1692     ldap_value_free(a2d2);
1693     free(userinfo_tl_data.tl_data_contents);
1694     free(pwdpolicydn);
1695     free(polname);
1696     free(tktpolname);
1697     free(policydn);
1698     krb5_free_unparsed_name(context, user);
1699     free_princ_ent_contents(&princ_ent);
1700     return ret;
1701 }
1702