xref: /illumos-gate/usr/src/lib/krb5/plugins/kdb/ldap/libkdb_ldap/ldap_create.c (revision 54925bf60766fbb4f1f2d7c843721406a7b7a3fb)
1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 
3 /*
4  * lib/kdb/kdb_ldap/ldap_create.c
5  *
6  * Copyright (c) 2004-2005, Novell, Inc.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are met:
11  *
12  *   * Redistributions of source code must retain the above copyright notice,
13  *       this list of conditions and the following disclaimer.
14  *   * Redistributions in binary form must reproduce the above copyright
15  *       notice, this list of conditions and the following disclaimer in the
16  *       documentation and/or other materials provided with the distribution.
17  *   * The copyright holder's name is not used to endorse or promote products
18  *       derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
35  * Use is subject to license terms.
36  */
37 
38 #include "ldap_main.h"
39 #include "ldap_realm.h"
40 #include "ldap_principal.h"
41 #include "ldap_krbcontainer.h"
42 #include "ldap_err.h"
43 #include <libintl.h>
44 
45 /*
46  * ******************************************************************************
47  * DAL functions
48  * ******************************************************************************
49  */
50 
51 /*
52  * This function will create a krbcontainer and realm on the LDAP Server, with
53  * the specified attributes.
54  */
55 krb5_error_code
56 krb5_ldap_create (krb5_context context, char *conf_section, char **db_args)
57 {
58     krb5_error_code status = 0;
59     char  **t_ptr = db_args;
60     krb5_ldap_realm_params *rparams = NULL;
61     kdb5_dal_handle *dal_handle = NULL;
62     krb5_ldap_context *ldap_context=NULL;
63     krb5_boolean realm_obj_created = FALSE;
64     krb5_boolean krbcontainer_obj_created = FALSE;
65     krb5_ldap_krbcontainer_params kparams = {0};
66     int srv_cnt = 0;
67     int mask = 0;
68 #ifdef HAVE_EDIRECTORY
69     int i = 0, rightsmask = 0;
70 #endif
71 
72     /* Clear the global error string */
73     krb5_clear_error_message(context);
74 
75     ldap_context = malloc(sizeof(krb5_ldap_context));
76     if (ldap_context == NULL) {
77 	status = ENOMEM;
78 	goto cleanup;
79     }
80     memset(ldap_context, 0, sizeof(*ldap_context));
81 
82     ldap_context->kcontext = context;
83 
84     /* populate ldap_context with ldap specific options */
85     while (t_ptr && *t_ptr) {
86 	char *opt = NULL, *val = NULL;
87 
88 	if ((status = krb5_ldap_get_db_opt(*t_ptr, &opt, &val)) != 0) {
89 	    goto cleanup;
90 	}
91 	if (opt && !strcmp(opt, "binddn")) {
92 	    if (ldap_context->bind_dn) {
93 		free (opt);
94 		free (val);
95 		status = EINVAL;
96 		krb5_set_error_message (context, status, gettext("'binddn' missing"));
97 		goto cleanup;
98 	    }
99 	    if (val == NULL) {
100 		status = EINVAL;
101 		krb5_set_error_message (context, status, gettext("'binddn' value missing"));
102 		free(opt);
103 		goto cleanup;
104 	    }
105 	    ldap_context->bind_dn = strdup(val);
106 	    if (ldap_context->bind_dn == NULL) {
107 		free (opt);
108 		free (val);
109 		status = ENOMEM;
110 		goto cleanup;
111 	    }
112 	} else if (opt && !strcmp(opt, "nconns")) {
113 	    if (ldap_context->max_server_conns) {
114 		free (opt);
115 		free (val);
116 		status = EINVAL;
117 		krb5_set_error_message (context, status, gettext("'nconns' missing"));
118 		goto cleanup;
119 	    }
120 	    if (val == NULL) {
121 		status = EINVAL;
122 		krb5_set_error_message (context, status, gettext("'nconns' value missing"));
123 		free(opt);
124 		goto cleanup;
125 	    }
126 	    ldap_context->max_server_conns = atoi(val) ? atoi(val) : DEFAULT_CONNS_PER_SERVER;
127 	} else if (opt && !strcmp(opt, "bindpwd")) {
128 	    if (ldap_context->bind_pwd) {
129 		free (opt);
130 		free (val);
131 		status = EINVAL;
132 		krb5_set_error_message (context, status, gettext("'bindpwd' missing"));
133 		goto cleanup;
134 	    }
135 	    if (val == NULL) {
136 		status = EINVAL;
137 		krb5_set_error_message (context, status, gettext("'bindpwd' value missing"));
138 		free(opt);
139 		goto cleanup;
140 	    }
141 	    ldap_context->bind_pwd = strdup(val);
142 	    if (ldap_context->bind_pwd == NULL) {
143 		free (opt);
144 		free (val);
145 		status = ENOMEM;
146 		goto cleanup;
147 	    }
148 	} else if (opt && !strcmp(opt, "host")) {
149 	    if (val == NULL) {
150 		status = EINVAL;
151 		krb5_set_error_message (context, status, gettext("'host' value missing"));
152 		free(opt);
153 		goto cleanup;
154 	    }
155 	    if (ldap_context->server_info_list == NULL)
156 		ldap_context->server_info_list =
157 		    (krb5_ldap_server_info **) calloc(SERV_COUNT+1, sizeof(krb5_ldap_server_info *));
158 
159 	    if (ldap_context->server_info_list == NULL) {
160 		free (opt);
161 		free (val);
162 		status = ENOMEM;
163 		goto cleanup;
164 	    }
165 
166 	    ldap_context->server_info_list[srv_cnt] =
167 		(krb5_ldap_server_info *) calloc(1, sizeof(krb5_ldap_server_info));
168 	    if (ldap_context->server_info_list[srv_cnt] == NULL) {
169 		free (opt);
170 		free (val);
171 		status = ENOMEM;
172 		goto cleanup;
173 	    }
174 
175 	    ldap_context->server_info_list[srv_cnt]->server_status = NOTSET;
176 
177 	    ldap_context->server_info_list[srv_cnt]->server_name = strdup(val);
178 	    if (ldap_context->server_info_list[srv_cnt]->server_name == NULL) {
179 		free (opt);
180 		free (val);
181 		status = ENOMEM;
182 		goto cleanup;
183 	    }
184 
185 	    srv_cnt++;
186 #ifdef HAVE_EDIRECTORY
187 	} else if (opt && !strcmp(opt, "cert")) {
188 	    if (val == NULL) {
189 		status = EINVAL;
190 		krb5_set_error_message (context, status, gettext("'cert' value missing"));
191 		free(opt);
192 		goto cleanup;
193 	    }
194 
195 	    if (ldap_context->root_certificate_file == NULL) {
196 		ldap_context->root_certificate_file = strdup(val);
197 		if (ldap_context->root_certificate_file == NULL) {
198 		    free (opt);
199 		    free (val);
200 		    status = ENOMEM;
201 		    goto cleanup;
202 		}
203 	    } else {
204 		void *tmp=NULL;
205 		char *oldstr = NULL;
206 		unsigned int len=0;
207 
208 		oldstr = strdup(ldap_context->root_certificate_file);
209 		if (oldstr == NULL) {
210 		    free (opt);
211 		    free (val);
212 		    status = ENOMEM;
213 		    goto cleanup;
214 		}
215 
216 		tmp = ldap_context->root_certificate_file;
217 		len = strlen(ldap_context->root_certificate_file) + 2 + strlen(val);
218 		ldap_context->root_certificate_file = realloc(ldap_context->root_certificate_file,
219 							      len);
220 		if (ldap_context->root_certificate_file == NULL) {
221 		    free (tmp);
222 		    free (opt);
223 		    free (val);
224 		    status = ENOMEM;
225 		    goto cleanup;
226 		}
227 		memset(ldap_context->root_certificate_file, 0, len);
228 		sprintf(ldap_context->root_certificate_file,"%s %s", oldstr, val);
229 		free (oldstr);
230 	    }
231 #endif
232 	} else {
233 	/* ignore hash argument. Might have been passed from create */
234 	    status = EINVAL;
235 	    if (opt && !strcmp(opt, "temporary")) {
236 		/*
237 		 * temporary is passed in when kdb5_util load without -update is done.
238 		 * This is unsupported by the LDAP plugin.
239 		 */
240 		krb5_set_error_message (context, status,
241 		    gettext("creation of LDAP entries aborted, plugin requires -update argument"));
242 	    } else {
243 		krb5_set_error_message (context, status, gettext("unknown option \'%s\'"),
244 					opt?opt:val);
245 	    }
246 	    free(opt);
247 	    free(val);
248 	    goto cleanup;
249 	}
250 
251 	free(opt);
252 	free(val);
253 	t_ptr++;
254     }
255 
256     dal_handle = (kdb5_dal_handle *) context->db_context;
257     dal_handle->db_context = (kdb5_dal_handle *) ldap_context;
258 
259     status = krb5_ldap_read_server_params(context, conf_section, KRB5_KDB_SRV_TYPE_ADMIN);
260     if (status) {
261 	dal_handle->db_context = NULL;
262 	prepend_err_str (context, gettext("Error reading LDAP server params: "), status, status);
263 	goto cleanup;
264     }
265     status = krb5_ldap_db_init(context, ldap_context);
266     if (status) {
267 	goto cleanup;
268     }
269 
270     /* read the kerberos container */
271     if ((status = krb5_ldap_read_krbcontainer_params(context,
272 			    &(ldap_context->krbcontainer))) == KRB5_KDB_NOENTRY) {
273 
274 	/* Read the kerberos container location from configuration file */
275 	if (ldap_context->conf_section) {
276 	    if ((status = profile_get_string(context->profile,
277 					   KDB_MODULE_SECTION, ldap_context->conf_section,
278 					   "ldap_kerberos_container_dn", NULL,
279 					   &kparams.DN)) != 0) {
280 		goto cleanup;
281 	    }
282 	}
283 	if (kparams.DN == NULL) {
284 	    if ((status = profile_get_string(context->profile,
285 					   KDB_MODULE_DEF_SECTION,
286 					   "ldap_kerberos_container_dn", NULL,
287 					   NULL, &kparams.DN)) != 0) {
288 		goto cleanup;
289 	    }
290 	}
291 
292 	/* create the kerberos container */
293 	status = krb5_ldap_create_krbcontainer(context,
294 					       ((kparams.DN != NULL) ? &kparams : NULL));
295 	if (status)
296 	    goto cleanup;
297 
298 	krbcontainer_obj_created = TRUE;
299 
300 	status = krb5_ldap_read_krbcontainer_params(context,
301 						    &(ldap_context->krbcontainer));
302 	if (status) {
303 	    krb5_set_error_message(context, status, gettext("while reading kerberos container information"));
304 	    goto cleanup;
305 	}
306 
307     } else if (status) {
308 	krb5_set_error_message(context, status, gettext("while reading kerberos container information"));
309 	goto cleanup;
310     }
311 
312     rparams = (krb5_ldap_realm_params *) malloc(sizeof(krb5_ldap_realm_params));
313     if (rparams == NULL) {
314 	status = ENOMEM;
315 	goto cleanup;
316     }
317     memset(rparams, 0, sizeof(*rparams));
318     rparams->realm_name = strdup(context->default_realm);
319     if (rparams->realm_name == NULL) {
320 	status = ENOMEM;
321 	goto cleanup;
322     }
323 
324     if ((status = krb5_ldap_create_realm(context, rparams, mask))) {
325 	krb5_set_error_message(context, status, gettext("while creating realm object entry"));
326 	goto cleanup;
327     }
328 
329     /* We just created the Realm container. Here starts our transaction tracking */
330     realm_obj_created = TRUE;
331 
332     /* verify realm object */
333     if ((status = krb5_ldap_read_realm_params(context,
334 					      rparams->realm_name,
335 					      &(ldap_context->lrparams),
336 					      &mask))) {
337 	krb5_set_error_message(context, status, gettext("while reading realm object entry"));
338 	goto cleanup;
339     }
340 
341 #ifdef HAVE_EDIRECTORY
342     if ((mask & LDAP_REALM_KDCSERVERS) || (mask & LDAP_REALM_ADMINSERVERS) ||
343 	(mask & LDAP_REALM_PASSWDSERVERS)) {
344 
345 	rightsmask =0;
346 	rightsmask |= LDAP_REALM_RIGHTS;
347 	rightsmask |= LDAP_SUBTREE_RIGHTS;
348 	if ((rparams != NULL) && (rparams->kdcservers != NULL)) {
349 	    for (i=0; (rparams->kdcservers[i] != NULL); i++) {
350 		if ((status=krb5_ldap_add_service_rights(context,
351 				     LDAP_KDC_SERVICE, rparams->kdcservers[i],
352 				     rparams->realm_name, rparams->subtree, rightsmask)) != 0) {
353 		    goto cleanup;
354 		}
355 	    }
356 	}
357 
358 	rightsmask = 0;
359 	rightsmask |= LDAP_REALM_RIGHTS;
360 	rightsmask |= LDAP_SUBTREE_RIGHTS;
361 	if ((rparams != NULL) && (rparams->adminservers != NULL)) {
362 	    for (i=0; (rparams->adminservers[i] != NULL); i++) {
363 		if ((status=krb5_ldap_add_service_rights(context,
364 				     LDAP_ADMIN_SERVICE, rparams->adminservers[i],
365 				     rparams->realm_name, rparams->subtree, rightsmask)) != 0) {
366 		    goto cleanup;
367 		}
368 	    }
369 	}
370 
371 	rightsmask = 0;
372 	rightsmask |= LDAP_REALM_RIGHTS;
373 	rightsmask |= LDAP_SUBTREE_RIGHTS;
374 	if ((rparams != NULL) && (rparams->passwdservers != NULL)) {
375 	    for (i=0; (rparams->passwdservers[i] != NULL); i++) {
376 		if ((status=krb5_ldap_add_service_rights(context,
377 				     LDAP_PASSWD_SERVICE, rparams->passwdservers[i],
378 				     rparams->realm_name, rparams->subtree, rightsmask)) != 0) {
379 		    goto cleanup;
380 		}
381 	    }
382 	}
383     }
384 #endif
385 
386 cleanup:
387 
388     /* If the krbcontainer/realm creation is not complete, do the roll-back here */
389     if ((krbcontainer_obj_created) && (!realm_obj_created)) {
390 	int rc;
391 	rc = krb5_ldap_delete_krbcontainer(context,
392 		    ((kparams.DN != NULL) ? &kparams : NULL));
393 	krb5_set_error_message(context, rc,
394 	    gettext("could not complete roll-back, error deleting Kerberos Container"));
395     }
396 
397     /* should call krb5_ldap_free_krbcontainer_params() but can't */
398     if (kparams.DN != NULL)
399 	krb5_xfree(kparams.DN);
400 
401     if (rparams)
402 	krb5_ldap_free_realm_params(rparams);
403 
404     return(status);
405 }
406