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
kdb5_create(argc,argv)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 };
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
tgt_keysalt_iterate(ksent,ptr)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
add_principal(context,princ,op,pblock,mkey)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