xref: /freebsd/crypto/krb5/src/kadmin/dbutil/kdb5_create.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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