xref: /illumos-gate/usr/src/cmd/krb5/kadmin/dbutil/kdb5_create.c (revision 4e93fb0f6383eaac21897dcdae56b87118131e4d)
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 = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL };
98 
99 struct realm_info {
100     krb5_deltat max_life;
101     krb5_deltat max_rlife;
102     krb5_timestamp expiration;
103     krb5_flags flags;
104     krb5_keyblock *key;
105     krb5_int32 nkslist;
106     krb5_key_salt_tuple *kslist;
107 } rblock = { /* XXX */
108     KRB5_KDB_MAX_LIFE,
109     KRB5_KDB_MAX_RLIFE,
110     KRB5_KDB_EXPIRATION,
111     KRB5_KDB_DEF_FLAGS,
112     (krb5_keyblock *) NULL,
113     1,
114     &def_kslist
115 };
116 
117 struct iterate_args {
118     krb5_context	ctx;
119     struct realm_info	*rblock;
120     krb5_db_entry	*dbentp;
121 };
122 
123 static krb5_error_code add_principal
124 	(krb5_context,
125 	 krb5_principal,
126 	 enum ap_op,
127 	 struct realm_info *,
128 		krb5_keyblock *);
129 
130 /*
131  * Steps in creating a database:
132  *
133  * 1) use the db calls to open/create a new database
134  *
135  * 2) get a realm name for the new db
136  *
137  * 3) get a master password for the new db; convert to an encryption key.
138  *
139  * 4) create various required entries in the database
140  *
141  * 5) close & exit
142  */
143 
144 extern krb5_principal master_princ;
145 
146 krb5_data tgt_princ_entries[] = {
147 	{0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
148 	{0, 0, 0} };
149 
150 krb5_data db_creator_entries[] = {
151 	{0, sizeof("db_creation")-1, "db_creation"} };
152 
153 /* XXX knows about contents of krb5_principal, and that tgt names
154  are of form TGT/REALM@REALM */
155 krb5_principal_data tgt_princ = {
156         0,					/* magic number */
157 	{0, 0, 0},				/* krb5_data realm */
158 	tgt_princ_entries,			/* krb5_data *data */
159 	2,					/* int length */
160 	KRB5_NT_SRV_INST			/* int type */
161 };
162 
163 krb5_principal_data db_create_princ = {
164         0,					/* magic number */
165 	{0, 0, 0},				/* krb5_data realm */
166 	db_creator_entries,			/* krb5_data *data */
167 	1,					/* int length */
168 	KRB5_NT_SRV_INST			/* int type */
169 };
170 
171 extern char *mkey_password;
172 
173 extern char *progname;
174 extern int exit_status;
175 extern osa_adb_policy_t policy_db;
176 extern kadm5_config_params global_params;
177 extern krb5_context util_context;
178 
179 void kdb5_create(argc, argv)
180    int argc;
181    char *argv[];
182 {
183     int optchar;
184 
185     krb5_error_code retval;
186     char *mkey_fullname;
187     char *pw_str = 0;
188     unsigned int pw_size = 0;
189     int do_stash = 0;
190     krb5_int32 crflags = KRB5_KDB_CREATE_BTREE;
191     krb5_data pwd, seed;
192     kdb_log_context *log_ctx;
193     krb5_keyblock mkey;
194     krb5_data master_salt = { 0, NULL };
195 
196     if (strrchr(argv[0], '/'))
197 	argv[0] = strrchr(argv[0], '/')+1;
198 
199     while ((optchar = getopt(argc, argv, "s")) != -1) {
200 	switch(optchar) {
201 	case 's':
202 	    do_stash++;
203 	    break;
204 	case 'h':
205 	    crflags = KRB5_KDB_CREATE_HASH;
206 	case '?':
207 	default:
208 	    usage();
209 	    return;
210 	}
211     }
212 
213     rblock.max_life = global_params.max_life;
214     rblock.max_rlife = global_params.max_rlife;
215     rblock.expiration = global_params.expiration;
216     rblock.flags = global_params.flags;
217     rblock.nkslist = global_params.num_keysalts;
218     rblock.kslist = global_params.keysalts;
219 
220     log_ctx = util_context->kdblog_context;
221 
222     retval = krb5_db_set_name(util_context, global_params.dbname);
223     if (!retval) retval = EEXIST;
224 
225     if (retval == EEXIST || retval == EACCES || retval == EPERM) {
226 	/* it exists ! */
227 		com_err(argv[0], 0,
228 			gettext("The database '%s' appears to already exist"),
229 		global_params.dbname);
230 	exit_status++; return;
231     }
232 /* SUNW14resync XXX */
233 #if 0
234     printf ("Loading random data\n");
235     retval = krb5_c_random_os_entropy (util_context, 1, NULL);
236     if (retval) {
237       com_err (argv[0], retval, "Loading random data");
238       exit_status++; return;
239     }
240 #endif
241     /* assemble & parse the master key name */
242 
243     if ((retval = krb5_db_setup_mkey_name(util_context,
244 					  global_params.mkey_name,
245 					  global_params.realm,
246 					  &mkey_fullname, &master_princ))) {
247 	com_err(argv[0], retval,
248 			gettext("while setting up master key name"));
249 	exit_status++; return;
250     }
251 
252     krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm);
253     krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm));
254     krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm);
255     krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm));
256     krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm;
257     krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm);
258 
259 	printf(gettext("Initializing database '%s' for realm '%s',\n"
260 			"master key name '%s'\n"),
261 	   global_params.dbname, global_params.realm, mkey_fullname);
262 
263     if (!mkey_password) {
264 	printf(gettext("You will be prompted for the "
265 			"database Master Password.\n"));
266 	printf(gettext("It is important that you NOT FORGET this password.\n"));
267 	fflush(stdout);
268 
269 	pw_size = 1024;
270 	pw_str = malloc(pw_size);
271 
272 	retval = krb5_read_password(util_context,
273 			    gettext("Enter KDC database master key"),
274 			    gettext("Re-enter KDC database "
275 				    "master key to verify"),
276 			    pw_str, &pw_size);
277 	if (retval) {
278 	    com_err(argv[0], retval,
279 		    gettext("while reading master key from keyboard"));
280 	    exit_status++; return;
281 	}
282 	mkey_password = pw_str;
283     }
284 
285     pwd.data = mkey_password;
286     pwd.length = strlen(mkey_password);
287     retval = krb5_principal2salt(util_context, master_princ, &master_salt);
288     if (retval) {
289 	com_err(argv[0], retval,
290 		gettext("while calculated master key salt"));
291 	exit_status++;
292 	goto cleanup;
293     }
294 
295     retval = krb5_c_string_to_key(util_context, global_params.enctype,
296 				  &pwd, &master_salt, &mkey);
297     if (retval) {
298 	com_err(argv[0], retval,
299 	    gettext("while transforming master key from password"));
300 	exit_status++;
301 	goto cleanup;
302     }
303 
304     retval = krb5_copy_keyblock(util_context, &mkey, &rblock.key);
305     if (retval) {
306 	com_err(argv[0], retval, gettext("while copying master key"));
307 	exit_status++;
308 	goto cleanup;
309     }
310 
311     seed.length = mkey.length;
312     seed.data = (char *)mkey.contents;
313 
314     if ((retval = krb5_c_random_seed(util_context, &seed))) {
315 	com_err(argv[0], retval,
316 		gettext("while initializing random key generator"));
317 	exit_status++;
318 	goto cleanup;
319     }
320     if ((retval = krb5_db_create(util_context,
321 				 global_params.dbname, crflags))) {
322 	com_err(argv[0], retval,
323 		gettext("while creating database '%s'"),
324 		global_params.dbname);
325 	exit_status++;
326 	goto cleanup;
327     }
328     if (retval = krb5_db_fini(util_context)) {
329 	com_err(argv[0], retval,
330 		gettext("while closing current database"));
331 	exit_status++;
332 	goto cleanup;
333     }
334     if ((retval = krb5_db_set_name(util_context, global_params.dbname))) {
335 	com_err(argv[0], retval,
336 		gettext("while setting active database to '%s'"),
337                global_params.dbname);
338 	exit_status++;
339 	goto cleanup;
340     }
341     if ((retval = krb5_db_init(util_context))) {
342 	com_err(argv[0], retval,
343 		gettext("while initializing the database '%s'"),
344 	global_params.dbname);
345 	exit_status++;
346 	goto cleanup;
347     }
348 
349     if (log_ctx && log_ctx->iproprole) {
350 	if (retval = ulog_map(util_context, &global_params, FKCOMMAND)) {
351 		com_err(argv[0], retval,
352 			gettext("while creating update log"));
353 		exit_status++;
354 		goto cleanup;
355 	}
356 
357 	/*
358 	 * We're reinitializing the update log in case one already
359 	 * existed, but this should never happen.
360 	 */
361 	(void) memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t));
362 
363 	log_ctx->ulog->kdb_hmagic = KDB_HMAGIC;
364 	log_ctx->ulog->db_version_num = KDB_VERSION;
365 	log_ctx->ulog->kdb_state = KDB_STABLE;
366 	log_ctx->ulog->kdb_block = ULOG_BLOCK;
367 
368 	/*
369 	 * Since we're creating a new db we shouldn't worry about
370 	 * adding the initial principals since any slave might as well
371 	 * do full resyncs from this newly created db.
372 	 */
373 	log_ctx->iproprole = IPROP_NULL;
374     }
375 
376     if ((retval = add_principal(util_context,
377 		    master_princ, MASTER_KEY, &rblock, &mkey)) ||
378 	(retval = add_principal(util_context,
379 			    &tgt_princ, TGT_KEY, &rblock, &mkey))) {
380 	(void) krb5_db_fini(util_context);
381 	com_err(argv[0], retval,
382 	    gettext("while adding entries to the database"));
383 	exit_status++;
384 	goto cleanup;
385     }
386     /*
387      * Always stash the master key so kadm5_create does not prompt for
388      * it; delete the file below if it was not requested.  DO NOT EXIT
389      * BEFORE DELETING THE KEYFILE if do_stash is not set.
390      */
391     retval = krb5_db_store_mkey(util_context,
392 			    global_params.stash_file,
393 			    master_princ,
394 			    &mkey);
395     if (retval) {
396 	com_err(argv[0], errno, gettext("while storing key"));
397 	printf(gettext("Warning: couldn't stash master key.\n"));
398     }
399 
400     if (pw_str)
401 	memset(pw_str, 0, pw_size);
402 
403     if (kadm5_create(&global_params)) {
404 	 if (!do_stash) unlink(global_params.stash_file);
405 	 exit_status++;
406 	 goto cleanup;
407     }
408     if (!do_stash) unlink(global_params.stash_file);
409 
410 cleanup:
411     if (pw_str) {
412 	if (mkey_password == pw_str)
413 		mkey_password = NULL;
414 	free(pw_str);
415     }
416     if (master_salt.data)
417 	free(master_salt.data);
418     krb5_free_keyblock_contents(util_context, rblock.key);
419     krb5_free_keyblock_contents(util_context, &mkey);
420     (void) krb5_db_fini(util_context);
421 
422     return;
423 }
424 
425 static krb5_error_code
426 tgt_keysalt_iterate(ksent, ptr)
427     krb5_key_salt_tuple	*ksent;
428     krb5_pointer	ptr;
429 {
430     krb5_context	context;
431     krb5_error_code	kret;
432     struct iterate_args	*iargs;
433     krb5_keyblock	key;
434     krb5_int32		ind;
435     krb5_data	pwd;
436 
437     iargs = (struct iterate_args *) ptr;
438     kret = 0;
439 
440     context = iargs->ctx;
441 
442     /*
443      * Convert the master key password into a key for this particular
444      * encryption system.
445      */
446     pwd.data = mkey_password;
447     pwd.length = strlen(mkey_password);
448     kret = krb5_c_random_seed(context, &pwd);
449     if (kret)
450 	return kret;
451 
452     if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
453 	ind = iargs->dbentp->n_key_data-1;
454 	if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype,
455 					    &key))) {
456 	    kret = krb5_dbekd_encrypt_key_data(context,
457 					       iargs->rblock->key,
458 					       &key,
459 					       NULL,
460 					       1,
461 					       &iargs->dbentp->key_data[ind]);
462 	    krb5_free_keyblock_contents(context, &key);
463 	}
464     }
465 
466     return(kret);
467 }
468 
469 static krb5_error_code
470 add_principal(context, princ, op, pblock, mkey)
471     krb5_context context;
472     krb5_principal princ;
473     enum ap_op op;
474     struct realm_info *pblock;
475     krb5_keyblock *mkey;
476 {
477     krb5_error_code 	  retval;
478     krb5_db_entry 	  entry;
479 
480     krb5_timestamp	  now;
481     struct iterate_args	  iargs;
482 
483     int			  nentries = 1;
484 
485     memset((char *) &entry, 0, sizeof(entry));
486 
487     entry.len = KRB5_KDB_V1_BASE_LENGTH;
488     entry.attributes = pblock->flags;
489     entry.max_life = pblock->max_life;
490     entry.max_renewable_life = pblock->max_rlife;
491     entry.expiration = pblock->expiration;
492 
493     if ((retval = krb5_copy_principal(context, princ, &entry.princ)))
494 	goto error_out;
495 
496     if ((retval = krb5_timeofday(context, &now)))
497 	goto error_out;
498 
499     if ((retval = krb5_dbe_update_mod_princ_data(context, &entry,
500 						 now, &db_create_princ)))
501 	goto error_out;
502 
503     switch (op) {
504     case MASTER_KEY:
505 	if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data)))
506 	    == NULL)
507 	    goto error_out;
508 	memset((char *) entry.key_data, 0, sizeof(krb5_key_data));
509 	entry.n_key_data = 1;
510 
511 	entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
512 	if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->key,
513 						  mkey, NULL,
514 						  1, entry.key_data)))
515 	    goto error_out;
516 	break;
517     case TGT_KEY:
518 	iargs.ctx = context;
519 	iargs.rblock = pblock;
520 	iargs.dbentp = &entry;
521 	/*
522 	 * Iterate through the key/salt list, ignoring salt types.
523 	 */
524 	if ((retval = krb5_keysalt_iterate(pblock->kslist,
525 					   pblock->nkslist,
526 					   1,
527 					   tgt_keysalt_iterate,
528 					   (krb5_pointer) &iargs)))
529 	    return retval;
530 	break;
531     case NULL_KEY:
532 	return EOPNOTSUPP;
533     default:
534 	break;
535     }
536 
537     retval = krb5_db_put_principal(context, &entry, &nentries);
538 
539 error_out:;
540     krb5_dbe_free_contents(context, &entry);
541     return retval;
542 }
543