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