86fa4d1e5e9ac2027d046ef12690ecc10edc5ccf
[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
1534 /*
1535  * Get the number of times an object has been referred to in a realm. this is
1536  * needed to find out if deleting the attribute will cause dangling links.
1537  *
1538  * An LDAP handle may be optionally specified to prevent race condition - there
1539  * are a limited number of LDAP handles.
1540  */
1541 krb5_error_code
1542 krb5_ldap_get_reference_count(krb5_context context, char *dn, char *refattr,
1543                               int *count, LDAP *ld)
1544 {
1545     int                         st = 0, tempst = 0, gothandle = 0;
1546     unsigned int                i, ntrees;
1547     char                        *refcntattr[2];
1548     char                        *filter = NULL;
1549     char                        **subtree = NULL, *ptr = NULL;
1550     kdb5_dal_handle             *dal_handle = NULL;
1551     krb5_ldap_context           *ldap_context = NULL;
1552     krb5_ldap_server_handle     *ldap_server_handle = NULL;
1553     LDAPMessage                 *result = NULL;
1554
1555
1556     if (dn == NULL || refattr == NULL) {
1557         st = EINVAL;
1558         goto cleanup;
1559     }
1560
1561     SETUP_CONTEXT();
1562     if (ld == NULL) {
1563         GET_HANDLE();
1564         gothandle = 1;
1565     }
1566
1567     refcntattr [0] = refattr;
1568     refcntattr [1] = NULL;
1569
1570     ptr = ldap_filter_correct (dn);
1571     if (ptr == NULL) {
1572         st = ENOMEM;
1573         goto cleanup;
1574     }
1575
1576     if (asprintf (&filter, "%s=%s", refattr, ptr) < 0) {
1577         filter = NULL;
1578         st = ENOMEM;
1579         goto cleanup;
1580     }
1581
1582     if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0)
1583         goto cleanup;
1584
1585     for (i = 0, *count = 0; i < ntrees; i++) {
1586         int n;
1587
1588         LDAP_SEARCH(subtree[i],
1589                     LDAP_SCOPE_SUBTREE,
1590                     filter,
1591                     refcntattr);
1592         n = ldap_count_entries (ld, result);
1593         if (n == -1) {
1594             int ret, errcode = 0;
1595             ret = ldap_parse_result (ld, result, &errcode, NULL, NULL, NULL, NULL, 0);
1596             if (ret != LDAP_SUCCESS)
1597                 errcode = ret;
1598             st = translate_ldap_error (errcode, OP_SEARCH);
1599             goto cleanup;
1600         }
1601
1602         ldap_msgfree(result);
1603         result = NULL;
1604
1605         *count += n;
1606     }
1607
1608 cleanup:
1609     if (filter != NULL)
1610         free (filter);
1611
1612     if (result != NULL)
1613         ldap_msgfree (result);
1614
1615     if (subtree != NULL) {
1616         for (i = 0; i < ntrees; i++)
1617             free (subtree[i]);
1618         free (subtree);
1619     }
1620
1621     if (ptr != NULL)
1622         free (ptr);
1623
1624     if (gothandle == 1)
1625         krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
1626
1627     return st;
1628 }
1629
1630 /*
1631  * For now, policy objects are expected to be directly under the realm
1632  * container.
1633  */
1634 krb5_error_code
1635 krb5_ldap_policydn_to_name(krb5_context context, char *policy_dn, char **name)
1636 {
1637     int len1, len2;
1638     krb5_error_code             st = 0;
1639     kdb5_dal_handle             *dal_handle=NULL;
1640     krb5_ldap_context           *ldap_context=NULL;
1641
1642     SETUP_CONTEXT();
1643
1644     if (ldap_context->lrparams->realmdn == NULL) {
1645         st = EINVAL;
1646         goto cleanup;
1647     }
1648
1649     len1 = strlen (ldap_context->lrparams->realmdn);
1650     len2 = strlen (policy_dn);
1651     if (len1 == 0 || len2 == 0 || len1 > len2) {
1652         st = EINVAL;
1653         goto cleanup;
1654     }
1655
1656     if (strcmp (ldap_context->lrparams->realmdn, policy_dn + (len2 - len1)) != 0) {
1657         st = EINVAL;
1658         goto cleanup;
1659     }
1660
1661 #if defined HAVE_LDAP_STR2DN
1662     {
1663         char *rdn;
1664         LDAPDN dn;
1665         rdn = strndup(policy_dn, len2 - len1 - 1); /* 1 character for ',' */
1666
1667         if (ldap_str2dn (rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC) != 0) {
1668             st = EINVAL;
1669             goto cleanup;
1670         }
1671         if (dn[0] == NULL || dn[1] != NULL)
1672             st = EINVAL;
1673         else if (strcasecmp (dn[0][0]->la_attr.bv_val, "cn") != 0)
1674             st = EINVAL;
1675         else {
1676             *name = strndup(dn[0][0]->la_value.bv_val, dn[0][0]->la_value.bv_len);
1677             if (*name == NULL)
1678                 st = EINVAL;
1679         }
1680
1681         ldap_memfree (dn);
1682     }
1683 #elif defined HAVE_LDAP_EXPLODE_DN
1684     {
1685         char **parsed_dn;
1686
1687         /* 1 = return DN components without type prefix */
1688         parsed_dn = ldap_explode_dn(policy_dn, 1);
1689         if (parsed_dn == NULL) {
1690             st = EINVAL;
1691         } else {
1692             *name = strdup(parsed_dn[0]);
1693             if (*name == NULL)
1694                 st = EINVAL;
1695
1696             ldap_value_free(parsed_dn);
1697         }
1698     }
1699 #else
1700     st = EINVAL;
1701 #endif
1702
1703 cleanup:
1704     return st;
1705 }
1706
1707 krb5_error_code
1708 krb5_ldap_name_to_policydn(krb5_context context, char *name, char **policy_dn)
1709 {
1710     int                         len;
1711     char                        *ptr = NULL;
1712     krb5_error_code             st = 0;
1713     kdb5_dal_handle             *dal_handle=NULL;
1714     krb5_ldap_context           *ldap_context=NULL;
1715
1716     *policy_dn = NULL;
1717
1718     /* validate the input parameters */
1719     if (name == NULL) {
1720         st = EINVAL;
1721         goto cleanup;
1722     }
1723
1724     /* Used for removing policy reference from an object */
1725     if (name[0] == '\0') {
1726         if ((*policy_dn = strdup ("")) == NULL)
1727             st = ENOMEM;
1728         goto cleanup;
1729     }
1730
1731     SETUP_CONTEXT();
1732
1733     if (ldap_context->lrparams->realmdn == NULL) {
1734         st = EINVAL;
1735         goto cleanup;
1736     }
1737     len = strlen (ldap_context->lrparams->realmdn);
1738
1739     ptr = ldap_filter_correct (name);
1740     if (ptr == NULL) {
1741         st = ENOMEM;
1742         goto cleanup;
1743     }
1744     len += strlen (ptr);
1745
1746     len += sizeof ("cn=") + 3;
1747
1748     *policy_dn = (char *) malloc (len);
1749     if (*policy_dn == NULL) {
1750         st = ENOMEM;
1751         goto cleanup;
1752     }
1753
1754     sprintf (*policy_dn, "cn=%s,%s", ptr, ldap_context->lrparams->realmdn);
1755
1756 cleanup:
1757     if (ptr != NULL)
1758         free (ptr);
1759     return st;
1760 }
1761
1762 /* remove overlapping and repeated subtree entries from the list of subtrees */
1763 static krb5_error_code
1764 remove_overlapping_subtrees(char **listin, char **listop, int *subtcount,
1765                             int sscope)
1766 {
1767     int     slen=0, k=0, j=0, lendiff=0;
1768     int     count = *subtcount;
1769     char    **subtree = listop;
1770
1771     slen = count-1;
1772     for (k=0; k<=slen && listin[k]!=NULL ; k++) {
1773         for (j=k+1; j<=slen && listin[j]!=NULL ;j++) {
1774             lendiff = strlen(listin[k]) - strlen(listin[j]);
1775             if (sscope == 2) {
1776                 if ((lendiff > 0) && (strcasecmp((listin[k])+lendiff, listin[j])==0)) {
1777                     if (k != slen) {
1778                         free(listin[k]);
1779                         listin[k] = listin[slen];
1780                         listin[slen] = NULL;
1781                     } else {
1782                         free(listin[k]);
1783                         listin[k] = NULL;
1784                     }
1785                     slen-=1;
1786                     k-=1;
1787                     break;
1788                 } else if ((lendiff < 0) && (strcasecmp((listin[j])+abs(lendiff), listin[k])==0)) {
1789                     if (j != slen) {
1790                         free(listin[j]);
1791                         listin[j] = listin[slen];
1792                         listin[slen]=NULL;
1793                     } else {
1794                         free(listin[j]);
1795                         listin[j] = NULL;
1796                     }
1797                     slen-=1;
1798                     j-=1;
1799                 }
1800             }
1801             if ((lendiff == 0) && (strcasecmp(listin[j], listin[k])==0)) {
1802                 if (j != slen) {
1803                     free(listin[j]);
1804                     listin[j] = listin[slen];
1805                     listin[slen]=NULL;
1806                 } else {
1807                     free(listin[j]);
1808                     listin[j] = NULL;
1809                 }
1810                 slen -=1;
1811                 j-=1;
1812             }
1813         }
1814     }
1815     *subtcount=slen+1;
1816     for (k=0; k<*subtcount && listin[k]!=NULL; k++) {
1817         subtree[k] = strdup(listin[k]);
1818         if (subtree[k] == NULL) {
1819             return ENOMEM;
1820         }
1821     }
1822     return 0;
1823 }
1824
1825 /*
1826  * Fill out a krb5_db_entry princ entry struct given a LDAP message containing
1827  * the results of a principal search of the directory.
1828  */
1829 krb5_error_code
1830 populate_krb5_db_entry(krb5_context context, krb5_ldap_context *ldap_context,
1831                        LDAP *ld, LDAPMessage *ent, krb5_const_principal princ,
1832                        krb5_db_entry *entry)
1833 {
1834     krb5_error_code st = 0;
1835     unsigned int    mask = 0;
1836     krb5_boolean    attr_present = FALSE;
1837     char            **values = NULL, *policydn = NULL, *pwdpolicydn = NULL;
1838     char            *polname = NULL, *tktpolname = NULL;
1839     struct berval   **bvalues = NULL;
1840     krb5_tl_data    userinfo_tl_data = {0};
1841     char            **link_references = NULL;
1842     char *DN = NULL;
1843
1844     if (princ == NULL) {
1845         /* XXX WAF probably should just extract princ from ldap result */
1846         st = EINVAL;
1847         goto cleanup;
1848     } else {
1849         if ((st=krb5_copy_principal(context, princ, &(entry->princ))) != 0)
1850             goto cleanup;
1851     }
1852     /* get the associated directory user information */
1853     if ((values = ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
1854         int i, pcount=0, kerberos_principal_object_type=0;
1855         char *user;
1856
1857         if ((st=krb5_unparse_name(context, princ, &user)) != 0)
1858             goto cleanup;
1859
1860         for (i=0; values[i] != NULL; ++i) {
1861             if (strcasecmp(values[i], user) == 0) {
1862                 pcount = ldap_count_values(values);
1863                 break;
1864             }
1865         }
1866         ldap_value_free(values);
1867         free(user);
1868
1869         if ((DN = ldap_get_dn(ld, ent)) == NULL) {
1870             ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
1871             st = set_ldap_error(context, st, 0);
1872             goto cleanup;
1873         }
1874
1875         if ((values=ldap_get_values(ld, ent, "objectclass")) != NULL) {
1876             for (i=0; values[i] != NULL; ++i)
1877                 if (strcasecmp(values[i], "krbprincipal") == 0) {
1878                     kerberos_principal_object_type = KDB_STANDALONE_PRINCIPAL_OBJECT;
1879                     if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE,
1880                                           &kerberos_principal_object_type)) != 0)
1881                         goto cleanup;
1882                     break;
1883                 }
1884             ldap_value_free(values);
1885         }
1886
1887         /* add principalcount, DN and principaltype user information to tl_data */
1888         if (((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount)) != 0) ||
1889             ((st=store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, DN)) != 0))
1890             goto cleanup;
1891     }
1892
1893     /* read all the kerberos attributes */
1894
1895     /* KRBLASTSUCCESSFULAUTH */
1896     if ((st=krb5_ldap_get_time(ld, ent, "krbLastSuccessfulAuth",
1897                                &(entry->last_success), &attr_present)) != 0)
1898         goto cleanup;
1899     if (attr_present == TRUE)
1900         mask |= KDB_LAST_SUCCESS_ATTR;
1901
1902     /* KRBLASTFAILEDAUTH */
1903     if ((st=krb5_ldap_get_time(ld, ent, "krbLastFailedAuth",
1904                                &(entry->last_failed), &attr_present)) != 0)
1905         goto cleanup;
1906     if (attr_present == TRUE)
1907         mask |= KDB_LAST_FAILED_ATTR;
1908
1909     /* KRBLOGINFAILEDCOUNT */
1910     if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount",
1911                             &(entry->fail_auth_count)) == 0)
1912         mask |= KDB_FAIL_AUTH_COUNT_ATTR;
1913
1914     /* KRBMAXTICKETLIFE */
1915     if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &(entry->max_life)) == 0)
1916         mask |= KDB_MAX_LIFE_ATTR;
1917
1918     /* KRBMAXRENEWABLEAGE */
1919     if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage",
1920                             &(entry->max_renewable_life)) == 0)
1921         mask |= KDB_MAX_RLIFE_ATTR;
1922
1923     /* KRBTICKETFLAGS */
1924     if (krb5_ldap_get_value(ld, ent, "krbticketflags", &(entry->attributes)) == 0)
1925         mask |= KDB_TKT_FLAGS_ATTR;
1926
1927     /* PRINCIPAL EXPIRATION TIME */
1928     if ((st=krb5_ldap_get_time(ld, ent, "krbprincipalexpiration", &(entry->expiration),
1929                                &attr_present)) != 0)
1930         goto cleanup;
1931     if (attr_present == TRUE)
1932         mask |= KDB_PRINC_EXPIRE_TIME_ATTR;
1933
1934     /* PASSWORD EXPIRATION TIME */
1935     if ((st=krb5_ldap_get_time(ld, ent, "krbpasswordexpiration", &(entry->pw_expiration),
1936                                &attr_present)) != 0)
1937         goto cleanup;
1938     if (attr_present == TRUE)
1939         mask |= KDB_PWD_EXPIRE_TIME_ATTR;
1940
1941     /* KRBPOLICYREFERENCE */
1942
1943     if ((st=krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn,
1944                                  &attr_present)) != 0)
1945         goto cleanup;
1946     if (attr_present == TRUE) {
1947         mask |= KDB_POL_REF_ATTR;
1948         /* Ensure that the policy is inside the realm container */
1949         if ((st = krb5_ldap_policydn_to_name (context, policydn, &tktpolname)) != 0)
1950             goto cleanup;
1951     }
1952
1953     /* KRBPWDPOLICYREFERENCE */
1954     if ((st=krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn,
1955                                  &attr_present)) != 0)
1956         goto cleanup;
1957     if (attr_present == TRUE) {
1958         krb5_tl_data  kadm_tl_data;
1959
1960         mask |= KDB_PWD_POL_REF_ATTR;
1961
1962         /* Ensure that the policy is inside the realm container */
1963         if ((st = krb5_ldap_policydn_to_name (context, pwdpolicydn, &polname)) != 0)
1964             goto cleanup;
1965
1966         if ((st = krb5_update_tl_kadm_data(polname, &kadm_tl_data)) != 0) {
1967             goto cleanup;
1968         }
1969         krb5_dbe_update_tl_data(context, entry, &kadm_tl_data);
1970     }
1971
1972     /* KRBSECRETKEY */
1973     if ((bvalues=ldap_get_values_len(ld, ent, "krbprincipalkey")) != NULL) {
1974         krb5_kvno mkvno = 0;
1975
1976         mask |= KDB_SECRET_KEY_ATTR;
1977         if ((st=krb5_decode_krbsecretkey(context, entry, bvalues, &userinfo_tl_data, &mkvno)) != 0)
1978             goto cleanup;
1979         if (mkvno != 0) {
1980             /* don't add the tl data if mkvno == 0 */
1981             if ((st=krb5_dbe_update_mkvno(context, entry, mkvno)) != 0)
1982                 goto cleanup;
1983         }
1984     }
1985
1986     /* LAST PASSWORD CHANGE */
1987     {
1988         krb5_timestamp lstpwdchng=0;
1989         if ((st=krb5_ldap_get_time(ld, ent, "krbLastPwdChange",
1990                                    &lstpwdchng, &attr_present)) != 0)
1991             goto cleanup;
1992         if (attr_present == TRUE) {
1993             if ((st=krb5_dbe_update_last_pwd_change(context, entry,
1994                                                     lstpwdchng)))
1995                 goto cleanup;
1996             mask |= KDB_LAST_PWD_CHANGE_ATTR;
1997         }
1998     }
1999
2000     /* LAST ADMIN UNLOCK */
2001     {
2002         krb5_timestamp unlock_time=0;
2003         if ((st=krb5_ldap_get_time(ld, ent, "krbLastAdminUnlock",
2004                                    &unlock_time, &attr_present)) != 0)
2005             goto cleanup;
2006         if (attr_present == TRUE) {
2007             if ((st=krb5_dbe_update_last_admin_unlock(context, entry,
2008                                                       unlock_time)))
2009                 goto cleanup;
2010             mask |= KDB_LAST_ADMIN_UNLOCK_ATTR;
2011         }
2012     }
2013
2014     /* ALLOWED TO DELEGATE TO */
2015     {
2016         char **a2d2 = NULL;
2017         int i;
2018         krb5_tl_data **tlp;
2019
2020         st = krb5_ldap_get_strings(ld, ent, "krbAllowedToDelegateTo",
2021                                    &a2d2, &attr_present);
2022         if (st != 0)
2023             goto cleanup;
2024
2025         if (attr_present == TRUE) {
2026             for (tlp = &entry->tl_data; *tlp; tlp = &(*tlp)->tl_data_next)
2027                 ;
2028             for (i = 0; a2d2[i] != NULL; i++) {
2029                 krb5_tl_data *tl = k5alloc(sizeof(*tl), &st);
2030                 if (st != 0) {
2031                     ldap_value_free(a2d2);
2032                     goto cleanup;
2033                 }
2034                 tl->tl_data_type = KRB5_TL_CONSTRAINED_DELEGATION_ACL;
2035                 tl->tl_data_length = strlen(a2d2[i]);
2036                 tl->tl_data_contents = (krb5_octet *)strdup(a2d2[i]);
2037                 if (tl->tl_data_contents == NULL) {
2038                     st = ENOMEM;
2039                     ldap_value_free(a2d2);
2040                     free(tl);
2041                     goto cleanup;
2042                 }
2043                 tl->tl_data_next = NULL;
2044                 *tlp = tl;
2045                 tlp = &tl->tl_data_next;
2046             }
2047             ldap_value_free(a2d2);
2048         }
2049     }
2050
2051     /* KRBOBJECTREFERENCES */
2052     {
2053         int i=0;
2054
2055         if ((st = krb5_ldap_get_strings(ld, ent, "krbobjectreferences",
2056                                         &link_references, &attr_present)) != 0)
2057             goto cleanup;
2058         if (link_references != NULL) {
2059             for (i=0; link_references[i] != NULL; ++i) {
2060                 if ((st = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN,
2061                                         link_references[i])) != 0)
2062                     goto cleanup;
2063             }
2064         }
2065     }
2066
2067     /* Set tl_data */
2068     {
2069         int i;
2070         struct berval **ber_tl_data = NULL;
2071         krb5_tl_data *ptr = NULL;
2072
2073         if ((ber_tl_data = ldap_get_values_len (ld, ent, "krbExtraData")) != NULL) {
2074             for (i = 0; ber_tl_data[i] != NULL; i++) {
2075                 if ((st = berval2tl_data (ber_tl_data[i] , &ptr)) != 0)
2076                     break;
2077                 if ((st = krb5_dbe_update_tl_data(context, entry, ptr)) != 0)
2078                     break;
2079             }
2080             ldap_value_free_len (ber_tl_data);
2081             if (st != 0)
2082                 goto cleanup;
2083             mask |= KDB_EXTRA_DATA_ATTR;
2084         }
2085     }
2086
2087     /* update the mask of attributes present on the directory object to the tl_data */
2088     if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask)) != 0)
2089         goto cleanup;
2090     if ((st=krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data)) != 0)
2091         goto cleanup;
2092
2093 #ifdef HAVE_EDIRECTORY
2094     {
2095         krb5_timestamp              expiretime=0;
2096         char                        *is_login_disabled=NULL;
2097
2098         /* LOGIN EXPIRATION TIME */
2099         if ((st=krb5_ldap_get_time(ld, ent, "loginexpirationtime", &expiretime,
2100                                    &attr_present)) != 0)
2101             goto cleanup;
2102
2103         if (attr_present == TRUE) {
2104             if (mask & KDB_PRINC_EXPIRE_TIME_ATTR) {
2105                 if (expiretime < entry->expiration)
2106                     entry->expiration = expiretime;
2107             } else {
2108                 entry->expiration = expiretime;
2109             }
2110         }
2111
2112         /* LOGIN DISABLED */
2113         if ((st=krb5_ldap_get_string(ld, ent, "logindisabled", &is_login_disabled,
2114                                      &attr_present)) != 0)
2115             goto cleanup;
2116         if (attr_present == TRUE) {
2117             if (strcasecmp(is_login_disabled, "TRUE")== 0)
2118                 entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
2119             free (is_login_disabled);
2120         }
2121     }
2122 #endif
2123
2124     if ((st=krb5_read_tkt_policy (context, ldap_context, entry, tktpolname)) !=0)
2125         goto cleanup;
2126
2127     /* We already know that the policy is inside the realm container. */
2128     if (polname) {
2129         osa_policy_ent_t   pwdpol;
2130         krb5_timestamp     last_pw_changed;
2131         krb5_ui_4          pw_max_life;
2132
2133         memset(&pwdpol, 0, sizeof(pwdpol));
2134
2135         if ((st=krb5_ldap_get_password_policy(context, polname, &pwdpol)) != 0)
2136             goto cleanup;
2137         pw_max_life = pwdpol->pw_max_life;
2138         free (pwdpol);
2139
2140         if (pw_max_life > 0) {
2141             if ((st=krb5_dbe_lookup_last_pwd_change(context, entry, &last_pw_changed)) != 0)
2142                 goto cleanup;
2143
2144             if (mask & KDB_PWD_EXPIRE_TIME_ATTR) {
2145                 if ((last_pw_changed + pw_max_life) < entry->pw_expiration)
2146                     entry->pw_expiration = last_pw_changed + pw_max_life;
2147             } else
2148                 entry->pw_expiration = last_pw_changed + pw_max_life;
2149         }
2150     }
2151     /* XXX so krb5_encode_princ_contents() will be happy */
2152     entry->len = KRB5_KDB_V1_BASE_LENGTH;
2153
2154 cleanup:
2155
2156     if (DN != NULL)
2157         ldap_memfree(DN);
2158
2159     if (userinfo_tl_data.tl_data_contents != NULL)
2160         free(userinfo_tl_data.tl_data_contents);
2161
2162     if (pwdpolicydn != NULL)
2163         free(pwdpolicydn);
2164
2165     if (polname != NULL)
2166         free(polname);
2167
2168     if (tktpolname != NULL)
2169         free (tktpolname);
2170
2171     if (policydn != NULL)
2172         free(policydn);
2173
2174     if (link_references) {
2175         int i;
2176         for (i=0; link_references[i] != NULL; ++i)
2177             free (link_references[i]);
2178         free (link_references);
2179     }
2180
2181     return (st);
2182 }
2183
2184 /*
2185  * Solaris libldap does not provide the following functions which are in
2186  * OpenLDAP.
2187  */
2188 #ifndef HAVE_LDAP_INITIALIZE
2189 int
2190 ldap_initialize(LDAP **ldp, char *url)
2191 {
2192     int rc = 0;
2193     LDAP *ld = NULL;
2194     LDAPURLDesc *ludp = NULL;
2195
2196     /* For now, we don't use any DN that may be provided.  And on
2197        Solaris (based on Mozilla's LDAP client code), we need the
2198        _nodn form to parse "ldap://host" without a trailing slash.
2199
2200        Also, this version won't handle an input string which contains
2201        multiple URLs, unlike the OpenLDAP ldap_initialize.  See
2202        https://bugzilla.mozilla.org/show_bug.cgi?id=353336#c1 .  */
2203 #ifdef HAVE_LDAP_URL_PARSE_NODN
2204     rc = ldap_url_parse_nodn(url, &ludp);
2205 #else
2206     rc = ldap_url_parse(url, &ludp);
2207 #endif
2208     if (rc == 0) {
2209
2210         ld = ldap_init(ludp->lud_host, ludp->lud_port);
2211         if (ld != NULL) {
2212             *ldp = ld;
2213 #if 0
2214             printf("lud_host %s lud_port %d\n", ludp->lud_host,
2215                    ludp->lud_port);
2216 #endif
2217         }
2218         else
2219             rc = KRB5_KDB_ACCESS_ERROR;
2220
2221         ldap_free_urldesc(ludp);
2222     }
2223     return rc;
2224 }
2225 #endif /* HAVE_LDAP_INITIALIZE */
2226
2227 #ifndef HAVE_LDAP_UNBIND_EXT_S
2228 int
2229 ldap_unbind_ext_s(LDAP *ld, LDAPControl **sctrls, LDAPControl **cctrls)
2230 {
2231     return ldap_unbind_ext(ld, sctrls, cctrls);
2232 }
2233 #endif /* HAVE_LDAP_UNBIND_EXT_S */