1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kadmin/dbutil/kdb5_create.c - Create a KDC database */
3 /*
4 * Copyright 1990,1991,2001, 2002, 2008 by the Massachusetts Institute of
5 * Technology. All Rights Reserved.
6 *
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
11 *
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
25 */
26 /*
27 * Copyright (C) 1998 by the FundsXpress, INC.
28 *
29 * All rights reserved.
30 *
31 * Export of this software from the United States of America may require
32 * a specific license from the United States Government. It is the
33 * responsibility of any person or organization contemplating export to
34 * obtain such a license before exporting.
35 *
36 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
37 * distribute this software and its documentation for any purpose and
38 * without fee is hereby granted, provided that the above copyright
39 * notice appear in all copies and that both that copyright notice and
40 * this permission notice appear in supporting documentation, and that
41 * the name of FundsXpress. not be used in advertising or publicity pertaining
42 * to distribution of the software without specific, written prior
43 * permission. FundsXpress makes no representations about the suitability of
44 * this software for any purpose. It is provided "as is" without express
45 * or implied warranty.
46 *
47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50 */
51 /*
52 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
53 * Use is subject to license terms.
54 */
55
56 #include <k5-int.h>
57 #include <kdb.h>
58 #include <kadm5/server_internal.h>
59 #include <kadm5/admin.h>
60 #include <adm_proto.h>
61 #include "kdb5_util.h"
62
63 enum ap_op {
64 NULL_KEY, /* setup null keys */
65 MASTER_KEY, /* use master key as new key */
66 TGT_KEY /* special handling for tgt key */
67 };
68
69 struct realm_info {
70 krb5_deltat max_life;
71 krb5_deltat max_rlife;
72 krb5_timestamp expiration;
73 krb5_flags flags;
74 krb5_keyblock *key;
75 krb5_int32 nkslist;
76 krb5_key_salt_tuple *kslist;
77 } rblock;
78
79 struct iterate_args {
80 krb5_context ctx;
81 struct realm_info *rblock;
82 krb5_db_entry *dbentp;
83 };
84
85 static krb5_error_code add_principal
86 (krb5_context,
87 krb5_principal,
88 enum ap_op,
89 struct realm_info *);
90
91 /*
92 * Steps in creating a database:
93 *
94 * 1) use the db calls to open/create a new database
95 *
96 * 2) get a realm name for the new db
97 *
98 * 3) get a master password for the new db; convert to an encryption key.
99 *
100 * 4) create various required entries in the database
101 *
102 * 5) close & exit
103 */
104
105 extern krb5_keyblock master_keyblock;
106 extern krb5_principal master_princ;
107 extern char *mkey_fullname;
108 krb5_data master_salt;
109
110 krb5_data tgt_princ_entries[] = {
111 {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
112 {0, 0, 0} };
113
114 krb5_data db_creator_entries[] = {
115 {0, sizeof("db_creation")-1, "db_creation"} };
116
117 /* XXX knows about contents of krb5_principal, and that tgt names
118 are of form TGT/REALM@REALM */
119 krb5_principal_data tgt_princ = {
120 0, /* magic number */
121 {0, 0, 0}, /* krb5_data realm */
122 tgt_princ_entries, /* krb5_data *data */
123 2, /* int length */
124 KRB5_NT_SRV_INST /* int type */
125 };
126
127 krb5_principal_data db_create_princ = {
128 0, /* magic number */
129 {0, 0, 0}, /* krb5_data realm */
130 db_creator_entries, /* krb5_data *data */
131 1, /* int length */
132 KRB5_NT_SRV_INST /* int type */
133 };
134
135 extern char *mkey_password;
136
137 extern char *progname;
138 extern int exit_status;
139 extern kadm5_config_params global_params;
140 extern krb5_context util_context;
141
142 void
kdb5_create(int argc,char * argv[])143 kdb5_create(int argc, char *argv[])
144 {
145 int optchar;
146
147 krb5_error_code retval;
148 char *pw_str = 0;
149 unsigned int pw_size = 0;
150 int do_stash = 0;
151 krb5_data pwd, seed;
152 kdb_log_context *log_ctx;
153 krb5_kvno mkey_kvno;
154
155 while ((optchar = getopt(argc, argv, "sW")) != -1) {
156 switch(optchar) {
157 case 's':
158 do_stash++;
159 break;
160 case 'W':
161 /* Ignore (deprecated weak random option). */
162 break;
163 case '?':
164 default:
165 usage();
166 return;
167 }
168 }
169
170 rblock.max_life = global_params.max_life;
171 rblock.max_rlife = global_params.max_rlife;
172 rblock.expiration = global_params.expiration;
173 rblock.flags = global_params.flags;
174 rblock.nkslist = global_params.num_keysalts;
175 rblock.kslist = global_params.keysalts;
176
177 log_ctx = util_context->kdblog_context;
178
179 /* assemble & parse the master key name */
180
181 if ((retval = krb5_db_setup_mkey_name(util_context,
182 global_params.mkey_name,
183 global_params.realm,
184 &mkey_fullname, &master_princ))) {
185 com_err(progname, retval, _("while setting up master key name"));
186 exit_status++; return;
187 }
188
189 krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm);
190 krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm));
191 krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm);
192 krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm));
193 krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm;
194 krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm);
195
196 printf(_("Initializing database '%s' for realm '%s',\n"
197 "master key name '%s'\n"),
198 global_params.dbname, global_params.realm, mkey_fullname);
199
200 if (!mkey_password) {
201 printf(_("You will be prompted for the database Master Password.\n"));
202 printf(_("It is important that you NOT FORGET this password.\n"));
203 fflush(stdout);
204
205 pw_size = 1024;
206 pw_str = malloc(pw_size);
207 if (pw_str == NULL) {
208 com_err(progname, ENOMEM, _("while creating new master key"));
209 exit_status++; return;
210 }
211
212 retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
213 pw_str, &pw_size);
214 if (retval) {
215 com_err(progname, retval,
216 _("while reading master key from keyboard"));
217 exit_status++; return;
218 }
219 mkey_password = pw_str;
220 }
221
222 pwd.data = mkey_password;
223 pwd.length = strlen(mkey_password);
224 retval = krb5_principal2salt(util_context, master_princ, &master_salt);
225 if (retval) {
226 com_err(progname, retval, _("while calculating master key salt"));
227 exit_status++; return;
228 }
229
230 retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,
231 &pwd, &master_salt, &master_keyblock);
232 if (retval) {
233 com_err(progname, retval,
234 _("while transforming master key from password"));
235 exit_status++; return;
236 }
237
238 rblock.key = &master_keyblock;
239
240 seed = make_data(master_keyblock.contents, master_keyblock.length);
241
242 if ((retval = krb5_c_random_seed(util_context, &seed))) {
243 com_err(progname, retval,
244 _("while initializing random key generator"));
245 exit_status++; return;
246 }
247 if ((retval = krb5_db_create(util_context,
248 db5util_db_args))) {
249 com_err(progname, retval, _("while creating database '%s'"),
250 global_params.dbname);
251 exit_status++; return;
252 }
253 /* if ((retval = krb5_db_fini(util_context))) { */
254 /* com_err(progname, retval, "while closing current database"); */
255 /* exit_status++; return; */
256 /* } */
257 /* if ((retval = krb5_db_open(util_context, db5util_db_args, KRB5_KDB_OPEN_RW))) { */
258 /* com_err(progname, retval, "while initializing the database '%s'", */
259 /* global_params.dbname); */
260 /* exit_status++; return; */
261 /* } */
262
263 if (log_ctx && log_ctx->iproprole) {
264 retval = ulog_map(util_context, global_params.iprop_logfile,
265 global_params.iprop_ulogsize);
266 if (retval) {
267 com_err(argv[0], retval, _("while creating update log"));
268 exit_status++;
269 return;
270 }
271
272 /*
273 * We're reinitializing the update log in case one already
274 * existed, but this should never happen.
275 */
276 retval = ulog_init_header(util_context);
277 if (retval) {
278 com_err(argv[0], retval, _("while initializing update log"));
279 exit_status++;
280 return;
281 }
282
283 /*
284 * Since we're creating a new db we shouldn't worry about
285 * adding the initial principals since any replica might as
286 * well do full resyncs from this newly created db.
287 */
288 log_ctx->iproprole = IPROP_NULL;
289 }
290
291 if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) ||
292 (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) {
293 com_err(progname, retval, _("while adding entries to the database"));
294 exit_status++; return;
295 }
296
297
298
299 /*
300 * Always stash the master key so kadm5_create does not prompt for
301 * it; delete the file below if it was not requested. DO NOT EXIT
302 * BEFORE DELETING THE KEYFILE if do_stash is not set.
303 */
304
305 /*
306 * Determine the kvno to use, it must be that used to create the master key
307 * princ.
308 */
309 if (global_params.mask & KADM5_CONFIG_KVNO)
310 mkey_kvno = global_params.kvno; /* user specified */
311 else
312 mkey_kvno = 1; /* Default */
313
314 retval = krb5_db_store_master_key(util_context,
315 global_params.stash_file,
316 master_princ,
317 mkey_kvno,
318 &master_keyblock,
319 mkey_password);
320 if (retval) {
321 com_err(progname, retval, _("while storing key"));
322 printf(_("Warning: couldn't stash master key.\n"));
323 }
324 /* clean up */
325 zapfree(pw_str, pw_size);
326 free(master_salt.data);
327
328 if (kadm5_create(&global_params)) {
329 if (!do_stash) unlink(global_params.stash_file);
330 exit_status++;
331 return;
332 }
333 if (!do_stash) unlink(global_params.stash_file);
334
335 return;
336 }
337
338 static krb5_error_code
tgt_keysalt_iterate(krb5_key_salt_tuple * ksent,krb5_pointer ptr)339 tgt_keysalt_iterate(krb5_key_salt_tuple *ksent, krb5_pointer ptr)
340 {
341 krb5_context context;
342 krb5_error_code kret;
343 struct iterate_args *iargs;
344 krb5_keyblock key;
345 krb5_int32 ind;
346 krb5_data pwd;
347
348 iargs = (struct iterate_args *) ptr;
349 kret = 0;
350
351 context = iargs->ctx;
352
353 /*
354 * Convert the master key password into a key for this particular
355 * encryption system.
356 */
357 pwd.data = mkey_password;
358 pwd.length = strlen(mkey_password);
359 kret = krb5_c_random_seed(context, &pwd);
360 if (kret)
361 return kret;
362
363 if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
364 ind = iargs->dbentp->n_key_data-1;
365 if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype,
366 &key))) {
367 kret = krb5_dbe_encrypt_key_data(context, iargs->rblock->key,
368 &key, NULL, 1,
369 &iargs->dbentp->key_data[ind]);
370 krb5_free_keyblock_contents(context, &key);
371 }
372 }
373
374 return(kret);
375 }
376
377 static krb5_error_code
add_principal(krb5_context context,krb5_principal princ,enum ap_op op,struct realm_info * pblock)378 add_principal(krb5_context context, krb5_principal princ, enum ap_op op,
379 struct realm_info *pblock)
380 {
381 krb5_error_code retval;
382 krb5_db_entry *entry = NULL;
383 krb5_kvno mkey_kvno;
384 krb5_timestamp now;
385 struct iterate_args iargs;
386 krb5_actkvno_node actkvno;
387
388 entry = calloc(1, sizeof(*entry));
389 if (entry == NULL)
390 return ENOMEM;
391
392 entry->len = KRB5_KDB_V1_BASE_LENGTH;
393 entry->attributes = pblock->flags;
394 entry->max_life = pblock->max_life;
395 entry->max_renewable_life = pblock->max_rlife;
396 entry->expiration = pblock->expiration;
397
398 if ((retval = krb5_copy_principal(context, princ, &entry->princ)))
399 goto cleanup;
400
401 if ((retval = krb5_timeofday(context, &now)))
402 goto cleanup;
403
404 if ((retval = krb5_dbe_update_mod_princ_data(context, entry,
405 now, &db_create_princ)))
406 goto cleanup;
407
408 switch (op) {
409 case MASTER_KEY:
410 if ((entry->key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data)))
411 == NULL)
412 goto cleanup;
413 memset(entry->key_data, 0, sizeof(krb5_key_data));
414 entry->n_key_data = 1;
415
416 if (global_params.mask & KADM5_CONFIG_KVNO)
417 mkey_kvno = global_params.kvno; /* user specified */
418 else
419 mkey_kvno = 1; /* Default */
420 entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
421 if ((retval = krb5_dbe_encrypt_key_data(context, pblock->key,
422 &master_keyblock, NULL,
423 mkey_kvno, entry->key_data)))
424 goto cleanup;
425 /*
426 * There should always be at least one "active" mkey so creating the
427 * KRB5_TL_ACTKVNO entry now so the initial mkey is active.
428 */
429 actkvno.next = NULL;
430 actkvno.act_kvno = mkey_kvno;
431 /* earliest possible time in case system clock is set back */
432 actkvno.act_time = 0;
433 if ((retval = krb5_dbe_update_actkvno(context, entry, &actkvno)))
434 goto cleanup;
435
436 /* so getprinc shows the right kvno */
437 if ((retval = krb5_dbe_update_mkvno(context, entry, mkey_kvno)))
438 goto cleanup;
439
440 break;
441 case TGT_KEY:
442 iargs.ctx = context;
443 iargs.rblock = pblock;
444 iargs.dbentp = entry;
445 /*
446 * Iterate through the key/salt list, ignoring salt types.
447 */
448 if ((retval = krb5_keysalt_iterate(pblock->kslist,
449 pblock->nkslist,
450 1,
451 tgt_keysalt_iterate,
452 (krb5_pointer) &iargs)))
453 goto cleanup;
454 break;
455 case NULL_KEY:
456 retval = EOPNOTSUPP;
457 goto cleanup;
458 default:
459 break;
460 }
461
462 entry->mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
463 KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA |
464 KADM5_PRINC_EXPIRE_TIME);
465 entry->attributes |= KRB5_KDB_LOCKDOWN_KEYS;
466
467 retval = krb5_db_put_principal(context, entry);
468
469 cleanup:
470 krb5_db_free_principal(context, entry);
471 return retval;
472 }
473