1 /*
2  * lib/kdb/kdb_ldap/ldap_create.c
3  *
4  * Copyright (c) 2004-2005, Novell, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  *   * Redistributions of source code must retain the above copyright notice,
11  *       this list of conditions and the following disclaimer.
12  *   * Redistributions in binary form must reproduce the above copyright
13  *       notice, this list of conditions and the following disclaimer in the
14  *       documentation and/or other materials provided with the distribution.
15  *   * The copyright holder's name is not used to endorse or promote products
16  *       derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 /*
32  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
33  * Use is subject to license terms.
34  */
35 
36 #include "ldap_main.h"
37 #include "ldap_realm.h"
38 #include "ldap_principal.h"
39 #include "ldap_krbcontainer.h"
40 #include "ldap_err.h"
41 #include <libintl.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
krb5_ldap_create(krb5_context context,char * conf_section,char ** db_args)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, gettext("'binddn' missing"));
95 		goto cleanup;
96 	    }
97 	    if (val == NULL) {
98 		status = EINVAL;
99 		krb5_set_error_message (context, status, gettext("'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, gettext("'nconns' missing"));
116 		goto cleanup;
117 	    }
118 	    if (val == NULL) {
119 		status = EINVAL;
120 		krb5_set_error_message (context, status, gettext("'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, gettext("'bindpwd' missing"));
131 		goto cleanup;
132 	    }
133 	    if (val == NULL) {
134 		status = EINVAL;
135 		krb5_set_error_message (context, status, gettext("'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, gettext("'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, gettext("'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 		void *tmp=NULL;
203 		char *oldstr = NULL;
204 		unsigned int len=0;
205 
206 		oldstr = strdup(ldap_context->root_certificate_file);
207 		if (oldstr == NULL) {
208 		    free (opt);
209 		    free (val);
210 		    status = ENOMEM;
211 		    goto cleanup;
212 		}
213 
214 		tmp = ldap_context->root_certificate_file;
215 		len = strlen(ldap_context->root_certificate_file) + 2 + strlen(val);
216 		ldap_context->root_certificate_file = realloc(ldap_context->root_certificate_file,
217 							      len);
218 		if (ldap_context->root_certificate_file == NULL) {
219 		    free (tmp);
220 		    free (opt);
221 		    free (val);
222 		    status = ENOMEM;
223 		    goto cleanup;
224 		}
225 		memset(ldap_context->root_certificate_file, 0, len);
226 		sprintf(ldap_context->root_certificate_file,"%s %s", oldstr, val);
227 		free (oldstr);
228 	    }
229 #endif
230 	} else {
231 	/* ignore hash argument. Might have been passed from create */
232 	    status = EINVAL;
233 	    if (opt && !strcmp(opt, "temporary")) {
234 		/*
235 		 * temporary is passed in when kdb5_util load without -update is done.
236 		 * This is unsupported by the LDAP plugin.
237 		 */
238 		krb5_set_error_message (context, status,
239 		    gettext("creation of LDAP entries aborted, plugin requires -update argument"));
240 	    } else {
241 		krb5_set_error_message (context, status, gettext("unknown option \'%s\'"),
242 					opt?opt:val);
243 	    }
244 	    free(opt);
245 	    free(val);
246 	    goto cleanup;
247 	}
248 
249 	free(opt);
250 	free(val);
251 	t_ptr++;
252     }
253 
254     dal_handle = (kdb5_dal_handle *) context->db_context;
255     dal_handle->db_context = (kdb5_dal_handle *) ldap_context;
256 
257     status = krb5_ldap_read_server_params(context, conf_section, KRB5_KDB_SRV_TYPE_ADMIN);
258     if (status) {
259 	dal_handle->db_context = NULL;
260 	prepend_err_str (context, gettext("Error reading LDAP server params: "), status, status);
261 	goto cleanup;
262     }
263     status = krb5_ldap_db_init(context, ldap_context);
264     if (status) {
265 	goto cleanup;
266     }
267 
268     /* read the kerberos container */
269     if ((status = krb5_ldap_read_krbcontainer_params(context,
270 			    &(ldap_context->krbcontainer))) == KRB5_KDB_NOENTRY) {
271 
272 	/* Read the kerberos container location from configuration file */
273 	if (ldap_context->conf_section) {
274 	    if ((status = profile_get_string(context->profile,
275 					   KDB_MODULE_SECTION, ldap_context->conf_section,
276 					   "ldap_kerberos_container_dn", NULL,
277 					   &kparams.DN)) != 0) {
278 		goto cleanup;
279 	    }
280 	}
281 	if (kparams.DN == NULL) {
282 	    if ((status = profile_get_string(context->profile,
283 					   KDB_MODULE_DEF_SECTION,
284 					   "ldap_kerberos_container_dn", NULL,
285 					   NULL, &kparams.DN)) != 0) {
286 		goto cleanup;
287 	    }
288 	}
289 
290 	/* create the kerberos container */
291 	status = krb5_ldap_create_krbcontainer(context,
292 					       ((kparams.DN != NULL) ? &kparams : NULL));
293 	if (status)
294 	    goto cleanup;
295 
296 	krbcontainer_obj_created = TRUE;
297 
298 	status = krb5_ldap_read_krbcontainer_params(context,
299 						    &(ldap_context->krbcontainer));
300 	if (status) {
301 	    krb5_set_error_message(context, status, gettext("while reading kerberos container information"));
302 	    goto cleanup;
303 	}
304 
305     } else if (status) {
306 	krb5_set_error_message(context, status, gettext("while reading kerberos container information"));
307 	goto cleanup;
308     }
309 
310     rparams = (krb5_ldap_realm_params *) malloc(sizeof(krb5_ldap_realm_params));
311     if (rparams == NULL) {
312 	status = ENOMEM;
313 	goto cleanup;
314     }
315     memset(rparams, 0, sizeof(*rparams));
316     rparams->realm_name = strdup(context->default_realm);
317     if (rparams->realm_name == NULL) {
318 	status = ENOMEM;
319 	goto cleanup;
320     }
321 
322     if ((status = krb5_ldap_create_realm(context, rparams, mask))) {
323 	krb5_set_error_message(context, status, gettext("while creating realm object entry"));
324 	goto cleanup;
325     }
326 
327     /* We just created the Realm container. Here starts our transaction tracking */
328     realm_obj_created = TRUE;
329 
330     /* verify realm object */
331     if ((status = krb5_ldap_read_realm_params(context,
332 					      rparams->realm_name,
333 					      &(ldap_context->lrparams),
334 					      &mask))) {
335 	krb5_set_error_message(context, status, gettext("while reading realm object entry"));
336 	goto cleanup;
337     }
338 
339 #ifdef HAVE_EDIRECTORY
340     if ((mask & LDAP_REALM_KDCSERVERS) || (mask & LDAP_REALM_ADMINSERVERS) ||
341 	(mask & LDAP_REALM_PASSWDSERVERS)) {
342 
343 	rightsmask =0;
344 	rightsmask |= LDAP_REALM_RIGHTS;
345 	rightsmask |= LDAP_SUBTREE_RIGHTS;
346 	if ((rparams != NULL) && (rparams->kdcservers != NULL)) {
347 	    for (i=0; (rparams->kdcservers[i] != NULL); i++) {
348 		if ((status=krb5_ldap_add_service_rights(context,
349 				     LDAP_KDC_SERVICE, rparams->kdcservers[i],
350 				     rparams->realm_name, rparams->subtree, rightsmask)) != 0) {
351 		    goto cleanup;
352 		}
353 	    }
354 	}
355 
356 	rightsmask = 0;
357 	rightsmask |= LDAP_REALM_RIGHTS;
358 	rightsmask |= LDAP_SUBTREE_RIGHTS;
359 	if ((rparams != NULL) && (rparams->adminservers != NULL)) {
360 	    for (i=0; (rparams->adminservers[i] != NULL); i++) {
361 		if ((status=krb5_ldap_add_service_rights(context,
362 				     LDAP_ADMIN_SERVICE, rparams->adminservers[i],
363 				     rparams->realm_name, rparams->subtree, rightsmask)) != 0) {
364 		    goto cleanup;
365 		}
366 	    }
367 	}
368 
369 	rightsmask = 0;
370 	rightsmask |= LDAP_REALM_RIGHTS;
371 	rightsmask |= LDAP_SUBTREE_RIGHTS;
372 	if ((rparams != NULL) && (rparams->passwdservers != NULL)) {
373 	    for (i=0; (rparams->passwdservers[i] != NULL); i++) {
374 		if ((status=krb5_ldap_add_service_rights(context,
375 				     LDAP_PASSWD_SERVICE, rparams->passwdservers[i],
376 				     rparams->realm_name, rparams->subtree, rightsmask)) != 0) {
377 		    goto cleanup;
378 		}
379 	    }
380 	}
381     }
382 #endif
383 
384 cleanup:
385 
386     /* If the krbcontainer/realm creation is not complete, do the roll-back here */
387     if ((krbcontainer_obj_created) && (!realm_obj_created)) {
388 	int rc;
389 	rc = krb5_ldap_delete_krbcontainer(context,
390 		    ((kparams.DN != NULL) ? &kparams : NULL));
391 	krb5_set_error_message(context, rc,
392 	    gettext("could not complete roll-back, error deleting Kerberos Container"));
393     }
394 
395     /* should call krb5_ldap_free_krbcontainer_params() but can't */
396     if (kparams.DN != NULL)
397 	krb5_xfree(kparams.DN);
398 
399     if (rparams)
400 	krb5_ldap_free_realm_params(rparams);
401 
402     return(status);
403 }
404