Add lockout-related performance tuning variables
[krb5.git] / src / plugins / kdb / ldap / libkdb_ldap / ldap_misc.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * lib/kdb/kdb_ldap/ldap_misc.c
4  *
5  * Copyright (c) 2004-2005, Novell, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  *   * Redistributions of source code must retain the above copyright notice,
12  *       this list of conditions and the following disclaimer.
13  *   * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in the
15  *       documentation and/or other materials provided with the distribution.
16  *   * The copyright holder's name is not used to endorse or promote products
17  *       derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 /*
32  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
33  * Use is subject to license terms.
34  */
35 #include <string.h>
36 #include <time.h>
37 #include "kdb_ldap.h"
38 #include "ldap_misc.h"
39 #include "ldap_handle.h"
40 #include "ldap_err.h"
41 #include "ldap_principal.h"
42 #include "princ_xdr.h"
43 #include "ldap_pwd_policy.h"
44
45 #ifdef NEED_STRPTIME_PROTO
46 extern char *strptime (const char *, const char *, struct tm *);
47 #endif
48
49 static krb5_error_code
50 remove_overlapping_subtrees(char **listin, char **listop, int *subtcount,
51                             int sscope);
52
53 /* Linux (GNU Libc) provides a length-limited variant of strdup.
54    But all the world's not Linux.  */
55 #undef strndup
56 #define strndup my_strndup
57 #ifdef HAVE_LDAP_STR2DN
58 static char *
59 my_strndup(const char *input, size_t limit)
60 {
61     size_t len = strlen(input);
62     char *result;
63     if (len > limit) {
64         result = malloc(1 + limit);
65         if (result != NULL) {
66             memcpy(result, input, limit);
67             result[limit] = 0;
68         }
69         return result;
70     } else
71         return strdup(input);
72 }
73 #endif
74
75 /* Get integer or string values from the config section, falling back
76    to the default section, then to hard-coded values.  */
77 static errcode_t
78 prof_get_integer_def(krb5_context ctx, const char *conf_section,
79                      const char *name, int dfl, krb5_ui_4 *out)
80 {
81     errcode_t err;
82     int out_temp = 0;
83
84     err = profile_get_integer (ctx->profile,
85                                KDB_MODULE_SECTION, conf_section, name,
86                                0, &out_temp);
87     if (err) {
88         krb5_set_error_message (ctx, err, "Error reading '%s' attribute: %s",
89                                 name, error_message(err));
90         return err;
91     }
92     if (out_temp != 0) {
93         *out = out_temp;
94         return 0;
95     }
96     err = profile_get_integer (ctx->profile,
97                                KDB_MODULE_DEF_SECTION, name, 0,
98                                dfl, &out_temp);
99     if (err) {
100         krb5_set_error_message (ctx, err, "Error reading '%s' attribute: %s",
101                                 name, error_message(err));
102         return err;
103     }
104     *out = out_temp;
105     return 0;
106 }
107
108 /* Get integer or string values from the config section, falling back
109    to the default section, then to hard-coded values.  */
110 static errcode_t
111 prof_get_boolean_def(krb5_context ctx, const char *conf_section,
112                      const char *name, krb5_boolean dfl, krb5_boolean *out)
113 {
114     errcode_t err;
115     int out_temp = 0;
116
117     err = profile_get_boolean(ctx->profile, KDB_MODULE_SECTION, conf_section,
118                               name, -1, &out_temp);
119     if (err) {
120         krb5_set_error_message(ctx, err, "Error reading '%s' attribute: %s",
121                                name, error_message(err));
122         return err;
123     }
124     if (out_temp != -1) {
125         *out = out_temp;
126         return 0;
127     }
128     err = profile_get_boolean(ctx->profile, KDB_MODULE_DEF_SECTION, name, 0,
129                               dfl, &out_temp);
130     if (err) {
131         krb5_set_error_message(ctx, err, "Error reading '%s' attribute: %s",
132                                name, error_message(err));
133         return err;
134     }
135     *out = out_temp;
136     return 0;
137 }
138
139 /* We don't have non-null defaults in any of our calls, so don't
140    bother with the extra argument.  */
141 static errcode_t
142 prof_get_string_def(krb5_context ctx, const char *conf_section,
143                     const char *name, char **out)
144 {
145     errcode_t err;
146
147     err = profile_get_string (ctx->profile,
148                               KDB_MODULE_SECTION, conf_section, name,
149                               0, out);
150     if (err) {
151         krb5_set_error_message (ctx, err, "Error reading '%s' attribute: %s",
152                                 name, error_message(err));
153         return err;
154     }
155     if (*out != 0)
156         return 0;
157     err = profile_get_string (ctx->profile,
158                               KDB_MODULE_DEF_SECTION, name, 0,
159                               0, out);
160     if (err) {
161         krb5_set_error_message (ctx, err, "Error reading '%s' attribute: %s",
162                                 name, error_message(err));
163         return err;
164     }
165     return 0;
166 }
167
168
169
170 /*
171  * This function reads the parameters from the krb5.conf file. The
172  * parameters read here are DAL-LDAP specific attributes. Some of
173  * these are ldap_server ....
174  */
175 krb5_error_code
176 krb5_ldap_read_server_params(krb5_context context, char *conf_section,
177                              int srv_type)
178 {
179     char                        *tempval=NULL, *save_ptr=NULL;
180     const char                  *delims="\t\n\f\v\r ,";
181     krb5_error_code             st=0;
182     kdb5_dal_handle             *dal_handle=NULL;
183     krb5_ldap_context           *ldap_context=NULL;
184     krb5_ldap_server_info       ***server_info=NULL;
185
186     dal_handle = context->dal_handle;
187     ldap_context = (krb5_ldap_context *) dal_handle->db_context;
188
189     /* copy the conf_section into ldap_context for later use */
190     if (conf_section) {
191         ldap_context->conf_section = strdup (conf_section);
192         if (ldap_context->conf_section == NULL) {
193             st = ENOMEM;
194             goto cleanup;
195         }
196     }
197
198     /* initialize the mutexs and condition variable */
199     /* this portion logically doesn't fit here should be moved appropriately */
200
201     /* this mutex is used in ldap reconnection pool */
202     if (k5_mutex_init(&(ldap_context->hndl_lock)) != 0) {
203         st = KRB5_KDB_SERVER_INTERNAL_ERR;
204 #if 0
205         st = -1;
206         krb5_ldap_dal_err_funcp(context, krb5_err_have_str, st,
207                                 "k5_mutex_init failed");
208 #endif
209         goto cleanup;
210     }
211
212     /*
213      * If max_server_conns is not set read it from database module
214      * section of conf file this parameter defines maximum ldap
215      * connections per ldap server.
216      */
217     if (ldap_context->max_server_conns == 0) {
218         st = prof_get_integer_def (context, conf_section,
219                                    KRB5_CONF_LDAP_CONNS_PER_SERVER,
220                                    DEFAULT_CONNS_PER_SERVER,
221                                    &ldap_context->max_server_conns);
222         if (st)
223             goto cleanup;
224     }
225
226     if (ldap_context->max_server_conns < 2) {
227         st = EINVAL;
228         krb5_set_error_message (context, st,
229                                 "Minimum connections required per server is 2");
230         goto cleanup;
231     }
232
233     /*
234      * If the bind dn is not set read it from the database module
235      * section of conf file this paramter is populated by one of the
236      * KDC, ADMIN or PASSWD dn to be used to connect to LDAP
237      * server.  The srv_type decides which dn to read.
238      */
239     if (ldap_context->bind_dn == NULL) {
240         char *name = 0;
241         if (srv_type == KRB5_KDB_SRV_TYPE_KDC)
242             name = KRB5_CONF_LDAP_KDC_DN;
243         else if (srv_type == KRB5_KDB_SRV_TYPE_ADMIN)
244             name = KRB5_CONF_LDAP_KADMIN_DN;
245         else if (srv_type == KRB5_KDB_SRV_TYPE_PASSWD)
246             name = "ldap_kpasswdd_dn";
247
248         if (name) {
249             st = prof_get_string_def (context, conf_section, name,
250                                       &ldap_context->bind_dn);
251             if (st)
252                 goto cleanup;
253         }
254     }
255
256     /*
257      * Read service_password_file parameter from database module
258      * section of conf file this file contains stashed passwords of
259      * the KDC, ADMIN and PASSWD dns.
260      */
261     if (ldap_context->service_password_file == NULL) {
262         st = prof_get_string_def (context, conf_section,
263                                   KRB5_CONF_LDAP_SERVICE_PASSWORD_FILE,
264                                   &ldap_context->service_password_file);
265         if (st)
266             goto cleanup;
267     }
268
269 #ifdef HAVE_EDIRECTORY
270     /*
271      * If root certificate file is not set read it from database
272      * module section of conf file this is the trusted root
273      * certificate of the Directory.
274      */
275     if (ldap_context->root_certificate_file == NULL) {
276         st = prof_get_string_def (context, conf_section,
277                                   KRB5_CONF_LDAP_ROOT_CERTIFICATE_FILE,
278                                   &ldap_context->root_certificate_file);
279         if (st)
280             goto cleanup;
281     }
282 #endif
283
284     /*
285      * If the ldap server parameter is not set read the list of ldap
286      * servers from the database module section of the conf file.
287      */
288
289     if (ldap_context->server_info_list == NULL) {
290         unsigned int ele=0;
291
292         server_info = &(ldap_context->server_info_list);
293         *server_info = (krb5_ldap_server_info **) calloc (SERV_COUNT+1,
294                                                           sizeof (krb5_ldap_server_info *));
295
296         if (*server_info == NULL) {
297             st = ENOMEM;
298             goto cleanup;
299         }
300
301         if ((st=profile_get_string(context->profile, KDB_MODULE_SECTION, conf_section,
302                                    KRB5_CONF_LDAP_SERVERS, NULL, &tempval)) != 0) {
303             krb5_set_error_message (context, st, "Error reading 'ldap_servers' attribute");
304             goto cleanup;
305         }
306
307         if (tempval == NULL) {
308
309             (*server_info)[ele] = (krb5_ldap_server_info *)calloc(1,
310                                                                   sizeof(krb5_ldap_server_info));
311
312             (*server_info)[ele]->server_name = strdup("ldapi://");
313             if ((*server_info)[ele]->server_name == NULL) {
314                 st = ENOMEM;
315                 goto cleanup;
316             }
317             (*server_info)[ele]->server_status = NOTSET;
318         } else {
319             char *item=NULL;
320
321             item = strtok_r(tempval,delims,&save_ptr);
322             while (item != NULL && ele<SERV_COUNT) {
323                 (*server_info)[ele] = (krb5_ldap_server_info *)calloc(1,
324                                                                       sizeof(krb5_ldap_server_info));
325                 if ((*server_info)[ele] == NULL) {
326                     st = ENOMEM;
327                     goto cleanup;
328                 }
329                 (*server_info)[ele]->server_name = strdup(item);
330                 if ((*server_info)[ele]->server_name == NULL) {
331                     st = ENOMEM;
332                     goto cleanup;
333                 }
334
335                 (*server_info)[ele]->server_status = NOTSET;
336                 item = strtok_r(NULL,delims,&save_ptr);
337                 ++ele;
338             }
339             profile_release_string(tempval);
340         }
341     }
342
343     if ((st = prof_get_boolean_def(context, conf_section,
344                                    KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE,
345                                    &ldap_context->disable_last_success)))
346         goto cleanup;
347
348     if ((st = prof_get_boolean_def(context, conf_section,
349                                    KRB5_CONF_DISABLE_LOCKOUT, FALSE,
350                                    &ldap_context->disable_lockout)))
351         goto cleanup;
352
353 cleanup:
354     return(st);
355 }
356
357 /*
358  * This function frees the krb5_ldap_context structure members.
359  */
360
361 krb5_error_code
362 krb5_ldap_free_server_context_params(krb5_ldap_context *ldap_context)
363 {
364     int                         i=0;
365     krb5_ldap_server_handle     *ldap_server_handle=NULL, *next_ldap_server_handle=NULL;
366
367     if (ldap_context == NULL)
368         return 0;
369
370     /* Free all ldap servers list and the ldap handles associated with
371        the ldap server.  */
372     if (ldap_context->server_info_list) {
373         while (ldap_context->server_info_list[i]) {
374             if (ldap_context->server_info_list[i]->server_name) {
375                 free (ldap_context->server_info_list[i]->server_name);
376             }
377 #ifdef HAVE_EDIRECTORY
378             if (ldap_context->server_info_list[i]->root_certificate_file) {
379                 free (ldap_context->server_info_list[i]->root_certificate_file);
380             }
381 #endif
382             if (ldap_context->server_info_list[i]->ldap_server_handles) {
383                 ldap_server_handle = ldap_context->server_info_list[i]->ldap_server_handles;
384                 while (ldap_server_handle) {
385                     ldap_unbind_ext_s(ldap_server_handle->ldap_handle, NULL, NULL);
386                     ldap_server_handle->ldap_handle = NULL;
387                     next_ldap_server_handle = ldap_server_handle->next;
388                     krb5_xfree(ldap_server_handle);
389                     ldap_server_handle = next_ldap_server_handle;
390                 }
391             }
392             krb5_xfree(ldap_context->server_info_list[i]);
393             i++;
394         }
395         krb5_xfree(ldap_context->server_info_list);
396     }
397
398     if (ldap_context->conf_section != NULL) {
399         krb5_xfree(ldap_context->conf_section);
400         ldap_context->conf_section = NULL;
401     }
402
403     if (ldap_context->bind_dn != NULL) {
404         krb5_xfree(ldap_context->bind_dn);
405         ldap_context->bind_dn = NULL;
406     }
407
408     if (ldap_context->bind_pwd != NULL) {
409         memset(ldap_context->bind_pwd, 0, strlen(ldap_context->bind_pwd));
410         krb5_xfree(ldap_context->bind_pwd);
411         ldap_context->bind_pwd = NULL;
412     }
413
414     if (ldap_context->service_password_file != NULL) {
415         krb5_xfree(ldap_context->service_password_file);
416         ldap_context->service_password_file = NULL;
417     }
418
419 #ifdef HAVE_EDIRECTORY
420     if (ldap_context->root_certificate_file != NULL) {
421         krb5_xfree(ldap_context->root_certificate_file);
422         ldap_context->root_certificate_file = NULL;
423     }
424 #endif
425
426     if (ldap_context->service_cert_path != NULL) {
427         krb5_xfree(ldap_context->service_cert_path);
428         ldap_context->service_cert_path = NULL;
429     }
430
431     if (ldap_context->service_cert_pass != NULL) {
432         krb5_xfree(ldap_context->service_cert_pass);
433         ldap_context->service_cert_pass = NULL;
434     }
435
436     if (ldap_context->certificates) {
437         i=0;
438         while (ldap_context->certificates[i] != NULL) {
439             krb5_xfree(ldap_context->certificates[i]->certificate);
440             krb5_xfree(ldap_context->certificates[i]);
441             ++i;
442         }
443         krb5_xfree(ldap_context->certificates);
444     }
445
446     return(0);
447 }
448
449 krb5_error_code
450 krb5_ldap_free_server_params(krb5_ldap_context *ldap_context)
451 {
452     if (ldap_context == NULL)
453         return 0;
454
455     krb5_ldap_free_server_context_params(ldap_context);
456
457     k5_mutex_destroy(&ldap_context->hndl_lock);
458     krb5_xfree(ldap_context);
459     return(0);
460 }
461
462 /*
463  * check to see if the principal belongs to the default realm.
464  * The default realm is present in the krb5_ldap_context structure.
465  * The principal has a realm portion. This realm portion is compared with the default realm
466  * to check whether the principal belong to the default realm.
467  * Return 0 if principal belongs to default realm else 1.
468  */
469
470 krb5_error_code
471 is_principal_in_realm(krb5_ldap_context *ldap_context,
472                       krb5_const_principal searchfor)
473 {
474     size_t                      defrealmlen=0;
475     char                        *defrealm=NULL;
476
477 #define FIND_MAX(a,b) ((a) > (b) ? (a) : (b))
478
479     defrealmlen = strlen(ldap_context->lrparams->realm_name);
480     defrealm = ldap_context->lrparams->realm_name;
481
482     /*
483      * Care should be taken for inter-realm principals as the default
484      * realm can exist in the realm part of the principal name or can
485      * also exist in the second portion of the name part.  However, if
486      * the default realm exist in the second part of the principal
487      * portion, then the first portion of the principal name SHOULD be
488      * "krbtgt".  All this check is done in the immediate block.
489      */
490     if (searchfor->length == 2)
491         if ((strncasecmp(searchfor->data[0].data, "krbtgt",
492                          FIND_MAX(searchfor->data[0].length, strlen("krbtgt"))) == 0) &&
493             (strncasecmp(searchfor->data[1].data, defrealm,
494                          FIND_MAX(searchfor->data[1].length, defrealmlen)) == 0))
495             return 0;
496
497     /* first check the length, if they are not equal, then they are not same */
498     if (strlen(defrealm) != searchfor->realm.length)
499         return 1;
500
501     /* if the length is equal, check for the contents */
502     if (strncmp(defrealm, searchfor->realm.data,
503                 searchfor->realm.length) != 0)
504         return 1;
505     /* if we are here, then the realm portions match, return 0 */
506     return 0;
507 }
508
509
510 /*
511  * Deduce the subtree information from the context. A realm can have
512  * multiple subtrees.
513  * 1. the Realm container
514  * 2. the actual subtrees associated with the Realm
515  *
516  * However, there are some conditions to be considered to deduce the
517  * actual subtree/s associated with the realm.  The conditions are as
518  * follows:
519  * 1. If the subtree information of the Realm is [Root] or NULL (that
520  *    is internal a [Root]) then the realm has only one subtree
521  *    i.e [Root], i.e. whole of the tree.
522  * 2. If the subtree information of the Realm is missing/absent, then the
523  *    realm has only one, i.e., the Realm container.  NOTE: In all cases
524  *    Realm container SHOULD be the one among the subtrees or the only
525  *    one subtree.
526  * 3. The subtree information of the realm is overlapping the realm
527  *    container of the realm, then the realm has only one subtree and
528  *    it is the subtree information associated with the realm.
529  */
530 krb5_error_code
531 krb5_get_subtree_info(krb5_ldap_context *ldap_context, char ***subtreearr,
532                       unsigned int *ntree)
533 {
534     int                         st=0, i=0, subtreecount=0;
535     int                         ncount=0, search_scope=0;
536     char                        **subtree=NULL, *realm_cont_dn=NULL;
537     char                        **subtarr=NULL;
538     char                        *containerref=NULL;
539     char                        **newsubtree=NULL;
540
541     containerref = ldap_context->lrparams->containerref;
542     subtree = ldap_context->lrparams->subtree;
543     realm_cont_dn = ldap_context->lrparams->realmdn;
544     subtreecount = ldap_context->lrparams->subtreecount;
545     search_scope = ldap_context->lrparams->search_scope;
546
547     subtarr = (char **) malloc(sizeof(char *) * (subtreecount + 1 /*realm dn*/ + 1 /*containerref*/ + 1));
548     if (subtarr == NULL) {
549         st = ENOMEM;
550         goto cleanup;
551     }
552     memset(subtarr, 0, (sizeof(char *) * (subtreecount+1+1+1)));
553
554     /* get the complete subtree list */
555     for (i=0; i<subtreecount && subtree[i]!=NULL; i++) {
556         subtarr[i] = strdup(subtree[i]);
557         if (subtarr[i] == NULL) {
558             st = ENOMEM;
559             goto cleanup;
560         }
561     }
562
563     subtarr[i] = strdup(realm_cont_dn);
564     if (subtarr[i++] == NULL) {
565         st = ENOMEM;
566         goto cleanup;
567     }
568
569     if (containerref != NULL) {
570         subtarr[i] = strdup(containerref);
571         if (subtarr[i++] == NULL) {
572             st = ENOMEM;
573             goto cleanup;
574         }
575     }
576
577     ncount = i;
578     newsubtree = (char **) malloc(sizeof(char *) * (ncount + 1));
579     if (newsubtree == NULL) {
580         st = ENOMEM;
581         goto cleanup;
582     }
583     memset(newsubtree, 0, (sizeof(char *) * (ncount+1)));
584     if ((st = remove_overlapping_subtrees(subtarr, newsubtree, &ncount,
585                                           search_scope)) != 0) {
586         goto cleanup;
587     }
588
589     *ntree = ncount;
590     *subtreearr = newsubtree;
591
592 cleanup:
593     if (subtarr != NULL) {
594         for (i=0; subtarr[i] != NULL; i++)
595             free(subtarr[i]);
596         free(subtarr);
597     }
598
599     if (st != 0) {
600         if (newsubtree != NULL) {
601             for (i=0; newsubtree[i] != NULL; i++)
602                 free(newsubtree[i]);
603             free(newsubtree);
604         }
605     }
606     return st;
607 }
608
609 /*
610  * This function appends the content with a type into the tl_data
611  * structure.  Based on the type the length of the content is either
612  * pre-defined or computed from the content.  Returns 0 in case of
613  * success and 1 if the type associated with the content is undefined.
614  */
615
616 krb5_error_code
617 store_tl_data(krb5_tl_data *tl_data, int tl_type, void *value)
618 {
619     unsigned int                currlen=0, tldatalen=0;
620     unsigned char               *curr=NULL;
621     void                        *reallocptr=NULL;
622
623     tl_data->tl_data_type = KDB_TL_USER_INFO;
624     switch (tl_type) {
625     case KDB_TL_PRINCCOUNT:
626     case KDB_TL_PRINCTYPE:
627     case KDB_TL_MASK:
628     {
629         int *iptr = (int *)value;
630         int ivalue = *iptr;
631
632         currlen = tl_data->tl_data_length;
633         tl_data->tl_data_length += 1 + 2 + 2;
634         /* allocate required memory */
635         reallocptr = tl_data->tl_data_contents;
636         tl_data->tl_data_contents = realloc(tl_data->tl_data_contents,
637                                             tl_data->tl_data_length);
638         if (tl_data->tl_data_contents == NULL) {
639             if (reallocptr)
640                 free (reallocptr);
641             return ENOMEM;
642         }
643         curr = (tl_data->tl_data_contents + currlen);
644
645         /* store the tl_type value */
646         memset(curr, tl_type, 1);
647         curr += 1;
648         /* store the content length */
649         tldatalen = 2;
650         STORE16_INT(curr, tldatalen);
651         curr += 2;
652         /* store the content */
653         STORE16_INT(curr, ivalue);
654         curr += 2;
655         break;
656     }
657
658     case KDB_TL_USERDN:
659     case KDB_TL_LINKDN:
660     {
661         char *cptr = (char *)value;
662
663         currlen = tl_data->tl_data_length;
664         tl_data->tl_data_length += 1 + 2 + strlen(cptr);
665         /* allocate required memory */
666         reallocptr = tl_data->tl_data_contents;
667         tl_data->tl_data_contents = realloc(tl_data->tl_data_contents,
668                                             tl_data->tl_data_length);
669         if (tl_data->tl_data_contents == NULL) {
670             if (reallocptr)
671                 free (reallocptr);
672             return ENOMEM;
673         }
674         curr = (tl_data->tl_data_contents + currlen);
675
676         /* store the tl_type value */
677         memset(curr, tl_type, 1);
678         curr += 1;
679         /* store the content length */
680         tldatalen = strlen(cptr);
681         STORE16_INT(curr, tldatalen);
682         curr += 2;
683         /* store the content */
684         memcpy(curr, cptr, tldatalen);
685         curr += tldatalen;
686         break;
687     }
688
689     default:
690         return 1;
691
692     }
693     return 0;
694 }
695
696 /*
697  * This function scans the tl_data structure to get the value of a
698  * type defined by the tl_type (second parameter).  The tl_data
699  * structure has all the data in the tl_data_contents member.  The
700  * format of the tl_data_contents is as follows.  The first byte
701  * defines the type of the content that follows.  The next 2 bytes
702  * define the size n (in terms of bytes) of the content that
703  * follows.  The next n bytes define the content itself.
704  */
705
706 krb5_error_code
707 decode_tl_data(krb5_tl_data *tl_data, int tl_type, void **data)
708 {
709     int                         subtype=0, i=0, limit=10;
710     unsigned int                sublen=0;
711     unsigned char               *curr=NULL;
712     int                         *intptr=NULL;
713     long                        *longptr=NULL;
714     char                        *DN=NULL, **DNarr=NULL;
715     krb5_error_code             st=-1;
716
717     *data = NULL;
718
719     curr = tl_data->tl_data_contents;
720     while (curr < (tl_data->tl_data_contents + tl_data->tl_data_length)) {
721
722         /* get the type of the content */
723         subtype = (int) curr[0];
724         /* forward by 1 byte*/
725         curr += 1;
726
727         if (subtype == tl_type) {
728             switch (subtype) {
729
730             case KDB_TL_PRINCCOUNT:
731             case KDB_TL_PRINCTYPE:
732             case KDB_TL_MASK:
733                 /* get the length of the content */
734                 UNSTORE16_INT(curr, sublen);
735                 /* forward by 2 bytes */
736                 curr += 2;
737                 /* get the actual content */
738                 if (sublen == 2) {
739                     /* intptr = malloc(sublen);   */
740                     intptr = malloc(sizeof(krb5_int32));
741                     if (intptr == NULL)
742                         return ENOMEM;
743                     memset(intptr, 0, sublen);
744                     UNSTORE16_INT(curr, (*intptr));
745                     *data = intptr;
746                 } else {
747                     longptr = malloc(sublen);
748                     if (longptr == NULL)
749                         return ENOMEM;
750                     memset(longptr, 0, sublen);
751                     UNSTORE32_INT(curr, (*longptr));
752                     *data = longptr;
753                 }
754                 curr += sublen;
755                 st = 0;
756                 return st;
757                 break;
758
759             case KDB_TL_CONTAINERDN:
760             case KDB_TL_USERDN:
761                 /* get the length of the content */
762                 UNSTORE16_INT(curr, sublen);
763                 /* forward by 2 bytes */
764                 curr += 2;
765                 DN = malloc (sublen + 1);
766                 if (DN == NULL)
767                     return ENOMEM;
768                 memcpy(DN, curr, sublen);
769                 DN[sublen] = 0;
770                 *data = DN;
771                 curr += sublen;
772                 st = 0;
773                 return st;
774                 break;
775
776             case KDB_TL_LINKDN:
777                 if (DNarr == NULL) {
778                     DNarr = calloc(limit, sizeof(char *));
779                     if (DNarr == NULL)
780                         return ENOMEM;
781                 }
782                 if (i == limit-1) {
783                     limit *= 2;
784                     DNarr = realloc(DNarr, sizeof(char *) * (limit));
785                     if (DNarr == NULL)
786                         return ENOMEM;
787                 }
788
789                 /* get the length of the content */
790                 UNSTORE16_INT(curr, sublen);
791                 /* forward by 2 bytes */
792                 curr += 2;
793                 DNarr[i] = malloc (sublen + 1);
794                 if (DNarr[i] == NULL)
795                     return ENOMEM;
796                 memcpy(DNarr[i], curr, sublen);
797                 DNarr[i][sublen] = 0;
798                 ++i;
799                 curr += sublen;
800                 *data = DNarr;
801                 st=0;
802                 break;
803             }
804         } else {
805             /* move to the current content block */
806             UNSTORE16_INT(curr, sublen);
807             curr += 2 + sublen;
808         }
809     }
810     return st;
811 }
812
813 /*
814  * wrapper routines for decode_tl_data
815  */
816 static krb5_error_code
817 krb5_get_int_from_tl_data(krb5_context context, krb5_db_entry *entries,
818                           int type, int *intval)
819 {
820     krb5_error_code             st=0;
821     krb5_tl_data                tl_data;
822     void                        *voidptr=NULL;
823     int                         *intptr=NULL;
824
825     tl_data.tl_data_type = KDB_TL_USER_INFO;
826     if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
827         goto cleanup;
828
829     if (decode_tl_data(&tl_data, type, &voidptr) == 0) {
830         intptr = (int *) voidptr;
831         *intval = *intptr;
832         free(intptr);
833     }
834
835 cleanup:
836     return st;
837 }
838
839 /*
840  * Get the mask representing the attributes set on the directory
841  * object (user, policy ...).
842  */
843 krb5_error_code
844 krb5_get_attributes_mask(krb5_context context, krb5_db_entry *entries,
845                          int *mask)
846 {
847     return krb5_get_int_from_tl_data(context, entries, KDB_TL_MASK, mask);
848 }
849
850 krb5_error_code
851 krb5_get_princ_type(krb5_context context, krb5_db_entry *entries, int *ptype)
852 {
853     return krb5_get_int_from_tl_data(context, entries, KDB_TL_PRINCTYPE, ptype);
854 }
855
856 krb5_error_code
857 krb5_get_princ_count(krb5_context context, krb5_db_entry *entries, int *pcount)
858 {
859     return krb5_get_int_from_tl_data(context, entries, KDB_TL_PRINCCOUNT, pcount);
860 }
861
862 krb5_error_code
863 krb5_get_linkdn(krb5_context context, krb5_db_entry *entries, char ***link_dn)
864 {
865     krb5_error_code             st=0;
866     krb5_tl_data                tl_data;
867     void                        *voidptr=NULL;
868
869     *link_dn = NULL;
870     tl_data.tl_data_type = KDB_TL_USER_INFO;
871     if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
872         goto cleanup;
873
874     if (decode_tl_data(&tl_data, KDB_TL_LINKDN, &voidptr) == 0) {
875         *link_dn = (char **) voidptr;
876     }
877
878 cleanup:
879     return st;
880 }
881
882 static krb5_error_code
883 krb5_get_str_from_tl_data(krb5_context context, krb5_db_entry *entries,
884                           int type, char **strval)
885 {
886     krb5_error_code             st=0;
887     krb5_tl_data                tl_data;
888     void                        *voidptr=NULL;
889
890     if (type != KDB_TL_USERDN && type != KDB_TL_CONTAINERDN) {
891         st = EINVAL;
892         goto cleanup;
893     }
894
895     tl_data.tl_data_type = KDB_TL_USER_INFO;
896     if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
897         goto cleanup;
898
899     if (decode_tl_data(&tl_data, type, &voidptr) == 0) {
900         *strval = (char *) voidptr;
901     }
902
903 cleanup:
904     return st;
905 }
906
907 krb5_error_code
908 krb5_get_userdn(krb5_context context, krb5_db_entry *entries, char **userdn)
909 {
910     *userdn = NULL;
911     return krb5_get_str_from_tl_data(context, entries, KDB_TL_USERDN, userdn);
912 }
913
914 krb5_error_code
915 krb5_get_containerdn(krb5_context context, krb5_db_entry *entries,
916                      char **containerdn)
917 {
918     *containerdn = NULL;
919     return krb5_get_str_from_tl_data(context, entries, KDB_TL_CONTAINERDN, containerdn);
920 }
921
922 /*
923  * This function reads the attribute values (if the attribute is
924  * non-null) from the dn.  The read attribute values is compared
925  * aganist the attrvalues passed to the function and a bit mask is set
926  * for all the matching attributes (attributes existing in both list).
927  * The bit to be set is selected such that the index of the attribute
928  * in the attrvalues parameter is the position of the bit.  For ex:
929  * the first element in the attrvalues is present in both list shall
930  * set the LSB of the bit mask.
931  *
932  * In case if either the attribute or the attrvalues parameter to the
933  * function is NULL, then the existence of the object is considered
934  * and appropriate status is returned back.
935  */
936
937 krb5_error_code
938 checkattributevalue(LDAP *ld, char *dn, char *attribute, char **attrvalues,
939                     int *mask)
940 {
941     int                         st=0, one=1;
942     char                        **values=NULL, *attributes[2] = {NULL};
943     LDAPMessage                 *result=NULL, *entry=NULL;
944
945     if (strlen(dn) == 0) {
946         st = set_ldap_error(0, LDAP_NO_SUCH_OBJECT, OP_SEARCH);
947         return st;
948     }
949
950     attributes[0] = attribute;
951
952     /* read the attribute values from the dn */
953     if ((st = ldap_search_ext_s(ld,
954                                 dn,
955                                 LDAP_SCOPE_BASE,
956                                 0,
957                                 attributes,
958                                 0,
959                                 NULL,
960                                 NULL,
961                                 &timelimit,
962                                 LDAP_NO_LIMIT,
963                                 &result)) != LDAP_SUCCESS) {
964         st = set_ldap_error(0, st, OP_SEARCH);
965         return st;
966     }
967
968     /*
969      * If the attribute/attrvalues is NULL, then check for the
970      * existence of the object alone.
971      */
972     if (attribute == NULL || attrvalues == NULL)
973         goto cleanup;
974
975     /* reset the bit mask */
976     *mask = 0;
977
978     if ((entry=ldap_first_entry(ld, result)) != NULL) {
979         /* read the attribute values */
980         if ((values=ldap_get_values(ld, entry, attribute)) != NULL) {
981             int i,j;
982
983             /*
984              * Compare the read attribute values with the attrvalues
985              * array and set the appropriate bit mask.
986              */
987             for (j=0; attrvalues[j]; ++j) {
988                 for (i=0; values[i]; ++i) {
989                     if (strcasecmp(values[i], attrvalues[j]) == 0) {
990                         *mask |= (one<<j);
991                         break;
992                     }
993                 }
994             }
995             ldap_value_free(values);
996         }
997     }
998
999 cleanup:
1000     ldap_msgfree(result);
1001     return st;
1002 }
1003
1004
1005 /*
1006  * This function updates a single attribute with a single value of a
1007  * specified dn.  This function is mainly used to update
1008  * krbRealmReferences, krbKdcServers, krbAdminServers... when KDC,
1009  * ADMIN, PASSWD servers are associated with some realms or vice
1010  * versa.
1011  */
1012
1013 krb5_error_code
1014 updateAttribute(LDAP *ld, char *dn, char *attribute, char *value)
1015 {
1016     int                         st=0;
1017     LDAPMod                     modAttr, *mods[2]={NULL};
1018     char                        *values[2]={NULL};
1019
1020     values[0] = value;
1021
1022     /* data to update the {attr,attrval} combination */
1023     memset(&modAttr, 0, sizeof(modAttr));
1024     modAttr.mod_type = attribute;
1025     modAttr.mod_op = LDAP_MOD_ADD;
1026     modAttr.mod_values = values;
1027     mods[0] = &modAttr;
1028
1029     /* ldap modify operation */
1030     st = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1031
1032     /* if the {attr,attrval} combination is already present return a success
1033      * LDAP_ALREADY_EXISTS is for single-valued attribute
1034      * LDAP_TYPE_OR_VALUE_EXISTS is for multi-valued attribute
1035      */
1036     if (st == LDAP_ALREADY_EXISTS || st == LDAP_TYPE_OR_VALUE_EXISTS)
1037         st = 0;
1038
1039     if (st != 0) {
1040         st = set_ldap_error (0, st, OP_MOD);
1041     }
1042
1043     return st;
1044 }
1045
1046 /*
1047  * This function deletes a single attribute with a single value of a
1048  * specified dn.  This function is mainly used to delete
1049  * krbRealmReferences, krbKdcServers, krbAdminServers... when KDC,
1050  * ADMIN, PASSWD servers are disassociated with some realms or vice
1051  * versa.
1052  */
1053
1054 krb5_error_code
1055 deleteAttribute(LDAP *ld, char *dn, char *attribute, char *value)
1056 {
1057     krb5_error_code             st=0;
1058     LDAPMod                     modAttr, *mods[2]={NULL};
1059     char                        *values[2]={NULL};
1060
1061     values[0] = value;
1062
1063     /* data to delete the {attr,attrval} combination */
1064     memset(&modAttr, 0, sizeof(modAttr));
1065     modAttr.mod_type = attribute;
1066     modAttr.mod_op = LDAP_MOD_DELETE;
1067     modAttr.mod_values = values;
1068     mods[0] = &modAttr;
1069
1070     /* ldap modify operation */
1071     st = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
1072
1073     /* if either the attribute or the attribute value is missing return a success */
1074     if (st == LDAP_NO_SUCH_ATTRIBUTE || st == LDAP_UNDEFINED_TYPE)
1075         st = 0;
1076
1077     if (st != 0) {
1078         st = set_ldap_error (0, st, OP_MOD);
1079     }
1080
1081     return st;
1082 }
1083
1084
1085 /*
1086  * This function takes in 2 string arrays, compares them to remove the
1087  * matching entries.  The first array is the original list and the
1088  * second array is the modified list.  Removing the matching entries
1089  * will result in a reduced array, where the left over first array
1090  * elements are the deleted entries and the left over second array
1091  * elements are the added entries.  These additions and deletions has
1092  * resulted in the modified second array.
1093  */
1094
1095 krb5_error_code
1096 disjoint_members(char **src, char **dest)
1097 {
1098     int                         i=0, j=0, slen=0, dlen=0;
1099
1100     /* validate the input parameters */
1101     if (src == NULL || dest == NULL)
1102         return 0;
1103
1104     /* compute the first array length */
1105     for (i=0;src[i]; ++i)
1106         ;
1107
1108     /* return if the length is 0 */
1109     if (i==0)
1110         return 0;
1111
1112     /* index of the last element and also the length of the array */
1113     slen = i-1;
1114
1115     /* compute the second array length */
1116     for (i=0;dest[i]; ++i)
1117         ;
1118
1119     /* return if the length is 0 */
1120     if (i==0)
1121         return 0;
1122
1123     /* index of the last element and also the length of the array */
1124     dlen = i-1;
1125
1126     /* check for the similar elements and delete them from both the arrays */
1127     for (i=0; src[i]; ++i) {
1128
1129         for (j=0; dest[j]; ++j) {
1130
1131             /* if the element are same */
1132             if (strcasecmp(src[i], dest[j]) == 0) {
1133                 /*
1134                  * If the matched element is in the middle, then copy
1135                  * the last element to the matched index.
1136                  */
1137                 if (i != slen) {
1138                     free (src[i]);
1139                     src[i] = src[slen];
1140                     src[slen] = NULL;
1141                 } else {
1142                     /*
1143                      * If the matched element is the last, free it and
1144                      * set it to NULL.
1145                      */
1146                     free (src[i]);
1147                     src[i] = NULL;
1148                 }
1149                 /* reduce the array length by 1 */
1150                 slen -= 1;
1151
1152                 /* repeat the same processing for the second array too */
1153                 if (j != dlen) {
1154                     free(dest[j]);
1155                     dest[j] = dest[dlen];
1156                     dest[dlen] = NULL;
1157                 } else {
1158                     free(dest[j]);
1159                     dest[j] = NULL;
1160                 }
1161                 dlen -=1;
1162
1163                 /*
1164                  * The source array is reduced by 1, so reduce the
1165                  * index variable used for source array by 1.  No need
1166                  * to adjust the second array index variable as it is
1167                  * reset while entering the inner loop.
1168                  */
1169                 i -= 1;
1170                 break;
1171             }
1172         }
1173     }
1174     return 0;
1175 }
1176
1177 /*
1178  * This function replicates the contents of the src array for later
1179  * use. Mostly the contents of the src array is obtained from a
1180  * ldap_search operation and the contents are required for later use.
1181  */
1182
1183 krb5_error_code
1184 copy_arrays(char **src, char ***dest, int count)
1185 {
1186     krb5_error_code             st=0;
1187     int                         i=0;
1188
1189     /* validate the input parameters */
1190     if (src == NULL || dest == NULL)
1191         return 0;
1192
1193     /* allocate memory for the dest array */
1194     *dest = (char **) calloc((unsigned) count+1, sizeof(char *));
1195     if (*dest == NULL) {
1196         st = ENOMEM;
1197         goto cleanup;
1198     }
1199
1200     /* copy the members from src to dest array. */
1201     for (i=0; i < count && src[i] != NULL; ++i) {
1202         (*dest)[i] = strdup(src[i]);
1203         if ((*dest)[i] == NULL) {
1204             st = ENOMEM;
1205             goto cleanup;
1206         }
1207     }
1208
1209 cleanup:
1210     /* in case of error free up everything and return */
1211     if (st != 0) {
1212         if (*dest != NULL) {
1213             for (i=0; (*dest)[i]; ++i) {
1214                 free ((*dest)[i]);
1215                 (*dest)[i] = NULL;
1216             }
1217             free (*dest);
1218             *dest = NULL;
1219         }
1220     }
1221     return st;
1222 }
1223
1224 static krb5_error_code
1225 getepochtime(char *strtime, krb5_timestamp *epochtime)
1226 {
1227     struct tm           tme;
1228
1229     memset(&tme, 0, sizeof(tme));
1230     if (strptime(strtime,"%Y%m%d%H%M%SZ", &tme) == NULL) {
1231         *epochtime = 0;
1232         return EINVAL;
1233     }
1234     *epochtime = krb5int_gmt_mktime(&tme);
1235     return 0;
1236 }
1237
1238 /*
1239  * krb5_ldap_get_value() - get the integer value of the attribute
1240  * Returns, 0 if the attribute is present, 1 if the attribute is missing.
1241  * The retval is 0 if the attribute is missing.
1242  */
1243
1244 krb5_error_code
1245 krb5_ldap_get_value(LDAP *ld, LDAPMessage *ent, char *attribute, int *retval)
1246 {
1247     char                           **values=NULL;
1248
1249     *retval = 0;
1250     values=ldap_get_values(ld, ent, attribute);
1251     if (values != NULL) {
1252         if (values[0] != NULL)
1253             *retval = atoi(values[0]);
1254         ldap_value_free(values);
1255         return 0;
1256     }
1257     return 1;
1258 }
1259
1260 /*
1261  * krb5_ldap_get_string() - Returns the first string of the
1262  * attribute.  Intended to
1263  *
1264  *
1265  */
1266 krb5_error_code
1267 krb5_ldap_get_string(LDAP *ld, LDAPMessage *ent, char *attribute,
1268                      char **retstr, krb5_boolean *attr_present)
1269 {
1270     char                           **values=NULL;
1271     krb5_error_code                st=0;
1272
1273     *retstr = NULL;
1274     if (attr_present != NULL)
1275         *attr_present = FALSE;
1276
1277     values=ldap_get_values(ld, ent, attribute);
1278     if (values != NULL) {
1279         if (values[0] != NULL) {
1280             if (attr_present!= NULL)
1281                 *attr_present = TRUE;
1282             *retstr = strdup(values[0]);
1283             if (*retstr == NULL)
1284                 st = ENOMEM;
1285         }
1286         ldap_value_free(values);
1287     }
1288     return st;
1289 }
1290
1291 /*
1292  * krb5_ldap_get_strings() - Returns all the values
1293  * of the attribute.
1294  */
1295 krb5_error_code
1296 krb5_ldap_get_strings(LDAP *ld, LDAPMessage *ent, char *attribute,
1297                       char ***retarr, krb5_boolean *attr_present)
1298 {
1299     char                        **values=NULL;
1300     krb5_error_code             st=0;
1301     unsigned int                i=0, count=0;
1302
1303     *retarr = NULL;
1304     if (attr_present != NULL)
1305         *attr_present = FALSE;
1306
1307     values=ldap_get_values(ld, ent, attribute);
1308     if (values != NULL) {
1309         if (attr_present != NULL)
1310             *attr_present = TRUE;
1311
1312         count = ldap_count_values(values);
1313         *retarr  = (char **) calloc(count+1, sizeof(char *));
1314         if (*retarr == NULL) {
1315             st = ENOMEM;
1316             return st;
1317         }
1318         for (i=0; i< count; ++i) {
1319             (*retarr)[i] = strdup(values[i]);
1320             if ((*retarr)[i] == NULL) {
1321                 st = ENOMEM;
1322                 goto cleanup;
1323             }
1324         }
1325         ldap_value_free(values);
1326     }
1327
1328 cleanup:
1329     if (st != 0) {
1330         if (*retarr != NULL) {
1331             for (i=0; i< count; ++i)
1332                 if ((*retarr)[i] != NULL)
1333                     free ((*retarr)[i]);
1334             free (*retarr);
1335         }
1336     }
1337     return st;
1338 }
1339
1340 krb5_error_code
1341 krb5_ldap_get_time(LDAP *ld, LDAPMessage *ent, char *attribute,
1342                    krb5_timestamp *rettime, krb5_boolean *attr_present)
1343 {
1344     char                         **values=NULL;
1345     krb5_error_code              st=0;
1346
1347     *rettime = 0;
1348     *attr_present = FALSE;
1349
1350     values=ldap_get_values(ld, ent, attribute);
1351     if (values != NULL) {
1352         if (values[0] != NULL) {
1353             *attr_present = TRUE;
1354             st = getepochtime(values[0], rettime);
1355         }
1356         ldap_value_free(values);
1357     }
1358     return st;
1359 }
1360
1361 /*
1362  * Function to allocate, set the values of LDAPMod structure. The
1363  * LDAPMod structure is then added to the array at the ind
1364  */
1365
1366 krb5_error_code
1367 krb5_add_member(LDAPMod ***mods, int *count)
1368 {
1369     int i=0;
1370     LDAPMod **lmods=NULL;
1371
1372     if ((*mods) != NULL) {
1373         for (;(*mods)[i] != NULL; ++i)
1374             ;
1375     }
1376     lmods = (LDAPMod **) realloc((*mods), (2+i) * sizeof(LDAPMod *));
1377     if (lmods == NULL)
1378         return ENOMEM;
1379
1380     *mods = lmods;
1381     (*mods)[i+1] = NULL;
1382     (*mods)[i] = (LDAPMod *) calloc(1, sizeof (LDAPMod));
1383     if ((*mods)[i] == NULL)
1384         return ENOMEM;
1385     *count = i;
1386     return 0;
1387 }
1388
1389 krb5_error_code
1390 krb5_add_str_mem_ldap_mod(LDAPMod ***mods, char *attribute, int op,
1391                           char **values)
1392 {
1393     int i=0, j=0;
1394     krb5_error_code   st=0;
1395
1396     if ((st=krb5_add_member(mods, &i)) != 0)
1397         return st;
1398
1399     (*mods)[i]->mod_type = strdup(attribute);
1400     if ((*mods)[i]->mod_type == NULL)
1401         return ENOMEM;
1402     (*mods)[i]->mod_op = op;
1403
1404     (*mods)[i]->mod_values = NULL;
1405
1406     if (values != NULL) {
1407         for (j=0; values[j] != NULL; ++j)
1408             ;
1409         (*mods)[i]->mod_values = malloc (sizeof(char *) * (j+1));
1410         if ((*mods)[i]->mod_values == NULL)
1411             return ENOMEM;
1412
1413         for (j=0; values[j] != NULL; ++j) {
1414             (*mods)[i]->mod_values[j] = strdup(values[j]);
1415             if ((*mods)[i]->mod_values[j] == NULL)
1416                 return ENOMEM;
1417         }
1418         (*mods)[i]->mod_values[j] = NULL;
1419     }
1420     return 0;
1421 }
1422
1423 krb5_error_code
1424 krb5_add_ber_mem_ldap_mod(LDAPMod ***mods, char *attribute, int op,
1425                           struct berval **ber_values)
1426 {
1427     int i=0, j=0;
1428     krb5_error_code   st=0;
1429
1430     if ((st=krb5_add_member(mods, &i)) != 0)
1431         return st;
1432
1433     (*mods)[i]->mod_type = strdup(attribute);
1434     if ((*mods)[i]->mod_type == NULL)
1435         return ENOMEM;
1436     (*mods)[i]->mod_op = op;
1437
1438     for (j=0; ber_values[j] != NULL; ++j)
1439         ;
1440     (*mods)[i]->mod_bvalues = malloc (sizeof(struct berval *) * (j+1));
1441     if ((*mods)[i]->mod_bvalues == NULL)
1442         return ENOMEM;
1443
1444     for (j=0; ber_values[j] != NULL; ++j) {
1445         (*mods)[i]->mod_bvalues[j] = calloc(1, sizeof(struct berval));
1446         if ((*mods)[i]->mod_bvalues[j] == NULL)
1447             return ENOMEM;
1448
1449         (*mods)[i]->mod_bvalues[j]->bv_len = ber_values[j]->bv_len;
1450         (*mods)[i]->mod_bvalues[j]->bv_val = malloc((*mods)[i]->mod_bvalues[j]->bv_len);
1451         if ((*mods)[i]->mod_bvalues[j]->bv_val == NULL)
1452             return ENOMEM;
1453
1454         memcpy((*mods)[i]->mod_bvalues[j]->bv_val, ber_values[j]->bv_val,
1455                ber_values[j]->bv_len);
1456     }
1457     (*mods)[i]->mod_bvalues[j] = NULL;
1458     return 0;
1459 }
1460
1461 static inline char *
1462 format_d (int val)
1463 {
1464     char tmpbuf[2+3*sizeof(val)];
1465     snprintf(tmpbuf, sizeof(tmpbuf), "%d", val);
1466     return strdup(tmpbuf);
1467 }
1468
1469 krb5_error_code
1470 krb5_add_int_arr_mem_ldap_mod(LDAPMod ***mods, char *attribute, int op,
1471                               int *value)
1472 {
1473     int i=0, j=0;
1474     krb5_error_code   st=0;
1475
1476     if ((st=krb5_add_member(mods, &i)) != 0)
1477         return st;
1478
1479     (*mods)[i]->mod_type = strdup(attribute);
1480     if ((*mods)[i]->mod_type == NULL)
1481         return ENOMEM;
1482     (*mods)[i]->mod_op = op;
1483
1484     for (j=0; value[j] != -1; ++j)
1485         ;
1486
1487     (*mods)[i]->mod_values = malloc(sizeof(char *) * (j+1));
1488
1489     for (j=0; value[j] != -1; ++j) {
1490         if (((*mods)[i]->mod_values[j] = format_d(value[j])) == NULL)
1491             return ENOMEM;
1492     }
1493     (*mods)[i]->mod_values[j] = NULL;
1494     return 0;
1495 }
1496
1497 krb5_error_code
1498 krb5_add_int_mem_ldap_mod(LDAPMod ***mods, char *attribute, int op, int value)
1499 {
1500     int i=0;
1501     krb5_error_code      st=0;
1502
1503     if ((st=krb5_add_member(mods, &i)) != 0)
1504         return st;
1505
1506     (*mods)[i]->mod_type = strdup(attribute);
1507     if ((*mods)[i]->mod_type == NULL)
1508         return ENOMEM;
1509
1510     (*mods)[i]->mod_op = op;
1511     (*mods)[i]->mod_values = calloc (2, sizeof(char *));
1512     if (((*mods)[i]->mod_values[0] = format_d(value)) == NULL)
1513         return ENOMEM;
1514     return 0;
1515 }
1516
1517 krb5_error_code
1518 krb5_ldap_lock(krb5_context kcontext, int mode)
1519 {
1520     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1521     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1522     return status;
1523 }
1524
1525 krb5_error_code
1526 krb5_ldap_unlock(krb5_context kcontext)
1527 {
1528     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1529     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1530     return status;
1531 }
1532
1533 krb5_error_code
1534 krb5_ldap_supported_realms(krb5_context kcontext, char **realms)
1535 {
1536     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1537     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1538     return status;
1539 }
1540
1541 krb5_error_code
1542 krb5_ldap_free_supported_realms(krb5_context kcontext, char **realms)
1543 {
1544     krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
1545     krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
1546     return status;
1547 }
1548
1549 const char *
1550 krb5_ldap_errcode_2_string(krb5_context kcontext, long err_code)
1551 {
1552     return krb5_get_error_message(kcontext, err_code);
1553 }
1554
1555 void
1556 krb5_ldap_release_errcode_string(krb5_context kcontext, const char *msg)
1557 {
1558     krb5_free_error_message(kcontext, msg);
1559 }
1560
1561
1562 /*
1563  * Get the number of times an object has been referred to in a realm. this is
1564  * needed to find out if deleting the attribute will cause dangling links.
1565  *
1566  * An LDAP handle may be optionally specified to prevent race condition - there
1567  * are a limited number of LDAP handles.
1568  */
1569 krb5_error_code
1570 krb5_ldap_get_reference_count(krb5_context context, char *dn, char *refattr,
1571                               int *count, LDAP *ld)
1572 {
1573     int                         st = 0, tempst = 0, gothandle = 0;
1574     unsigned int                i, ntrees;
1575     char                        *refcntattr[2];
1576     char                        *filter = NULL;
1577     char                        **subtree = NULL, *ptr = NULL;
1578     kdb5_dal_handle             *dal_handle = NULL;
1579     krb5_ldap_context           *ldap_context = NULL;
1580     krb5_ldap_server_handle     *ldap_server_handle = NULL;
1581     LDAPMessage                 *result = NULL;
1582
1583
1584     if (dn == NULL || refattr == NULL) {
1585         st = EINVAL;
1586         goto cleanup;
1587     }
1588
1589     SETUP_CONTEXT();
1590     if (ld == NULL) {
1591         GET_HANDLE();
1592         gothandle = 1;
1593     }
1594
1595     refcntattr [0] = refattr;
1596     refcntattr [1] = NULL;
1597
1598     ptr = ldap_filter_correct (dn);
1599     if (ptr == NULL) {
1600         st = ENOMEM;
1601         goto cleanup;
1602     }
1603
1604     if (asprintf (&filter, "%s=%s", refattr, ptr) < 0) {
1605         filter = NULL;
1606         st = ENOMEM;
1607         goto cleanup;
1608     }
1609
1610     if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0)
1611         goto cleanup;
1612
1613     for (i = 0, *count = 0; i < ntrees; i++) {
1614         int n;
1615
1616         LDAP_SEARCH(subtree[i],
1617                     LDAP_SCOPE_SUBTREE,
1618                     filter,
1619                     refcntattr);
1620         n = ldap_count_entries (ld, result);
1621         if (n == -1) {
1622             int ret, errcode = 0;
1623             ret = ldap_parse_result (ld, result, &errcode, NULL, NULL, NULL, NULL, 0);
1624             if (ret != LDAP_SUCCESS)
1625                 errcode = ret;
1626             st = translate_ldap_error (errcode, OP_SEARCH);
1627             goto cleanup;
1628         }
1629
1630         ldap_msgfree(result);
1631         result = NULL;
1632
1633         *count += n;
1634     }
1635
1636 cleanup:
1637     if (filter != NULL)
1638         free (filter);
1639
1640     if (result != NULL)
1641         ldap_msgfree (result);
1642
1643     if (subtree != NULL) {
1644         for (i = 0; i < ntrees; i++)
1645             free (subtree[i]);
1646         free (subtree);
1647     }
1648
1649     if (ptr != NULL)
1650         free (ptr);
1651
1652     if (gothandle == 1)
1653         krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1654
1655     return st;
1656 }
1657
1658 /*
1659  * For now, policy objects are expected to be directly under the realm
1660  * container.
1661  */
1662 krb5_error_code
1663 krb5_ldap_policydn_to_name(krb5_context context, char *policy_dn, char **name)
1664 {
1665     int len1, len2;
1666     krb5_error_code             st = 0;
1667     kdb5_dal_handle             *dal_handle=NULL;
1668     krb5_ldap_context           *ldap_context=NULL;
1669
1670     SETUP_CONTEXT();
1671
1672     if (ldap_context->lrparams->realmdn == NULL) {
1673         st = EINVAL;
1674         goto cleanup;
1675     }
1676
1677     len1 = strlen (ldap_context->lrparams->realmdn);
1678     len2 = strlen (policy_dn);
1679     if (len1 == 0 || len2 == 0 || len1 > len2) {
1680         st = EINVAL;
1681         goto cleanup;
1682     }
1683
1684     if (strcmp (ldap_context->lrparams->realmdn, policy_dn + (len2 - len1)) != 0) {
1685         st = EINVAL;
1686         goto cleanup;
1687     }
1688
1689 #if defined HAVE_LDAP_STR2DN
1690     {
1691         char *rdn;
1692         LDAPDN dn;
1693         rdn = strndup(policy_dn, len2 - len1 - 1); /* 1 character for ',' */
1694
1695         if (ldap_str2dn (rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC) != 0) {
1696             st = EINVAL;
1697             goto cleanup;
1698         }
1699         if (dn[0] == NULL || dn[1] != NULL)
1700             st = EINVAL;
1701         else if (strcasecmp (dn[0][0]->la_attr.bv_val, "cn") != 0)
1702             st = EINVAL;
1703         else {
1704             *name = strndup(dn[0][0]->la_value.bv_val, dn[0][0]->la_value.bv_len);
1705             if (*name == NULL)
1706                 st = EINVAL;
1707         }
1708
1709         ldap_memfree (dn);
1710     }
1711 #elif defined HAVE_LDAP_EXPLODE_DN
1712     {
1713         char **parsed_dn;
1714
1715         /* 1 = return DN components without type prefix */
1716         parsed_dn = ldap_explode_dn(policy_dn, 1);
1717         if (parsed_dn == NULL) {
1718             st = EINVAL;
1719         } else {
1720             *name = strdup(parsed_dn[0]);
1721             if (*name == NULL)
1722                 st = EINVAL;
1723
1724             ldap_value_free(parsed_dn);
1725         }
1726     }
1727 #else
1728     st = EINVAL;
1729 #endif
1730
1731 cleanup:
1732     return st;
1733 }
1734
1735 krb5_error_code
1736 krb5_ldap_name_to_policydn(krb5_context context, char *name, char **policy_dn)
1737 {
1738     int                         len;
1739     char                        *ptr = NULL;
1740     krb5_error_code             st = 0;
1741     kdb5_dal_handle             *dal_handle=NULL;
1742     krb5_ldap_context           *ldap_context=NULL;
1743
1744     *policy_dn = NULL;
1745
1746     /* validate the input parameters */
1747     if (name == NULL) {
1748         st = EINVAL;
1749         goto cleanup;
1750     }
1751
1752     /* Used for removing policy reference from an object */
1753     if (name[0] == '\0') {
1754         if ((*policy_dn = strdup ("")) == NULL)
1755             st = ENOMEM;
1756         goto cleanup;
1757     }
1758
1759     SETUP_CONTEXT();
1760
1761     if (ldap_context->lrparams->realmdn == NULL) {
1762         st = EINVAL;
1763         goto cleanup;
1764     }
1765     len = strlen (ldap_context->lrparams->realmdn);
1766
1767     ptr = ldap_filter_correct (name);
1768     if (ptr == NULL) {
1769         st = ENOMEM;
1770         goto cleanup;
1771     }
1772     len += strlen (ptr);
1773
1774     len += sizeof ("cn=") + 3;
1775
1776     *policy_dn = (char *) malloc (len);
1777     if (*policy_dn == NULL) {
1778         st = ENOMEM;
1779         goto cleanup;
1780     }
1781
1782     sprintf (*policy_dn, "cn=%s,%s", ptr, ldap_context->lrparams->realmdn);
1783
1784 cleanup:
1785     if (ptr != NULL)
1786         free (ptr);
1787     return st;
1788 }
1789
1790 /* remove overlapping and repeated subtree entries from the list of subtrees */
1791 static krb5_error_code
1792 remove_overlapping_subtrees(char **listin, char **listop, int *subtcount,
1793                             int sscope)
1794 {
1795     int     slen=0, k=0, j=0, lendiff=0;
1796     int     count = *subtcount;
1797     char    **subtree = listop;
1798
1799     slen = count-1;
1800     for (k=0; k<=slen && listin[k]!=NULL ; k++) {
1801         for (j=k+1; j<=slen && listin[j]!=NULL ;j++) {
1802             lendiff = strlen(listin[k]) - strlen(listin[j]);
1803             if (sscope == 2) {
1804                 if ((lendiff > 0) && (strcasecmp((listin[k])+lendiff, listin[j])==0)) {
1805                     if (k != slen) {
1806                         free(listin[k]);
1807                         listin[k] = listin[slen];
1808                         listin[slen] = NULL;
1809                     } else {
1810                         free(listin[k]);
1811                         listin[k] = NULL;
1812                     }
1813                     slen-=1;
1814                     k-=1;
1815                     break;
1816                 } else if ((lendiff < 0) && (strcasecmp((listin[j])+abs(lendiff), listin[k])==0)) {
1817                     if (j != slen) {
1818                         free(listin[j]);
1819                         listin[j] = listin[slen];
1820                         listin[slen]=NULL;
1821                     } else {
1822                         free(listin[j]);
1823                         listin[j] = NULL;
1824                     }
1825                     slen-=1;
1826                     j-=1;
1827                 }
1828             }
1829             if ((lendiff == 0) && (strcasecmp(listin[j], listin[k])==0)) {
1830                 if (j != slen) {
1831                     free(listin[j]);
1832                     listin[j] = listin[slen];
1833                     listin[slen]=NULL;
1834                 } else {
1835                     free(listin[j]);
1836                     listin[j] = NULL;
1837                 }
1838                 slen -=1;
1839                 j-=1;
1840             }
1841         }
1842     }
1843     *subtcount=slen+1;
1844     for (k=0; k<*subtcount && listin[k]!=NULL; k++) {
1845         subtree[k] = strdup(listin[k]);
1846         if (subtree[k] == NULL) {
1847             return ENOMEM;
1848         }
1849     }
1850     return 0;
1851 }
1852
1853 /*
1854  * Fill out a krb5_db_entry princ entry struct given a LDAP message containing
1855  * the results of a principal search of the directory.
1856  */
1857 krb5_error_code
1858 populate_krb5_db_entry(krb5_context context, krb5_ldap_context *ldap_context,
1859                        LDAP *ld, LDAPMessage *ent, krb5_const_principal princ,
1860                        krb5_db_entry *entry)
1861 {
1862     krb5_error_code st = 0;
1863     unsigned int    mask = 0;
1864     krb5_boolean    attr_present = FALSE;
1865     char            **values = NULL, *policydn = NULL, *pwdpolicydn = NULL;
1866     char            *polname = NULL, *tktpolname = NULL;
1867     struct berval   **bvalues = NULL;
1868     krb5_tl_data    userinfo_tl_data = {0};
1869     char            **link_references = NULL;
1870     char *DN = NULL;
1871
1872     if (princ == NULL) {
1873         /* XXX WAF probably should just extract princ from ldap result */
1874         st = EINVAL;
1875         goto cleanup;
1876     } else {
1877         if ((st=krb5_copy_principal(context, princ, &(entry->princ))) != 0)
1878             goto cleanup;
1879     }
1880     /* get the associated directory user information */
1881     if ((values = ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
1882         int i, pcount=0, kerberos_principal_object_type=0;
1883         char *user;
1884
1885         if ((st=krb5_unparse_name(context, princ, &user)) != 0)
1886             goto cleanup;
1887
1888         for (i=0; values[i] != NULL; ++i) {
1889             if (strcasecmp(values[i], user) == 0) {
1890                 pcount = ldap_count_values(values);
1891                 break;
1892             }
1893         }
1894         ldap_value_free(values);
1895         free(user);
1896
1897         if ((DN = ldap_get_dn(ld, ent)) == NULL) {
1898             ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
1899             st = set_ldap_error(context, st, 0);
1900             goto cleanup;
1901         }
1902
1903         if ((values=ldap_get_values(ld, ent, "objectclass")) != NULL) {
1904             for (i=0; values[i] != NULL; ++i)
1905                 if (strcasecmp(values[i], "krbprincipal") == 0) {
1906                     kerberos_principal_object_type = KDB_STANDALONE_PRINCIPAL_OBJECT;
1907                     if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE,
1908                                           &kerberos_principal_object_type)) != 0)
1909                         goto cleanup;
1910                     break;
1911                 }
1912             ldap_value_free(values);
1913         }
1914
1915         /* add principalcount, DN and principaltype user information to tl_data */
1916         if (((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount)) != 0) ||
1917             ((st=store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, DN)) != 0))
1918             goto cleanup;
1919     }
1920
1921     /* read all the kerberos attributes */
1922
1923     /* KRBLASTSUCCESSFULAUTH */
1924     if ((st=krb5_ldap_get_time(ld, ent, "krbLastSuccessfulAuth",
1925                                &(entry->last_success), &attr_present)) != 0)
1926         goto cleanup;
1927     if (attr_present == TRUE)
1928         mask |= KDB_LAST_SUCCESS_ATTR;
1929
1930     /* KRBLASTFAILEDAUTH */
1931     if ((st=krb5_ldap_get_time(ld, ent, "krbLastFailedAuth",
1932                                &(entry->last_failed), &attr_present)) != 0)
1933         goto cleanup;
1934     if (attr_present == TRUE)
1935         mask |= KDB_LAST_FAILED_ATTR;
1936
1937     /* KRBLOGINFAILEDCOUNT */
1938     if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount",
1939                             &(entry->fail_auth_count)) == 0)
1940         mask |= KDB_FAIL_AUTH_COUNT_ATTR;
1941
1942     /* KRBMAXTICKETLIFE */
1943     if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &(entry->max_life)) == 0)
1944         mask |= KDB_MAX_LIFE_ATTR;
1945
1946     /* KRBMAXRENEWABLEAGE */
1947     if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage",
1948                             &(entry->max_renewable_life)) == 0)
1949         mask |= KDB_MAX_RLIFE_ATTR;
1950
1951     /* KRBTICKETFLAGS */
1952     if (krb5_ldap_get_value(ld, ent, "krbticketflags", &(entry->attributes)) == 0)
1953         mask |= KDB_TKT_FLAGS_ATTR;
1954
1955     /* PRINCIPAL EXPIRATION TIME */
1956     if ((st=krb5_ldap_get_time(ld, ent, "krbprincipalexpiration", &(entry->expiration),
1957                                &attr_present)) != 0)
1958         goto cleanup;
1959     if (attr_present == TRUE)
1960         mask |= KDB_PRINC_EXPIRE_TIME_ATTR;
1961
1962     /* PASSWORD EXPIRATION TIME */
1963     if ((st=krb5_ldap_get_time(ld, ent, "krbpasswordexpiration", &(entry->pw_expiration),
1964                                &attr_present)) != 0)
1965         goto cleanup;
1966     if (attr_present == TRUE)
1967         mask |= KDB_PWD_EXPIRE_TIME_ATTR;
1968
1969     /* KRBPOLICYREFERENCE */
1970
1971     if ((st=krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn,
1972                                  &attr_present)) != 0)
1973         goto cleanup;
1974     if (attr_present == TRUE) {
1975         mask |= KDB_POL_REF_ATTR;
1976         /* Ensure that the policy is inside the realm container */
1977         if ((st = krb5_ldap_policydn_to_name (context, policydn, &tktpolname)) != 0)
1978             goto cleanup;
1979     }
1980
1981     /* KRBPWDPOLICYREFERENCE */
1982     if ((st=krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn,
1983                                  &attr_present)) != 0)
1984         goto cleanup;
1985     if (attr_present == TRUE) {
1986         krb5_tl_data  kadm_tl_data;
1987
1988         mask |= KDB_PWD_POL_REF_ATTR;
1989
1990         /* Ensure that the policy is inside the realm container */
1991         if ((st = krb5_ldap_policydn_to_name (context, pwdpolicydn, &polname)) != 0)
1992             goto cleanup;
1993
1994         if ((st = krb5_update_tl_kadm_data(polname, &kadm_tl_data)) != 0) {
1995             goto cleanup;
1996         }
1997         krb5_dbe_update_tl_data(context, entry, &kadm_tl_data);
1998     }
1999
2000     /* KRBSECRETKEY */
2001     if ((bvalues=ldap_get_values_len(ld, ent, "krbprincipalkey")) != NULL) {
2002         krb5_kvno mkvno = 0;
2003
2004         mask |= KDB_SECRET_KEY_ATTR;
2005         if ((st=krb5_decode_krbsecretkey(context, entry, bvalues, &userinfo_tl_data, &mkvno)) != 0)
2006             goto cleanup;
2007         if (mkvno != 0) {
2008             /* don't add the tl data if mkvno == 0 */
2009             if ((st=krb5_dbe_update_mkvno(context, entry, mkvno)) != 0)
2010                 goto cleanup;
2011         }
2012     }
2013
2014     /* LAST PASSWORD CHANGE */
2015     {
2016         krb5_timestamp lstpwdchng=0;
2017         if ((st=krb5_ldap_get_time(ld, ent, "krbLastPwdChange",
2018                                    &lstpwdchng, &attr_present)) != 0)
2019             goto cleanup;
2020         if (attr_present == TRUE) {
2021             if ((st=krb5_dbe_update_last_pwd_change(context, entry,
2022                                                     lstpwdchng)))
2023                 goto cleanup;
2024             mask |= KDB_LAST_PWD_CHANGE_ATTR;
2025         }
2026     }
2027
2028     /* ALLOWED TO DELEGATE TO */
2029     {
2030         char **a2d2 = NULL;
2031         int i;
2032         krb5_tl_data **tlp;
2033
2034         st = krb5_ldap_get_strings(ld, ent, "krbAllowedToDelegateTo",
2035                                    &a2d2, &attr_present);
2036         if (st != 0)
2037             goto cleanup;
2038
2039         if (attr_present == TRUE) {
2040             for (tlp = &entry->tl_data; *tlp; tlp = &(*tlp)->tl_data_next)
2041                 ;
2042             for (i = 0; a2d2[i] != NULL; i++) {
2043                 krb5_tl_data *tl = k5alloc(sizeof(*tl), &st);
2044                 if (st != 0) {
2045                     ldap_value_free(a2d2);
2046                     goto cleanup;
2047                 }
2048                 tl->tl_data_type = KRB5_TL_CONSTRAINED_DELEGATION_ACL;
2049                 tl->tl_data_length = strlen(a2d2[i]);
2050                 tl->tl_data_contents = (krb5_octet *)strdup(a2d2[i]);
2051                 if (tl->tl_data_contents == NULL) {
2052                     st = ENOMEM;
2053                     ldap_value_free(a2d2);
2054                     free(tl);
2055                     goto cleanup;
2056                 }
2057                 tl->tl_data_next = NULL;
2058                 *tlp = tl;
2059                 tlp = &tl->tl_data_next;
2060             }
2061             ldap_value_free(a2d2);
2062         }
2063     }
2064
2065     /* KRBOBJECTREFERENCES */
2066     {
2067         int i=0;
2068
2069         if ((st = krb5_ldap_get_strings(ld, ent, "krbobjectreferences",
2070                                         &link_references, &attr_present)) != 0)
2071             goto cleanup;
2072         if (link_references != NULL) {
2073             for (i=0; link_references[i] != NULL; ++i) {
2074                 if ((st = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN,
2075                                         link_references[i])) != 0)
2076                     goto cleanup;
2077             }
2078         }
2079     }
2080
2081     /* Set tl_data */
2082     {
2083         int i;
2084         struct berval **ber_tl_data = NULL;
2085         krb5_tl_data *ptr = NULL;
2086
2087         if ((ber_tl_data = ldap_get_values_len (ld, ent, "krbExtraData")) != NULL) {
2088             for (i = 0; ber_tl_data[i] != NULL; i++) {
2089                 if ((st = berval2tl_data (ber_tl_data[i] , &ptr)) != 0)
2090                     break;
2091                 if ((st = krb5_dbe_update_tl_data(context, entry, ptr)) != 0)
2092                     break;
2093             }
2094             ldap_value_free_len (ber_tl_data);
2095             if (st != 0)
2096                 goto cleanup;
2097             mask |= KDB_EXTRA_DATA_ATTR;
2098         }
2099     }
2100
2101     /* update the mask of attributes present on the directory object to the tl_data */
2102     if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask)) != 0)
2103         goto cleanup;
2104     if ((st=krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data)) != 0)
2105         goto cleanup;
2106
2107 #ifdef HAVE_EDIRECTORY
2108     {
2109         krb5_timestamp              expiretime=0;
2110         char                        *is_login_disabled=NULL;
2111
2112         /* LOGIN EXPIRATION TIME */
2113         if ((st=krb5_ldap_get_time(ld, ent, "loginexpirationtime", &expiretime,
2114                                    &attr_present)) != 0)
2115             goto cleanup;
2116
2117         if (attr_present == TRUE) {
2118             if ((mask & KDB_PRINC_EXPIRE_TIME_ATTR) == 1) {
2119                 if (expiretime < entry->expiration)
2120                     entry->expiration = expiretime;
2121             } else {
2122                 entry->expiration = expiretime;
2123             }
2124         }
2125
2126         /* LOGIN DISABLED */
2127         if ((st=krb5_ldap_get_string(ld, ent, "logindisabled", &is_login_disabled,
2128                                      &attr_present)) != 0)
2129             goto cleanup;
2130         if (attr_present == TRUE) {
2131             if (strcasecmp(is_login_disabled, "TRUE")== 0)
2132                 entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
2133             free (is_login_disabled);
2134         }
2135     }
2136 #endif
2137
2138     if ((st=krb5_read_tkt_policy (context, ldap_context, entry, tktpolname)) !=0)
2139         goto cleanup;
2140
2141     /* We already know that the policy is inside the realm container. */
2142     if (polname) {
2143         osa_policy_ent_t   pwdpol;
2144         int                cnt=0;
2145         krb5_timestamp     last_pw_changed;
2146         krb5_ui_4          pw_max_life;
2147
2148         memset(&pwdpol, 0, sizeof(pwdpol));
2149
2150         if ((st=krb5_ldap_get_password_policy(context, polname, &pwdpol, &cnt)) != 0)
2151             goto cleanup;
2152         pw_max_life = pwdpol->pw_max_life;
2153         free (pwdpol);
2154
2155         if (pw_max_life > 0) {
2156             if ((st=krb5_dbe_lookup_last_pwd_change(context, entry, &last_pw_changed)) != 0)
2157                 goto cleanup;
2158
2159             if ((mask & KDB_PWD_EXPIRE_TIME_ATTR) == 1) {
2160                 if ((last_pw_changed + pw_max_life) < entry->pw_expiration)
2161                     entry->pw_expiration = last_pw_changed + pw_max_life;
2162             } else
2163                 entry->pw_expiration = last_pw_changed + pw_max_life;
2164         }
2165     }
2166     /* XXX so krb5_encode_princ_contents() will be happy */
2167     entry->len = KRB5_KDB_V1_BASE_LENGTH;
2168
2169 cleanup:
2170
2171     if (DN != NULL)
2172         ldap_memfree(DN);
2173
2174     if (userinfo_tl_data.tl_data_contents != NULL)
2175         free(userinfo_tl_data.tl_data_contents);
2176
2177     if (pwdpolicydn != NULL)
2178         free(pwdpolicydn);
2179
2180     if (polname != NULL)
2181         free(polname);
2182
2183     if (tktpolname != NULL)
2184         free (tktpolname);
2185
2186     if (policydn != NULL)
2187         free(policydn);
2188
2189     if (link_references) {
2190         int i;
2191         for (i=0; link_references[i] != NULL; ++i)
2192             free (link_references[i]);
2193         free (link_references);
2194     }
2195
2196     return (st);
2197 }
2198
2199 /*
2200  * Solaris libldap does not provide the following functions which are in
2201  * OpenLDAP.
2202  */
2203 #ifndef HAVE_LDAP_INITIALIZE
2204 int
2205 ldap_initialize(LDAP **ldp, char *url)
2206 {
2207     int rc = 0;
2208     LDAP *ld = NULL;
2209     LDAPURLDesc *ludp = NULL;
2210
2211     /* For now, we don't use any DN that may be provided.  And on
2212        Solaris (based on Mozilla's LDAP client code), we need the
2213        _nodn form to parse "ldap://host" without a trailing slash.
2214
2215        Also, this version won't handle an input string which contains
2216        multiple URLs, unlike the OpenLDAP ldap_initialize.  See
2217        https://bugzilla.mozilla.org/show_bug.cgi?id=353336#c1 .  */
2218 #ifdef HAVE_LDAP_URL_PARSE_NODN
2219     rc = ldap_url_parse_nodn(url, &ludp);
2220 #else
2221     rc = ldap_url_parse(url, &ludp);
2222 #endif
2223     if (rc == 0) {
2224
2225         ld = ldap_init(ludp->lud_host, ludp->lud_port);
2226         if (ld != NULL) {
2227             *ldp = ld;
2228 #if 0
2229             printf("lud_host %s lud_port %d\n", ludp->lud_host,
2230                    ludp->lud_port);
2231 #endif
2232         }
2233         else
2234             rc = KRB5_KDB_ACCESS_ERROR;
2235
2236         ldap_free_urldesc(ludp);
2237     }
2238     return rc;
2239 }
2240 #endif /* HAVE_LDAP_INITIALIZE */
2241
2242 #ifndef HAVE_LDAP_UNBIND_EXT_S
2243 int
2244 ldap_unbind_ext_s(LDAP *ld, LDAPControl **sctrls, LDAPControl **cctrls)
2245 {
2246     return ldap_unbind_ext(ld, sctrls, cctrls);
2247 }
2248 #endif /* HAVE_LDAP_UNBIND_EXT_S */