xref: /freebsd/crypto/krb5/src/kadmin/dbutil/kdb5_create.c (revision 4b15965daa99044daf184221b7c283bf7f2d7e66)
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(argc, argv)
143     int argc;
144     char *argv[];
145 {
146     int optchar;
147 
148     krb5_error_code retval;
149     char *pw_str = 0;
150     unsigned int pw_size = 0;
151     int do_stash = 0;
152     krb5_data pwd, seed;
153     kdb_log_context *log_ctx;
154     krb5_kvno mkey_kvno;
155 
156     while ((optchar = getopt(argc, argv, "sW")) != -1) {
157         switch(optchar) {
158         case 's':
159             do_stash++;
160             break;
161         case 'W':
162             /* Ignore (deprecated weak random option). */
163             break;
164         case '?':
165         default:
166             usage();
167             return;
168         }
169     }
170 
171     rblock.max_life = global_params.max_life;
172     rblock.max_rlife = global_params.max_rlife;
173     rblock.expiration = global_params.expiration;
174     rblock.flags = global_params.flags;
175     rblock.nkslist = global_params.num_keysalts;
176     rblock.kslist = global_params.keysalts;
177 
178     log_ctx = util_context->kdblog_context;
179 
180     /* assemble & parse the master key name */
181 
182     if ((retval = krb5_db_setup_mkey_name(util_context,
183                                           global_params.mkey_name,
184                                           global_params.realm,
185                                           &mkey_fullname, &master_princ))) {
186         com_err(progname, retval, _("while setting up master key name"));
187         exit_status++; return;
188     }
189 
190     krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm);
191     krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm));
192     krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm);
193     krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm));
194     krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm;
195     krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm);
196 
197     printf(_("Initializing database '%s' for realm '%s',\n"
198              "master key name '%s'\n"),
199            global_params.dbname, global_params.realm, mkey_fullname);
200 
201     if (!mkey_password) {
202         printf(_("You will be prompted for the database Master Password.\n"));
203         printf(_("It is important that you NOT FORGET this password.\n"));
204         fflush(stdout);
205 
206         pw_size = 1024;
207         pw_str = malloc(pw_size);
208         if (pw_str == NULL) {
209             com_err(progname, ENOMEM, _("while creating new master key"));
210             exit_status++; return;
211         }
212 
213         retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
214                                     pw_str, &pw_size);
215         if (retval) {
216             com_err(progname, retval,
217                     _("while reading master key from keyboard"));
218             exit_status++; return;
219         }
220         mkey_password = pw_str;
221     }
222 
223     pwd.data = mkey_password;
224     pwd.length = strlen(mkey_password);
225     retval = krb5_principal2salt(util_context, master_princ, &master_salt);
226     if (retval) {
227         com_err(progname, retval, _("while calculating master key salt"));
228         exit_status++; return;
229     }
230 
231     retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,
232                                   &pwd, &master_salt, &master_keyblock);
233     if (retval) {
234         com_err(progname, retval,
235                 _("while transforming master key from password"));
236         exit_status++; return;
237     }
238 
239     rblock.key = &master_keyblock;
240 
241     seed = make_data(master_keyblock.contents, master_keyblock.length);
242 
243     if ((retval = krb5_c_random_seed(util_context, &seed))) {
244         com_err(progname, retval,
245                 _("while initializing random key generator"));
246         exit_status++; return;
247     }
248     if ((retval = krb5_db_create(util_context,
249                                  db5util_db_args))) {
250         com_err(progname, retval, _("while creating database '%s'"),
251                 global_params.dbname);
252         exit_status++; return;
253     }
254 /*     if ((retval = krb5_db_fini(util_context))) { */
255 /*         com_err(progname, retval, "while closing current database"); */
256 /*         exit_status++; return; */
257 /*     } */
258 /*     if ((retval = krb5_db_open(util_context, db5util_db_args, KRB5_KDB_OPEN_RW))) { */
259 /*      com_err(progname, retval, "while initializing the database '%s'", */
260 /*              global_params.dbname); */
261 /*      exit_status++; return; */
262 /*     } */
263 
264     if (log_ctx && log_ctx->iproprole) {
265         retval = ulog_map(util_context, global_params.iprop_logfile,
266                           global_params.iprop_ulogsize);
267         if (retval) {
268             com_err(argv[0], retval, _("while creating update log"));
269             exit_status++;
270             return;
271         }
272 
273         /*
274          * We're reinitializing the update log in case one already
275          * existed, but this should never happen.
276          */
277         retval = ulog_init_header(util_context);
278         if (retval) {
279             com_err(argv[0], retval, _("while initializing update log"));
280             exit_status++;
281             return;
282         }
283 
284         /*
285          * Since we're creating a new db we shouldn't worry about
286          * adding the initial principals since any replica might as
287          * well do full resyncs from this newly created db.
288          */
289         log_ctx->iproprole = IPROP_NULL;
290     }
291 
292     if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) ||
293         (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) {
294         com_err(progname, retval, _("while adding entries to the database"));
295         exit_status++; return;
296     }
297 
298 
299 
300     /*
301      * Always stash the master key so kadm5_create does not prompt for
302      * it; delete the file below if it was not requested.  DO NOT EXIT
303      * BEFORE DELETING THE KEYFILE if do_stash is not set.
304      */
305 
306     /*
307      * Determine the kvno to use, it must be that used to create the master key
308      * princ.
309      */
310     if (global_params.mask & KADM5_CONFIG_KVNO)
311         mkey_kvno = global_params.kvno; /* user specified */
312     else
313         mkey_kvno = 1;  /* Default */
314 
315     retval = krb5_db_store_master_key(util_context,
316                                       global_params.stash_file,
317                                       master_princ,
318                                       mkey_kvno,
319                                       &master_keyblock,
320                                       mkey_password);
321     if (retval) {
322         com_err(progname, retval, _("while storing key"));
323         printf(_("Warning: couldn't stash master key.\n"));
324     }
325     /* clean up */
326     zapfree(pw_str, pw_size);
327     free(master_salt.data);
328 
329     if (kadm5_create(&global_params)) {
330         if (!do_stash) unlink(global_params.stash_file);
331         exit_status++;
332         return;
333     }
334     if (!do_stash) unlink(global_params.stash_file);
335 
336     return;
337 }
338 
339 static krb5_error_code
340 tgt_keysalt_iterate(ksent, ptr)
341     krb5_key_salt_tuple *ksent;
342     krb5_pointer        ptr;
343 {
344     krb5_context        context;
345     krb5_error_code     kret;
346     struct iterate_args *iargs;
347     krb5_keyblock       key;
348     krb5_int32          ind;
349     krb5_data   pwd;
350 
351     iargs = (struct iterate_args *) ptr;
352     kret = 0;
353 
354     context = iargs->ctx;
355 
356     /*
357      * Convert the master key password into a key for this particular
358      * encryption system.
359      */
360     pwd.data = mkey_password;
361     pwd.length = strlen(mkey_password);
362     kret = krb5_c_random_seed(context, &pwd);
363     if (kret)
364         return kret;
365 
366     if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
367         ind = iargs->dbentp->n_key_data-1;
368         if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype,
369                                             &key))) {
370             kret = krb5_dbe_encrypt_key_data(context, iargs->rblock->key,
371                                              &key, NULL, 1,
372                                              &iargs->dbentp->key_data[ind]);
373             krb5_free_keyblock_contents(context, &key);
374         }
375     }
376 
377     return(kret);
378 }
379 
380 static krb5_error_code
381 add_principal(context, princ, op, pblock)
382     krb5_context context;
383     krb5_principal princ;
384     enum ap_op op;
385     struct realm_info *pblock;
386 {
387     krb5_error_code       retval;
388     krb5_db_entry         *entry = NULL;
389     krb5_kvno             mkey_kvno;
390     krb5_timestamp        now;
391     struct iterate_args   iargs;
392     krb5_actkvno_node     actkvno;
393 
394     entry = calloc(1, sizeof(*entry));
395     if (entry == NULL)
396         return ENOMEM;
397 
398     entry->len = KRB5_KDB_V1_BASE_LENGTH;
399     entry->attributes = pblock->flags;
400     entry->max_life = pblock->max_life;
401     entry->max_renewable_life = pblock->max_rlife;
402     entry->expiration = pblock->expiration;
403 
404     if ((retval = krb5_copy_principal(context, princ, &entry->princ)))
405         goto cleanup;
406 
407     if ((retval = krb5_timeofday(context, &now)))
408         goto cleanup;
409 
410     if ((retval = krb5_dbe_update_mod_princ_data(context, entry,
411                                                  now, &db_create_princ)))
412         goto cleanup;
413 
414     switch (op) {
415     case MASTER_KEY:
416         if ((entry->key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data)))
417             == NULL)
418             goto cleanup;
419         memset(entry->key_data, 0, sizeof(krb5_key_data));
420         entry->n_key_data = 1;
421 
422         if (global_params.mask & KADM5_CONFIG_KVNO)
423             mkey_kvno = global_params.kvno; /* user specified */
424         else
425             mkey_kvno = 1;  /* Default */
426         entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
427         if ((retval = krb5_dbe_encrypt_key_data(context, pblock->key,
428                                                 &master_keyblock, NULL,
429                                                 mkey_kvno, entry->key_data)))
430             goto cleanup;
431         /*
432          * There should always be at least one "active" mkey so creating the
433          * KRB5_TL_ACTKVNO entry now so the initial mkey is active.
434          */
435         actkvno.next = NULL;
436         actkvno.act_kvno = mkey_kvno;
437         /* earliest possible time in case system clock is set back */
438         actkvno.act_time = 0;
439         if ((retval = krb5_dbe_update_actkvno(context, entry, &actkvno)))
440             goto cleanup;
441 
442         /* so getprinc shows the right kvno */
443         if ((retval = krb5_dbe_update_mkvno(context, entry, mkey_kvno)))
444             goto cleanup;
445 
446         break;
447     case TGT_KEY:
448         iargs.ctx = context;
449         iargs.rblock = pblock;
450         iargs.dbentp = entry;
451         /*
452          * Iterate through the key/salt list, ignoring salt types.
453          */
454         if ((retval = krb5_keysalt_iterate(pblock->kslist,
455                                            pblock->nkslist,
456                                            1,
457                                            tgt_keysalt_iterate,
458                                            (krb5_pointer) &iargs)))
459             goto cleanup;
460         break;
461     case NULL_KEY:
462         retval = EOPNOTSUPP;
463         goto cleanup;
464     default:
465         break;
466     }
467 
468     entry->mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
469                    KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA |
470                    KADM5_PRINC_EXPIRE_TIME);
471     entry->attributes |= KRB5_KDB_LOCKDOWN_KEYS;
472 
473     retval = krb5_db_put_principal(context, entry);
474 
475 cleanup:
476     krb5_db_free_principal(context, entry);
477     return retval;
478 }
479