1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
10  *
11  *	Openvision retains the copyright to derivative works of
12  *	this source code.  Do *NOT* create a derivative of this
13  *	source code before consulting with your legal department.
14  *	Do *NOT* integrate *ANY* of this source code into another
15  *	product before consulting with your legal department.
16  *
17  *	For further information, read the top-level Openvision
18  *	copyright which is contained in the top-level MIT Kerberos
19  *	copyright.
20  *
21  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
22  *
23  */
24 
25 
26 /*
27  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
28  *
29  * $Id: server_init.c,v 1.8 2002/10/15 15:40:49 epeisach Exp $
30  * $Source: /cvs/krbdev/krb5/src/lib/kadm5/srv/server_init.c,v $
31  */
32 
33 #if !defined(lint) && !defined(__CODECENTER__)
34 static char *rcsid = "$Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/server_init.c,v 1.8 2002/10/15 15:40:49 epeisach Exp $";
35 #endif
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <com_err.h>
40 #include <kadm5/admin.h>
41 #include <krb5.h>
42 #include "server_internal.h"
43 #include <kdb/kdb_log.h>
44 
45 /*
46  * Function check_handle
47  *
48  * Purpose: Check a server handle and return a com_err code if it is
49  * invalid or 0 if it is valid.
50  *
51  * Arguments:
52  *
53  * 	handle		The server handle.
54  */
55 
56 static int check_handle(void *handle)
57 {
58      CHECK_HANDLE(handle);
59      return 0;
60 }
61 
62 static int dup_db_args(kadm5_server_handle_t handle, char **db_args)
63 {
64     int count  = 0;
65     int ret = 0;
66 
67     for (count=0; db_args && db_args[count]; count++);
68     if (count == 0) {
69 	handle->db_args = NULL;
70 	goto clean_n_exit;
71     }
72 
73     handle->db_args = calloc(sizeof(char*), count+1);
74     if (handle->db_args == NULL) {
75 	ret=ENOMEM;
76 	goto clean_n_exit;
77     }
78 
79     for (count=0; db_args[count]; count++) {
80 	handle->db_args[count] = strdup(db_args[count]);
81 	if (handle->db_args[count] == NULL) {
82 	    ret = ENOMEM;
83 	    goto clean_n_exit;
84 	}
85     }
86 
87  clean_n_exit:
88     if (ret && handle->db_args) {
89 	for (count=0; handle->db_args[count]; count++)
90 	    free(handle->db_args[count]);
91 
92 	free(handle->db_args), handle->db_args = NULL;
93     }
94 
95     return ret;
96 }
97 
98 static void free_db_args(kadm5_server_handle_t handle)
99 {
100     int count;
101 
102     if (handle->db_args) {
103 	for (count=0; handle->db_args[count]; count++)
104 	    free(handle->db_args[count]);
105 
106 	free(handle->db_args), handle->db_args = NULL;
107     }
108 }
109 
110 kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass,
111 				     char *service_name,
112 				     kadm5_config_params *params,
113 				     krb5_ui_4 struct_version,
114 				     krb5_ui_4 api_version,
115 				     char **db_args,
116 				     void **server_handle)
117 {
118      return kadm5_init(client_name, pass, service_name, params,
119 		       struct_version, api_version, db_args,
120 		       server_handle);
121 }
122 
123 kadm5_ret_t kadm5_init_with_creds(char *client_name,
124 				  krb5_ccache ccache,
125 				  char *service_name,
126 				  kadm5_config_params *params,
127 				  krb5_ui_4 struct_version,
128 				  krb5_ui_4 api_version,
129 				  char **db_args,
130 				  void **server_handle)
131 {
132      /*
133       * A program calling init_with_creds *never* expects to prompt the
134       * user.  Therefore, always pass a dummy password in case this is
135       * KADM5_API_VERSION_1.  If this is KADM5_API_VERSION_2 and
136       * MKEY_FROM_KBD is non-zero, return an error.
137       */
138      if (api_version == KADM5_API_VERSION_2 && params &&
139 	 (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) &&
140 	 params->mkey_from_kbd)
141 	  return KADM5_BAD_SERVER_PARAMS;
142      return kadm5_init(client_name, NULL, service_name, params,
143 		       struct_version, api_version, db_args,
144 		       server_handle);
145 }
146 
147 
148 kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab,
149 				 char *service_name,
150 				 kadm5_config_params *params,
151 				 krb5_ui_4 struct_version,
152 				 krb5_ui_4 api_version,
153 				 char **db_args,
154 				 void **server_handle)
155 {
156      /*
157       * A program calling init_with_skey *never* expects to prompt the
158       * user.  Therefore, always pass a dummy password in case this is
159       * KADM5_API_VERSION_1.  If this is KADM5_API_VERSION_2 and
160       * MKEY_FROM_KBD is non-zero, return an error.
161       */
162      if (api_version == KADM5_API_VERSION_2 && params &&
163 	 (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) &&
164 	 params->mkey_from_kbd)
165 	  return KADM5_BAD_SERVER_PARAMS;
166      return kadm5_init(client_name, NULL, service_name, params,
167 		       struct_version, api_version, db_args,
168 		       server_handle);
169 }
170 
171 /*
172  * Solaris Kerberos:
173  * A private extended version of kadm5_init which potentially
174  * returns more information in case of an error.
175  */
176 kadm5_ret_t kadm5_init2(char *client_name, char *pass,
177 		       char *service_name,
178 		       kadm5_config_params *params_in,
179 		       krb5_ui_4 struct_version,
180 		       krb5_ui_4 api_version,
181 		       char **db_args,
182 		       void **server_handle,
183 		       char **emsg)
184 {
185      int ret;
186      kadm5_server_handle_t handle;
187      kadm5_config_params params_local; /* for v1 compat */
188 
189     if (emsg)
190 	*emsg = NULL;
191 
192     if (! server_handle)
193 	 return EINVAL;
194 
195     if (! client_name)
196 	 return EINVAL;
197 
198     if (! (handle = (kadm5_server_handle_t) malloc(sizeof *handle)))
199 	 return ENOMEM;
200     memset(handle, 0, sizeof(*handle));
201 
202     ret = dup_db_args(handle, db_args);
203     if (ret) {
204 	free(handle);
205 	return ret;
206     }
207 
208     ret = (int) krb5int_init_context_kdc(&(handle->context));
209     if (ret) {
210 	 free_db_args(handle);
211 	 free(handle);
212 	 return(ret);
213     }
214 
215     handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
216     handle->struct_version = struct_version;
217     handle->api_version = api_version;
218 
219      /*
220       * Verify the version numbers before proceeding; we can't use
221       * CHECK_HANDLE because not all fields are set yet.
222       */
223      GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION,
224 			  KADM5_NEW_SERVER_API_VERSION);
225 
226      /*
227       * Acquire relevant profile entries.  In version 2, merge values
228       * in params_in with values from profile, based on
229       * params_in->mask.
230       *
231       * In version 1, we've given a realm (which may be NULL) instead
232       * of params_in.  So use that realm, make params_in contain an
233       * empty mask, and behave like version 2.
234       */
235      memset((char *) &params_local, 0, sizeof(params_local));
236      if (api_version == KADM5_API_VERSION_1) {
237 	  params_local.realm = (char *) params_in;
238 	  if (params_in)
239 	       params_local.mask = KADM5_CONFIG_REALM;
240 	  params_in = &params_local;
241      }
242 
243 #if 0 /* Now that we look at krb5.conf as well as kdc.conf, we can
244 	 expect to see admin_server being set sometimes.  */
245 #define ILLEGAL_PARAMS (KADM5_CONFIG_ADMIN_SERVER)
246      if (params_in && (params_in->mask & ILLEGAL_PARAMS)) {
247 	  krb5_free_context(handle->context);
248 	  free_db_args(handle);
249 	  free(handle);
250 	  return KADM5_BAD_SERVER_PARAMS;
251      }
252 #endif
253 
254      ret = kadm5_get_config_params(handle->context, (char *) NULL,
255 				       (char *) NULL, params_in,
256 				       &handle->params);
257 
258      if (ret) {
259 	  krb5_free_context(handle->context);
260 	  free_db_args(handle);
261 	  free(handle);
262 	  return(ret);
263      }
264 
265 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_DBNAME | \
266 			 KADM5_CONFIG_ADBNAME | \
267 			 KADM5_CONFIG_ADB_LOCKFILE | \
268 			 KADM5_CONFIG_ENCTYPE | \
269 			 KADM5_CONFIG_FLAGS | \
270 			 KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \
271 			 KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES)
272 
273      if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
274 	  kadm5_free_config_params(handle->context, &handle->params);
275 	  krb5_free_context(handle->context);
276 	  free_db_args(handle);
277 	  free(handle);
278 	  return KADM5_MISSING_CONF_PARAMS;
279      }
280 
281      ret = krb5_set_default_realm(handle->context, handle->params.realm);
282      if (ret) {
283 	  kadm5_free_config_params(handle->context, &handle->params);
284 	  krb5_free_context(handle->context);
285 	  free_db_args(handle);
286 	  free(handle);
287 	  return ret;
288      }
289 
290     ret = krb5_db_open(handle->context, db_args,
291 		       KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
292     if (ret) {
293 	 if (emsg) {
294 		 const char *m = krb5_get_error_message(handle->context, ret);
295 		 *emsg = strdup(m);
296 		 krb5_free_error_message(handle->context, m);
297 	 }
298 	 kadm5_free_config_params(handle->context, &handle->params);
299 	 krb5_free_context(handle->context);
300 	 free_db_args(handle);
301 	 free(handle);
302 	 return(ret);
303     }
304 
305     if ((ret = krb5_parse_name(handle->context, client_name,
306 			       &handle->current_caller))) {
307 	 krb5_db_fini(handle->context);
308 	 kadm5_free_config_params(handle->context, &handle->params);
309 	 krb5_free_context(handle->context);
310 	 free_db_args(handle);
311 	 free(handle);
312 	 return ret;
313     }
314 
315     if (! (handle->lhandle = malloc(sizeof(*handle)))) {
316 	 krb5_db_fini(handle->context);
317 	 kadm5_free_config_params(handle->context, &handle->params);
318 	 krb5_free_context(handle->context);
319 	 free_db_args(handle);
320 	 free(handle);
321 	 return ENOMEM;
322     }
323     *handle->lhandle = *handle;
324     handle->lhandle->api_version = KADM5_API_VERSION_2;
325     handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
326     handle->lhandle->lhandle = handle->lhandle;
327 
328     /* can't check the handle until current_caller is set */
329     ret = check_handle((void *) handle);
330     if (ret) {
331 	krb5_db_fini(handle->context);
332 	kadm5_free_config_params(handle->context, &handle->params);
333 	krb5_free_context(handle->context);
334 	free_db_args(handle);
335 	free(handle);
336 	return ret;
337     }
338 
339     /*
340      * The KADM5_API_VERSION_1 spec said "If pass (or keytab) is NULL
341      * or an empty string, reads the master password from [the stash
342      * file].  Otherwise, the non-NULL password is ignored and the
343      * user is prompted for it via the tty."  However, the code was
344      * implemented the other way: when a non-NULL password was
345      * provided, the stash file was used.  This is somewhat more
346      * sensible, as then a local or remote client that provides a
347      * password does not prompt the user.  This code maintains the
348      * previous actual behavior, and not the old spec behavior,
349      * because that is how the unit tests are written.
350      *
351      * In KADM5_API_VERSION_2, this decision is controlled by
352      * params.
353      *
354      * kdb_init_master's third argument is "from_keyboard".
355      */
356     /*
357      * Solaris Kerberos: Setting to an unknown enc type will make the function
358      * read the encryption type in the stash file instead of assumming that it
359      * is the default type.
360      */
361     if (handle->params.enctype == DEFAULT_KDC_ENCTYPE)
362 	handle->params.enctype = ENCTYPE_UNKNOWN;
363     ret = kdb_init_master(handle, handle->params.realm,
364 			  (handle->api_version == KADM5_API_VERSION_1 ?
365 			   ((pass == NULL) || !(strlen(pass))) :
366 			   ((handle->params.mask & KADM5_CONFIG_MKEY_FROM_KBD)
367 			    && handle->params.mkey_from_kbd)
368 			));
369     if (ret) {
370 	krb5_db_fini(handle->context);
371 	kadm5_free_config_params(handle->context, &handle->params);
372 	krb5_free_context(handle->context);
373 	free_db_args(handle);
374 	free(handle);
375 	return ret;
376     }
377     /*
378      * Solaris Kerberos: We used the enc type that was discovered in the stash
379      * file to associate with the other magic principals in the database.
380      */
381     handle->params.enctype = handle->master_keyblock.enctype;
382 
383     ret = kdb_init_hist(handle, handle->params.realm);
384     if (ret) {
385 	 krb5_db_fini(handle->context);
386 	 kadm5_free_config_params(handle->context, &handle->params);
387 	 krb5_free_context(handle->context);
388 	 free_db_args(handle);
389 	 free(handle);
390 	 return ret;
391     }
392 
393     ret = init_dict(&handle->params);
394     if (ret) {
395 	 krb5_db_fini(handle->context);
396 	 krb5_free_principal(handle->context, handle->current_caller);
397 	 kadm5_free_config_params(handle->context, &handle->params);
398 	 krb5_free_context(handle->context);
399 	 free_db_args(handle);
400 	 free(handle);
401 	 return ret;
402     }
403 
404     *server_handle = (void *) handle;
405 
406     return KADM5_OK;
407 }
408 
409 kadm5_ret_t kadm5_init(char *client_name, char *pass,
410 		       char *service_name,
411 		       kadm5_config_params *params_in,
412 		       krb5_ui_4 struct_version,
413 		       krb5_ui_4 api_version,
414 		       char **db_args,
415 		       void **server_handle) {
416 	return (kadm5_init2(client_name, pass, service_name, params_in,
417 	    struct_version, api_version, db_args, server_handle, NULL));
418 
419 }
420 
421 kadm5_ret_t kadm5_destroy(void *server_handle)
422 {
423     kadm5_server_handle_t handle = server_handle;
424 
425     CHECK_HANDLE(server_handle);
426 
427     destroy_dict();
428 
429     adb_policy_close(handle);
430     krb5_db_fini(handle->context);
431     krb5_free_principal(handle->context, handle->current_caller);
432     kadm5_free_config_params(handle->context, &handle->params);
433     krb5_free_context(handle->context);
434     handle->magic_number = 0;
435     free(handle->lhandle);
436     free_db_args(handle);
437     free(handle);
438 
439     return KADM5_OK;
440 }
441 
442 kadm5_ret_t kadm5_lock(void *server_handle)
443 {
444     kadm5_server_handle_t handle = server_handle;
445     kadm5_ret_t ret;
446 
447     CHECK_HANDLE(server_handle);
448     ret = krb5_db_lock(handle->context, KRB5_DB_LOCKMODE_EXCLUSIVE);
449     if (ret)
450 	return ret;
451 
452     return KADM5_OK;
453 }
454 
455 kadm5_ret_t kadm5_unlock(void *server_handle)
456 {
457     kadm5_server_handle_t handle = server_handle;
458     kadm5_ret_t ret;
459 
460     CHECK_HANDLE(server_handle);
461     ret = krb5_db_unlock(handle->context);
462     if (ret)
463 	return ret;
464 
465     return KADM5_OK;
466 }
467 
468 kadm5_ret_t kadm5_flush(void *server_handle)
469 {
470      kadm5_server_handle_t handle = server_handle;
471      kadm5_ret_t ret;
472 
473      CHECK_HANDLE(server_handle);
474 
475      if ((ret = krb5_db_fini(handle->context)) ||
476 	 (ret = krb5_db_open(handle->context, handle->db_args,
477 			     KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN)) ||
478 	 (ret = adb_policy_close(handle)) ||
479 	 (ret = adb_policy_init(handle))) {
480 	  (void) kadm5_destroy(server_handle);
481 	  return ret;
482      }
483      return KADM5_OK;
484 }
485 
486 int _kadm5_check_handle(void *handle)
487 {
488      CHECK_HANDLE(handle);
489      return 0;
490 }
491 
492 #include "gssapiP_krb5.h"
493 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx)
494 {
495     /* Solaris Kerberos: not needed */
496 #if 0 /************** Begin IFDEF'ed OUT *******************************/
497     static int first_time = 1;
498     if (first_time) {
499 	krb5_error_code err;
500 	err = krb5_gss_use_kdc_context();
501 	if (err)
502 	    return err;
503 	first_time = 0;
504     }
505 #endif /**************** END IFDEF'ed OUT *******************************/
506     return krb5int_init_context_kdc(ctx);
507 }
508 
509 krb5_error_code
510 kadm5_init_iprop(void *handle)
511 {
512 	kadm5_server_handle_t iprop_h;
513 	krb5_error_code retval;
514 
515 	iprop_h = handle;
516 	if (iprop_h->params.iprop_enabled) {
517 		ulog_set_role(iprop_h->context, IPROP_MASTER);
518 		if ((retval = ulog_map(iprop_h->context, &iprop_h->params,
519 		    FKCOMMAND)) != 0)
520 			return (retval);
521 	}
522 	return (0);
523 }
524