pull up r24622 from trunk
[krb5.git] / src / plugins / kdb / ldap / libkdb_ldap / ldap_create.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * lib/kdb/kdb_ldap/ldap_create.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 /*
33  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
34  * Use is subject to license terms.
35  */
36
37 #include "ldap_main.h"
38 #include "ldap_realm.h"
39 #include "ldap_principal.h"
40 #include "ldap_krbcontainer.h"
41 #include "ldap_err.h"
42
43 /*
44  * ******************************************************************************
45  * DAL functions
46  * ******************************************************************************
47  */
48
49 /*
50  * This function will create a krbcontainer and realm on the LDAP Server, with
51  * the specified attributes.
52  */
53 krb5_error_code
54 krb5_ldap_create(krb5_context context, char *conf_section, char **db_args)
55 {
56     krb5_error_code status = 0;
57     char  **t_ptr = db_args;
58     krb5_ldap_realm_params *rparams = NULL;
59     kdb5_dal_handle *dal_handle = NULL;
60     krb5_ldap_context *ldap_context=NULL;
61     krb5_boolean realm_obj_created = FALSE;
62     krb5_boolean krbcontainer_obj_created = FALSE;
63     krb5_ldap_krbcontainer_params kparams = {0};
64     int srv_cnt = 0;
65     int mask = 0;
66 #ifdef HAVE_EDIRECTORY
67     int i = 0, rightsmask = 0;
68 #endif
69
70     /* Clear the global error string */
71     krb5_clear_error_message(context);
72
73     ldap_context = malloc(sizeof(krb5_ldap_context));
74     if (ldap_context == NULL) {
75         status = ENOMEM;
76         goto cleanup;
77     }
78     memset(ldap_context, 0, sizeof(*ldap_context));
79
80     ldap_context->kcontext = context;
81
82     /* populate ldap_context with ldap specific options */
83     while (t_ptr && *t_ptr) {
84         char *opt = NULL, *val = NULL;
85
86         if ((status = krb5_ldap_get_db_opt(*t_ptr, &opt, &val)) != 0) {
87             goto cleanup;
88         }
89         if (opt && !strcmp(opt, "binddn")) {
90             if (ldap_context->bind_dn) {
91                 free (opt);
92                 free (val);
93                 status = EINVAL;
94                 krb5_set_error_message (context, status, "'binddn' missing");
95                 goto cleanup;
96             }
97             if (val == NULL) {
98                 status = EINVAL;
99                 krb5_set_error_message (context, status, "'binddn' value missing");
100                 free(opt);
101                 goto cleanup;
102             }
103             ldap_context->bind_dn = strdup(val);
104             if (ldap_context->bind_dn == NULL) {
105                 free (opt);
106                 free (val);
107                 status = ENOMEM;
108                 goto cleanup;
109             }
110         } else if (opt && !strcmp(opt, "nconns")) {
111             if (ldap_context->max_server_conns) {
112                 free (opt);
113                 free (val);
114                 status = EINVAL;
115                 krb5_set_error_message (context, status, "'nconns' missing");
116                 goto cleanup;
117             }
118             if (val == NULL) {
119                 status = EINVAL;
120                 krb5_set_error_message (context, status, "'nconns' value missing");
121                 free(opt);
122                 goto cleanup;
123             }
124             ldap_context->max_server_conns = atoi(val) ? atoi(val) : DEFAULT_CONNS_PER_SERVER;
125         } else if (opt && !strcmp(opt, "bindpwd")) {
126             if (ldap_context->bind_pwd) {
127                 free (opt);
128                 free (val);
129                 status = EINVAL;
130                 krb5_set_error_message (context, status, "'bindpwd' missing");
131                 goto cleanup;
132             }
133             if (val == NULL) {
134                 status = EINVAL;
135                 krb5_set_error_message (context, status, "'bindpwd' value missing");
136                 free(opt);
137                 goto cleanup;
138             }
139             ldap_context->bind_pwd = strdup(val);
140             if (ldap_context->bind_pwd == NULL) {
141                 free (opt);
142                 free (val);
143                 status = ENOMEM;
144                 goto cleanup;
145             }
146         } else if (opt && !strcmp(opt, "host")) {
147             if (val == NULL) {
148                 status = EINVAL;
149                 krb5_set_error_message (context, status, "'host' value missing");
150                 free(opt);
151                 goto cleanup;
152             }
153             if (ldap_context->server_info_list == NULL)
154                 ldap_context->server_info_list =
155                     (krb5_ldap_server_info **) calloc(SERV_COUNT+1, sizeof(krb5_ldap_server_info *));
156
157             if (ldap_context->server_info_list == NULL) {
158                 free (opt);
159                 free (val);
160                 status = ENOMEM;
161                 goto cleanup;
162             }
163
164             ldap_context->server_info_list[srv_cnt] =
165                 (krb5_ldap_server_info *) calloc(1, sizeof(krb5_ldap_server_info));
166             if (ldap_context->server_info_list[srv_cnt] == NULL) {
167                 free (opt);
168                 free (val);
169                 status = ENOMEM;
170                 goto cleanup;
171             }
172
173             ldap_context->server_info_list[srv_cnt]->server_status = NOTSET;
174
175             ldap_context->server_info_list[srv_cnt]->server_name = strdup(val);
176             if (ldap_context->server_info_list[srv_cnt]->server_name == NULL) {
177                 free (opt);
178                 free (val);
179                 status = ENOMEM;
180                 goto cleanup;
181             }
182
183             srv_cnt++;
184 #ifdef HAVE_EDIRECTORY
185         } else if (opt && !strcmp(opt, "cert")) {
186             if (val == NULL) {
187                 status = EINVAL;
188                 krb5_set_error_message (context, status, "'cert' value missing");
189                 free(opt);
190                 goto cleanup;
191             }
192
193             if (ldap_context->root_certificate_file == NULL) {
194                 ldap_context->root_certificate_file = strdup(val);
195                 if (ldap_context->root_certificate_file == NULL) {
196                     free (opt);
197                     free (val);
198                     status = ENOMEM;
199                     goto cleanup;
200                 }
201             } else {
202                 char *newstr;
203
204                 if (asprintf(&newstr, "%s %s",
205                              ldap_context->root_certificate_file, val) < 0) {
206                     free (opt);
207                     free (val);
208                     status = ENOMEM;
209                     goto cleanup;
210                 }
211                 ldap_context->root_certificate_file = newstr;
212             }
213 #endif
214         } else {
215             /* ignore hash argument. Might have been passed from create */
216             status = EINVAL;
217             if (opt && !strcmp(opt, "temporary")) {
218                 /*
219                  * temporary is passed in when kdb5_util load without -update is done.
220                  * This is unsupported by the LDAP plugin.
221                  */
222                 krb5_set_error_message (context, status,
223                                         "creation of LDAP entries aborted, plugin requires -update argument");
224             } else {
225                 krb5_set_error_message (context, status, "unknown option \'%s\'",
226                                         opt?opt:val);
227             }
228             free(opt);
229             free(val);
230             goto cleanup;
231         }
232
233         free(opt);
234         free(val);
235         t_ptr++;
236     }
237
238     dal_handle = context->dal_handle;
239     dal_handle->db_context = (kdb5_dal_handle *) ldap_context;
240
241     status = krb5_ldap_read_server_params(context, conf_section, KRB5_KDB_SRV_TYPE_ADMIN);
242     if (status) {
243         dal_handle->db_context = NULL;
244         prepend_err_str (context, "Error reading LDAP server params: ", status, status);
245         goto cleanup;
246     }
247     status = krb5_ldap_db_init(context, ldap_context);
248     if (status) {
249         goto cleanup;
250     }
251
252     /* read the kerberos container */
253     if ((status = krb5_ldap_read_krbcontainer_params(context,
254                                                      &(ldap_context->krbcontainer))) == KRB5_KDB_NOENTRY) {
255
256         /* Read the kerberos container location from configuration file */
257         if (ldap_context->conf_section) {
258             if ((status = profile_get_string(context->profile,
259                                              KDB_MODULE_SECTION, ldap_context->conf_section,
260                                              "ldap_kerberos_container_dn", NULL,
261                                              &kparams.DN)) != 0) {
262                 goto cleanup;
263             }
264         }
265         if (kparams.DN == NULL) {
266             if ((status = profile_get_string(context->profile,
267                                              KDB_MODULE_DEF_SECTION,
268                                              "ldap_kerberos_container_dn", NULL,
269                                              NULL, &kparams.DN)) != 0) {
270                 goto cleanup;
271             }
272         }
273
274         /* create the kerberos container */
275         status = krb5_ldap_create_krbcontainer(context,
276                                                ((kparams.DN != NULL) ? &kparams : NULL));
277         if (status)
278             goto cleanup;
279
280         krbcontainer_obj_created = TRUE;
281
282         status = krb5_ldap_read_krbcontainer_params(context,
283                                                     &(ldap_context->krbcontainer));
284         if (status)
285             goto cleanup;
286
287     } else if (status) {
288         goto cleanup;
289     }
290
291     rparams = (krb5_ldap_realm_params *) malloc(sizeof(krb5_ldap_realm_params));
292     if (rparams == NULL) {
293         status = ENOMEM;
294         goto cleanup;
295     }
296     memset(rparams, 0, sizeof(*rparams));
297     rparams->realm_name = strdup(context->default_realm);
298     if (rparams->realm_name == NULL) {
299         status = ENOMEM;
300         goto cleanup;
301     }
302
303     if ((status = krb5_ldap_create_realm(context, rparams, mask)))
304         goto cleanup;
305
306     /* We just created the Realm container. Here starts our transaction tracking */
307     realm_obj_created = TRUE;
308
309     /* verify realm object */
310     if ((status = krb5_ldap_read_realm_params(context,
311                                               rparams->realm_name,
312                                               &(ldap_context->lrparams),
313                                               &mask)))
314         goto cleanup;
315
316 #ifdef HAVE_EDIRECTORY
317     if ((mask & LDAP_REALM_KDCSERVERS) || (mask & LDAP_REALM_ADMINSERVERS) ||
318         (mask & LDAP_REALM_PASSWDSERVERS)) {
319
320         rightsmask =0;
321         rightsmask |= LDAP_REALM_RIGHTS;
322         rightsmask |= LDAP_SUBTREE_RIGHTS;
323         if ((rparams != NULL) && (rparams->kdcservers != NULL)) {
324             for (i=0; (rparams->kdcservers[i] != NULL); i++) {
325                 if ((status=krb5_ldap_add_service_rights(context,
326                                                          LDAP_KDC_SERVICE, rparams->kdcservers[i],
327                                                          rparams->realm_name, rparams->subtree, rparams->containerref, rightsmask)) != 0) {
328                     goto cleanup;
329                 }
330             }
331         }
332
333         rightsmask = 0;
334         rightsmask |= LDAP_REALM_RIGHTS;
335         rightsmask |= LDAP_SUBTREE_RIGHTS;
336         if ((rparams != NULL) && (rparams->adminservers != NULL)) {
337             for (i=0; (rparams->adminservers[i] != NULL); i++) {
338                 if ((status=krb5_ldap_add_service_rights(context,
339                                                          LDAP_ADMIN_SERVICE, rparams->adminservers[i],
340                                                          rparams->realm_name, rparams->subtree, rparams->containerref, rightsmask)) != 0) {
341                     goto cleanup;
342                 }
343             }
344         }
345
346         rightsmask = 0;
347         rightsmask |= LDAP_REALM_RIGHTS;
348         rightsmask |= LDAP_SUBTREE_RIGHTS;
349         if ((rparams != NULL) && (rparams->passwdservers != NULL)) {
350             for (i=0; (rparams->passwdservers[i] != NULL); i++) {
351                 if ((status=krb5_ldap_add_service_rights(context,
352                                                          LDAP_PASSWD_SERVICE, rparams->passwdservers[i],
353                                                          rparams->realm_name, rparams->subtree, rparams->containerref, rightsmask)) != 0) {
354                     goto cleanup;
355                 }
356             }
357         }
358     }
359 #endif
360
361 cleanup:
362
363     /* If the krbcontainer/realm creation is not complete, do the roll-back here */
364     if ((krbcontainer_obj_created) && (!realm_obj_created)) {
365         int rc;
366         rc = krb5_ldap_delete_krbcontainer(context,
367                                            ((kparams.DN != NULL) ? &kparams : NULL));
368         krb5_set_error_message(context, rc,
369                                "could not complete roll-back, error deleting Kerberos Container");
370     }
371
372     /* should call krb5_ldap_free_krbcontainer_params() but can't */
373     if (kparams.DN != NULL)
374         krb5_xfree(kparams.DN);
375
376     if (rparams)
377         krb5_ldap_free_realm_params(rparams);
378
379     return(status);
380 }