1 /*
2  * Copyright 2007 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 kadm5_ret_t kadm5_init(char *client_name, char *pass,
172 		       char *service_name,
173 		       kadm5_config_params *params_in,
174 		       krb5_ui_4 struct_version,
175 		       krb5_ui_4 api_version,
176 		       char **db_args,
177 		       void **server_handle)
178 {
179      int ret;
180      kadm5_server_handle_t handle;
181      kadm5_config_params params_local; /* for v1 compat */
182 
183     if (! server_handle)
184 	 return EINVAL;
185 
186     if (! client_name)
187 	 return EINVAL;
188 
189     if (! (handle = (kadm5_server_handle_t) malloc(sizeof *handle)))
190 	 return ENOMEM;
191     memset(handle, 0, sizeof(*handle));
192 
193     ret = dup_db_args(handle, db_args);
194     if (ret) {
195 	free(handle);
196 	return ret;
197     }
198 
199     ret = (int) krb5int_init_context_kdc(&(handle->context));
200     if (ret) {
201 	 free_db_args(handle);
202 	 free(handle);
203 	 return(ret);
204     }
205 
206     handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
207     handle->struct_version = struct_version;
208     handle->api_version = api_version;
209 
210      /*
211       * Verify the version numbers before proceeding; we can't use
212       * CHECK_HANDLE because not all fields are set yet.
213       */
214      GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION,
215 			  KADM5_NEW_SERVER_API_VERSION);
216 
217      /*
218       * Acquire relevant profile entries.  In version 2, merge values
219       * in params_in with values from profile, based on
220       * params_in->mask.
221       *
222       * In version 1, we've given a realm (which may be NULL) instead
223       * of params_in.  So use that realm, make params_in contain an
224       * empty mask, and behave like version 2.
225       */
226      memset((char *) &params_local, 0, sizeof(params_local));
227      if (api_version == KADM5_API_VERSION_1) {
228 	  params_local.realm = (char *) params_in;
229 	  if (params_in)
230 	       params_local.mask = KADM5_CONFIG_REALM;
231 	  params_in = &params_local;
232      }
233 
234 #if 0 /* Now that we look at krb5.conf as well as kdc.conf, we can
235 	 expect to see admin_server being set sometimes.  */
236 #define ILLEGAL_PARAMS (KADM5_CONFIG_ADMIN_SERVER)
237      if (params_in && (params_in->mask & ILLEGAL_PARAMS)) {
238 	  krb5_free_context(handle->context);
239 	  free_db_args(handle);
240 	  free(handle);
241 	  return KADM5_BAD_SERVER_PARAMS;
242      }
243 #endif
244 
245      ret = kadm5_get_config_params(handle->context, (char *) NULL,
246 				       (char *) NULL, params_in,
247 				       &handle->params);
248 
249      if (ret) {
250 	  krb5_free_context(handle->context);
251 	  free_db_args(handle);
252 	  free(handle);
253 	  return(ret);
254      }
255 
256 #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_DBNAME | \
257 			 KADM5_CONFIG_ADBNAME | \
258 			 KADM5_CONFIG_ADB_LOCKFILE | \
259 			 KADM5_CONFIG_ENCTYPE | \
260 			 KADM5_CONFIG_FLAGS | \
261 			 KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \
262 			 KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES)
263 
264      if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
265 	  krb5_free_context(handle->context);
266 	  free_db_args(handle);
267 	  free(handle);
268 	  return KADM5_MISSING_CONF_PARAMS;
269      }
270 
271      ret = krb5_set_default_realm(handle->context, handle->params.realm);
272      if (ret) {
273 	  krb5_free_context(handle->context);
274 	  free_db_args(handle);
275 	  free(handle);
276 	  return ret;
277      }
278 
279     ret = krb5_db_open(handle->context, db_args,
280 		       KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
281     if (ret) {
282 	 krb5_free_context(handle->context);
283 	 free_db_args(handle);
284 	 free(handle);
285 	 return(ret);
286     }
287 
288     if ((ret = krb5_parse_name(handle->context, client_name,
289 			       &handle->current_caller))) {
290 	 krb5_db_fini(handle->context);
291 	 krb5_free_context(handle->context);
292 	 free_db_args(handle);
293 	 free(handle);
294 	 return ret;
295     }
296 
297     if (! (handle->lhandle = malloc(sizeof(*handle)))) {
298 	 krb5_db_fini(handle->context);
299 	 krb5_free_context(handle->context);
300 	 free_db_args(handle);
301 	 free(handle);
302 	 return ENOMEM;
303     }
304     *handle->lhandle = *handle;
305     handle->lhandle->api_version = KADM5_API_VERSION_2;
306     handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
307     handle->lhandle->lhandle = handle->lhandle;
308 
309     /* can't check the handle until current_caller is set */
310     ret = check_handle((void *) handle);
311     if (ret) {
312 	free_db_args(handle);
313 	free(handle);
314 	return ret;
315     }
316 
317     /*
318      * The KADM5_API_VERSION_1 spec said "If pass (or keytab) is NULL
319      * or an empty string, reads the master password from [the stash
320      * file].  Otherwise, the non-NULL password is ignored and the
321      * user is prompted for it via the tty."  However, the code was
322      * implemented the other way: when a non-NULL password was
323      * provided, the stash file was used.  This is somewhat more
324      * sensible, as then a local or remote client that provides a
325      * password does not prompt the user.  This code maintains the
326      * previous actual behavior, and not the old spec behavior,
327      * because that is how the unit tests are written.
328      *
329      * In KADM5_API_VERSION_2, this decision is controlled by
330      * params.
331      *
332      * kdb_init_master's third argument is "from_keyboard".
333      */
334     /*
335      * Solaris Kerberos: Setting to an unknown enc type will make the function
336      * read the encryption type in the stash file instead of assumming that it
337      * is the default type.
338      */
339     if (handle->params.enctype == DEFAULT_KDC_ENCTYPE)
340 	handle->params.enctype = ENCTYPE_UNKNOWN;
341     ret = kdb_init_master(handle, handle->params.realm,
342 			  (handle->api_version == KADM5_API_VERSION_1 ?
343 			   ((pass == NULL) || !(strlen(pass))) :
344 			   ((handle->params.mask & KADM5_CONFIG_MKEY_FROM_KBD)
345 			    && handle->params.mkey_from_kbd)
346 			));
347     if (ret) {
348 	krb5_db_fini(handle->context);
349 	krb5_free_context(handle->context);
350 	free_db_args(handle);
351 	free(handle);
352 	return ret;
353     }
354     /*
355      * Solaris Kerberos: We used the enc type that was discovered in the stash
356      * file to associate with the other magic principals in the database.
357      */
358     handle->params.enctype = handle->master_keyblock.enctype;
359 
360     ret = kdb_init_hist(handle, handle->params.realm);
361     if (ret) {
362 	 krb5_db_fini(handle->context);
363 	 krb5_free_context(handle->context);
364 	 free_db_args(handle);
365 	 free(handle);
366 	 return ret;
367     }
368 
369     ret = init_dict(&handle->params);
370     if (ret) {
371 	 krb5_db_fini(handle->context);
372 	 krb5_free_principal(handle->context, handle->current_caller);
373 	 krb5_free_context(handle->context);
374 	 free_db_args(handle);
375 	 free(handle);
376 	 return ret;
377     }
378 
379     *server_handle = (void *) handle;
380 
381     return KADM5_OK;
382 }
383 
384 kadm5_ret_t kadm5_destroy(void *server_handle)
385 {
386     kadm5_server_handle_t handle = server_handle;
387 
388     CHECK_HANDLE(server_handle);
389 
390     destroy_dict();
391 
392     adb_policy_close(handle);
393     krb5_db_fini(handle->context);
394     krb5_free_principal(handle->context, handle->current_caller);
395     kadm5_free_config_params(handle->context, &handle->params);
396     krb5_free_context(handle->context);
397     handle->magic_number = 0;
398     free(handle->lhandle);
399     free_db_args(handle);
400     free(handle);
401 
402     return KADM5_OK;
403 }
404 
405 kadm5_ret_t kadm5_lock(void *server_handle)
406 {
407     kadm5_server_handle_t handle = server_handle;
408     kadm5_ret_t ret;
409 
410     CHECK_HANDLE(server_handle);
411     ret = krb5_db_lock(handle->context, KRB5_DB_LOCKMODE_EXCLUSIVE);
412     if (ret)
413 	return ret;
414 
415     return KADM5_OK;
416 }
417 
418 kadm5_ret_t kadm5_unlock(void *server_handle)
419 {
420     kadm5_server_handle_t handle = server_handle;
421     kadm5_ret_t ret;
422 
423     CHECK_HANDLE(server_handle);
424     ret = krb5_db_unlock(handle->context);
425     if (ret)
426 	return ret;
427 
428     return KADM5_OK;
429 }
430 
431 kadm5_ret_t kadm5_flush(void *server_handle)
432 {
433      kadm5_server_handle_t handle = server_handle;
434      kadm5_ret_t ret;
435 
436      CHECK_HANDLE(server_handle);
437 
438      if ((ret = krb5_db_fini(handle->context)) ||
439 	 (ret = krb5_db_open(handle->context, handle->db_args,
440 			     KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN)) ||
441 	 (ret = adb_policy_close(handle)) ||
442 	 (ret = adb_policy_init(handle))) {
443 	  (void) kadm5_destroy(server_handle);
444 	  return ret;
445      }
446      return KADM5_OK;
447 }
448 
449 int _kadm5_check_handle(void *handle)
450 {
451      CHECK_HANDLE(handle);
452      return 0;
453 }
454 
455 #include "gssapiP_krb5.h"
456 krb5_error_code kadm5_init_krb5_context (krb5_context *ctx)
457 {
458     /* Solaris Kerberos: not needed */
459 #if 0 /************** Begin IFDEF'ed OUT *******************************/
460     static int first_time = 1;
461     if (first_time) {
462 	krb5_error_code err;
463 	err = krb5_gss_use_kdc_context();
464 	if (err)
465 	    return err;
466 	first_time = 0;
467     }
468 #endif /**************** END IFDEF'ed OUT *******************************/
469     return krb5int_init_context_kdc(ctx);
470 }
471 
472 krb5_error_code
473 kadm5_init_iprop(void *handle)
474 {
475 	kadm5_server_handle_t iprop_h;
476 	krb5_error_code retval;
477 
478 	iprop_h = handle;
479 	if (iprop_h->params.iprop_enabled) {
480 		ulog_set_role(iprop_h->context, IPROP_MASTER);
481 		if ((retval = ulog_map(iprop_h->context, &iprop_h->params,
482 		    FKCOMMAND)) != 0)
483 			return (retval);
484 	}
485 	return (0);
486 }
487