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