1 /*
2  * Copyright 2006 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  * admin/create/kdb5_create.c
28  *
29  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
30  * All Rights Reserved.
31  *
32  * Export of this software from the United States of America may
33  *   require a specific license from the United States Government.
34  *   It is the responsibility of any person or organization contemplating
35  *   export to obtain such a license before exporting.
36  *
37  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
38  * distribute this software and its documentation for any purpose and
39  * without fee is hereby granted, provided that the above copyright
40  * notice appear in all copies and that both that copyright notice and
41  * this permission notice appear in supporting documentation, and that
42  * the name of M.I.T. not be used in advertising or publicity pertaining
43  * to distribution of the software without specific, written prior
44  * permission.  Furthermore if you modify this software you must label
45  * your software as modified software and not distribute it in such a
46  * fashion that it might be confused with the original M.I.T. software.
47  * M.I.T. makes no representations about the suitability of
48  * this software for any purpose.  It is provided "as is" without express
49  * or implied warranty.
50  *
51  *
52  * Generate (from scratch) a Kerberos KDC database.
53  */
54 
55 /*
56  *  Yes, I know this is a hack, but we need admin.h without including the
57  *  rpc.h header. Additionally, our rpc.h header brings in
58  *  a des.h header which causes other problems.
59  */
60 #define	_RPC_RPC_H
61 
62 #include <stdio.h>
63 #define KDB5_DISPATCH
64 #define KRB5_KDB5_DBM__
65 #include <k5-int.h>
66 /* #define these to avoid an indirection function; for future implementations,
67    these may be redirected from a dispatch table/routine */
68 #define krb5_dbm_db_set_name krb5_db_set_name
69 #define krb5_dbm_db_set_nonblocking krb5_db_set_nonblocking
70 #define krb5_dbm_db_init krb5_db_init
71 #define krb5_dbm_db_get_age krb5_db_get_age
72 #define krb5_dbm_db_create krb5_db_create
73 #define krb5_dbm_db_rename krb5_db_rename
74 #define krb5_dbm_db_get_principal krb5_db_get_principal
75 #define krb5_dbm_db_free_principal krb5_db_free_principal
76 #define krb5_dbm_db_put_principal krb5_db_put_principal
77 #define krb5_dbm_db_delete_principal krb5_db_delete_principal
78 #define krb5_dbm_db_lock krb5_db_lock
79 #define krb5_dbm_db_unlock krb5_db_unlock
80 #define krb5_dbm_db_set_lockmode krb5_db_set_lockmode
81 #define krb5_dbm_db_close_database krb5_db_close_database
82 #define krb5_dbm_db_open_database krb5_db_open_database
83 
84 #include <kadm5/admin.h>
85 #include <rpc/types.h>
86 #include <rpc/xdr.h>
87 #include <kadm5/adb.h>
88 #include <libintl.h>
89 #include "kdb5_util.h"
90 
91 enum ap_op {
92     NULL_KEY,				/* setup null keys */
93     MASTER_KEY,				/* use master key as new key */
94     TGT_KEY				/* special handling for tgt key */
95 };
96 
97 krb5_key_salt_tuple def_kslist =
98 	{ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL};
99 
100 struct realm_info {
101     krb5_deltat max_life;
102     krb5_deltat max_rlife;
103     krb5_timestamp expiration;
104     krb5_flags flags;
105     krb5_keyblock *key;
106     krb5_int32 nkslist;
107     krb5_key_salt_tuple *kslist;
108 } rblock = { /* XXX */
109 
110     KRB5_KDB_MAX_LIFE,
111     KRB5_KDB_MAX_RLIFE,
112     KRB5_KDB_EXPIRATION,
113     KRB5_KDB_DEF_FLAGS,
114     (krb5_keyblock *) NULL,
115     1,
116     &def_kslist
117 };
118 
119 struct iterate_args {
120     krb5_context	ctx;
121     struct realm_info	*rblock;
122     krb5_db_entry	*dbentp;
123 };
124 
125 static krb5_error_code add_principal(krb5_context,
126 		krb5_principal,
127 		enum ap_op,
128 		struct realm_info *,
129 		krb5_keyblock *);
130 
131 /*
132  * Steps in creating a database:
133  *
134  * 1) use the db calls to open/create a new database
135  *
136  * 2) get a realm name for the new db
137  *
138  * 3) get a master password for the new db; convert to an encryption key.
139  *
140  * 4) create various required entries in the database
141  *
142  * 5) close & exit
143  */
144 
145 extern krb5_principal master_princ;
146 
147 krb5_data tgt_princ_entries[] = {
148 	{0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
149 	{0, 0, 0} };
150 
151 krb5_data db_creator_entries[] = {
152 	{0, sizeof("db_creation")-1, "db_creation"} };
153 
154 /*
155  * XXX knows about contents of krb5_principal, and that tgt names
156  * are of form TGT/REALM@REALM
157  */
158 krb5_principal_data tgt_princ = {
159         0,					/* magic number */
160 	{0, 0, 0},				/* krb5_data realm */
161 	tgt_princ_entries,			/* krb5_data *data */
162 	2,					/* int length */
163 	KRB5_NT_SRV_INST			/* int type */
164 };
165 
166 krb5_principal_data db_create_princ = {
167         0,					/* magic number */
168 	{0, 0, 0},				/* krb5_data realm */
169 	db_creator_entries,			/* krb5_data *data */
170 	1,					/* int length */
171 	KRB5_NT_SRV_INST			/* int type */
172 };
173 
174 extern char *mkey_password;
175 
176 extern char *progname;
177 extern int exit_status;
178 extern osa_adb_policy_t policy_db;
179 extern kadm5_config_params global_params;
180 extern krb5_context util_context;
181 
182 void
183 kdb5_create(argc, argv)
184    int argc;
185    char *argv[];
186 {
187     int optchar;
188 
189     krb5_error_code retval;
190     char *mkey_fullname;
191     char *pw_str = 0;
192     unsigned int pw_size = 0;
193     int do_stash = 0;
194     krb5_int32 crflags = KRB5_KDB_CREATE_BTREE;
195     krb5_data pwd, seed;
196     kdb_log_context *log_ctx;
197     krb5_keyblock mkey;
198     krb5_data master_salt = { 0, NULL };
199 
200     if (strrchr(argv[0], '/'))
201 	argv[0] = strrchr(argv[0], '/')+1;
202 
203     while ((optchar = getopt(argc, argv, "s")) != -1) {
204 	switch(optchar) {
205 	case 's':
206 	    do_stash++;
207 	    break;
208 	case 'h':
209 	    crflags = KRB5_KDB_CREATE_HASH;
210 	case '?':
211 	default:
212 	    usage();
213 	    return;
214 	}
215     }
216 
217     rblock.max_life = global_params.max_life;
218     rblock.max_rlife = global_params.max_rlife;
219     rblock.expiration = global_params.expiration;
220     rblock.flags = global_params.flags;
221     rblock.nkslist = global_params.num_keysalts;
222     rblock.kslist = global_params.keysalts;
223 
224     log_ctx = util_context->kdblog_context;
225 
226     retval = krb5_db_set_name(util_context, global_params.dbname);
227 	if (!retval)
228 		retval = EEXIST;
229 
230     if (retval == EEXIST || retval == EACCES || retval == EPERM) {
231 	/* it exists ! */
232 		com_err(argv[0], 0,
233 			gettext("The database '%s' appears to already exist"),
234 		global_params.dbname);
235 		exit_status++;
236 		return;
237     }
238     /* assemble & parse the master key name */
239 
240     if ((retval = krb5_db_setup_mkey_name(util_context,
241 					  global_params.mkey_name,
242 					  global_params.realm,
243 					  &mkey_fullname, &master_princ))) {
244 		com_err(argv[0], retval,
245 			gettext("while setting up master key name"));
246 		exit_status++;
247 		return;
248     }
249 	krb5_princ_set_realm_data(util_context,
250 				&db_create_princ, global_params.realm);
251 	krb5_princ_set_realm_length(util_context,
252 				    &db_create_princ,
253 				    strlen(global_params.realm));
254 	krb5_princ_set_realm_data(util_context,
255 				&tgt_princ, global_params.realm);
256 	krb5_princ_set_realm_length(util_context,
257 				    &tgt_princ, strlen(global_params.realm));
258 	krb5_princ_component(util_context, &tgt_princ, 1)->data =
259 	    global_params.realm;
260 	krb5_princ_component(util_context, &tgt_princ, 1)->length =
261 	    strlen(global_params.realm);
262 
263 	printf(gettext("Initializing database '%s' for realm '%s',\n"
264 			"master key name '%s'\n"),
265 	   global_params.dbname, global_params.realm, mkey_fullname);
266 
267     if (!mkey_password) {
268 	printf(gettext("You will be prompted for the "
269 			"database Master Password.\n"));
270 	printf(gettext("It is important that you NOT FORGET this password.\n"));
271 	fflush(stdout);
272 
273 	pw_size = 1024;
274 	pw_str = malloc(pw_size);
275 
276 	retval = krb5_read_password(util_context,
277 			    gettext("Enter KDC database master key"),
278 			    gettext("Re-enter KDC database "
279 				    "master key to verify"),
280 			    pw_str, &pw_size);
281 	if (retval) {
282 		com_err(argv[0], retval,
283 		    gettext("while reading master key from keyboard"));
284 		exit_status++;
285 		return;
286 	}
287 	mkey_password = pw_str;
288     }
289 
290     pwd.data = mkey_password;
291     pwd.length = strlen(mkey_password);
292 
293     retval = krb5_principal2salt(util_context, master_princ, &master_salt);
294     if (retval) {
295 	com_err(argv[0], retval,
296 		gettext("while calculated master key salt"));
297 	exit_status++;
298 	goto cleanup;
299     }
300 
301     if (retval = krb5_c_string_to_key(util_context, global_params.enctype,
302 			      &pwd, &master_salt, &mkey)) {
303 	com_err(argv[0], retval,
304 	    gettext("while transforming master key from password"));
305 	exit_status++;
306 	goto cleanup;
307     }
308 
309     retval = krb5_copy_keyblock(util_context, &mkey, &rblock.key);
310     if (retval) {
311 	com_err(argv[0], retval, gettext("while copying master key"));
312 	exit_status++;
313 	goto cleanup;
314     }
315 
316     seed.length = mkey.length;
317     seed.data = (char *)mkey.contents;
318 
319     if ((retval = krb5_c_random_seed(util_context, &seed))) {
320 	com_err(argv[0], retval,
321 		gettext("while initializing random key generator"));
322 	exit_status++;
323 	goto cleanup;
324     }
325     if ((retval = krb5_db_create(util_context,
326 				 global_params.dbname, crflags))) {
327 	com_err(argv[0], retval,
328 		gettext("while creating database '%s'"),
329 		global_params.dbname);
330 	exit_status++;
331 	goto cleanup;
332     }
333     if (retval = krb5_db_fini(util_context)) {
334 	com_err(argv[0], retval,
335 		gettext("while closing current database"));
336 	exit_status++;
337 	goto cleanup;
338     }
339     if ((retval = krb5_db_set_name(util_context, global_params.dbname))) {
340 	com_err(argv[0], retval,
341 		gettext("while setting active database to '%s'"),
342                global_params.dbname);
343 	exit_status++;
344 	goto cleanup;
345     }
346     if ((retval = krb5_db_init(util_context))) {
347 	com_err(argv[0], retval,
348 		gettext("while initializing the database '%s'"),
349 	global_params.dbname);
350 	exit_status++;
351 	goto cleanup;
352     }
353 
354     if (log_ctx && log_ctx->iproprole) {
355 	if (retval = ulog_map(util_context, &global_params, FKCOMMAND)) {
356 		com_err(argv[0], retval,
357 			gettext("while creating update log"));
358 		exit_status++;
359 		goto cleanup;
360 	}
361 
362 	/*
363 	 * We're reinitializing the update log in case one already
364 	 * existed, but this should never happen.
365 	 */
366 	(void) memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t));
367 
368 	log_ctx->ulog->kdb_hmagic = KDB_HMAGIC;
369 	log_ctx->ulog->db_version_num = KDB_VERSION;
370 	log_ctx->ulog->kdb_state = KDB_STABLE;
371 	log_ctx->ulog->kdb_block = ULOG_BLOCK;
372 
373 	/*
374 	 * Since we're creating a new db we shouldn't worry about
375 	 * adding the initial principals since any slave might as well
376 	 * do full resyncs from this newly created db.
377 	 */
378 	log_ctx->iproprole = IPROP_NULL;
379     }
380 
381     if ((retval = add_principal(util_context,
382 		    master_princ, MASTER_KEY, &rblock, &mkey)) ||
383 	(retval = add_principal(util_context,
384 			    &tgt_princ, TGT_KEY, &rblock, &mkey))) {
385 	(void) krb5_db_fini(util_context);
386 	com_err(argv[0], retval,
387 	    gettext("while adding entries to the database"));
388 	exit_status++;
389 	goto cleanup;
390     }
391     /*
392      * Always stash the master key so kadm5_create does not prompt for
393      * it; delete the file below if it was not requested.  DO NOT EXIT
394      * BEFORE DELETING THE KEYFILE if do_stash is not set.
395      */
396     if (retval = krb5_db_store_mkey(util_context,
397 				    global_params.stash_file,
398 				    master_princ,
399 				    &mkey)) {
400 	com_err(argv[0], errno, gettext("while storing key"));
401 	printf(gettext("Warning: couldn't stash master key.\n"));
402     }
403 
404     if (pw_str)
405 	memset(pw_str, 0, pw_size);
406 
407     if (kadm5_create(&global_params)) {
408 	if (!do_stash)
409 		unlink(global_params.stash_file);
410 	exit_status++;
411 	goto cleanup;
412     }
413     if (!do_stash)
414 	unlink(global_params.stash_file);
415 
416 cleanup:
417     if (pw_str) {
418 	if (mkey_password == pw_str)
419 		mkey_password = NULL;
420 	free(pw_str);
421     }
422     if (master_salt.data)
423 	free(master_salt.data);
424     krb5_free_keyblock_contents(util_context, rblock.key);
425     krb5_free_keyblock_contents(util_context, &mkey);
426     (void) krb5_db_fini(util_context);
427 
428     return;
429 
430 }
431 
432 static krb5_error_code
433 tgt_keysalt_iterate(ksent, ptr)
434     krb5_key_salt_tuple	*ksent;
435     krb5_pointer	ptr;
436 {
437     krb5_context	context;
438     krb5_error_code	kret;
439     struct iterate_args	*iargs;
440     krb5_keyblock	key;
441     krb5_int32		ind;
442     krb5_pointer rseed;
443     krb5_data	pwd;
444 
445     iargs = (struct iterate_args *) ptr;
446     kret = 0;
447 
448     context = iargs->ctx;
449 
450     /*
451      * Convert the master key password into a key for this particular
452      * encryption system.
453      */
454     pwd.data = mkey_password;
455     pwd.length = strlen(mkey_password);
456     if (kret = krb5_c_random_seed(context, &pwd))
457 	return kret;
458 
459     if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
460 	ind = iargs->dbentp->n_key_data-1;
461 	if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype,
462 					    &key))) {
463 	    kret = krb5_dbekd_encrypt_key_data(context,
464 					       iargs->rblock->key,
465 					       &key,
466 					       NULL,
467 					       1,
468 					       &iargs->dbentp->key_data[ind]);
469 	    krb5_free_keyblock_contents(context, &key);
470 	}
471     }
472 
473     return(kret);
474 }
475 
476 static krb5_error_code
477 add_principal(krb5_context context,
478     krb5_principal princ,
479     enum ap_op op,
480     struct realm_info *pblock,
481     krb5_keyblock *mkey)
482 {
483     krb5_error_code 	  retval;
484     krb5_db_entry 	  entry;
485 
486     krb5_timestamp	  now;
487     struct iterate_args	  iargs;
488 
489     int			  nentries = 1;
490 
491     memset((char *) &entry, 0, sizeof(entry));
492 
493     entry.len = KRB5_KDB_V1_BASE_LENGTH;
494     entry.attributes = pblock->flags;
495     entry.max_life = pblock->max_life;
496     entry.max_renewable_life = pblock->max_rlife;
497     entry.expiration = pblock->expiration;
498 
499     if ((retval = krb5_copy_principal(context, princ, &entry.princ)))
500 	goto error_out;
501 
502     if ((retval = krb5_timeofday(context, &now)))
503 	goto error_out;
504 
505     if ((retval = krb5_dbe_update_mod_princ_data(context, &entry,
506 						 now, &db_create_princ)))
507 	goto error_out;
508 
509     switch (op) {
510     case MASTER_KEY:
511 	entry.key_data = (krb5_key_data *) malloc(sizeof (krb5_key_data));
512 	if (entry.key_data == NULL)
513 	    goto error_out;
514 
515 	memset((char *) entry.key_data, 0, sizeof(krb5_key_data));
516 	entry.n_key_data = 1;
517 
518 	entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
519 	if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->key,
520 				  mkey, NULL, 1, entry.key_data)))
521 		goto error_out;
522 	break;
523     case TGT_KEY:
524 	iargs.ctx = context;
525 	iargs.rblock = pblock;
526 	iargs.dbentp = &entry;
527 	/*
528 	 * Iterate through the key/salt list, ignoring salt types.
529 	 */
530 	if ((retval = krb5_keysalt_iterate(pblock->kslist,
531 					   pblock->nkslist,
532 					   1,
533 					   tgt_keysalt_iterate,
534 					   (krb5_pointer) &iargs)))
535 		return (retval);
536 	break;
537     case NULL_KEY:
538 	return (EOPNOTSUPP);
539     default:
540 	break;
541     }
542 
543     retval = krb5_db_put_principal(context, &entry, &nentries);
544 
545 error_out:;
546 	krb5_dbe_free_contents(context, &entry);
547 	return (retval);
548 }
549