Path: blob/main/crypto/krb5/src/kadmin/dbutil/kdb5_create.c
34907 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* kadmin/dbutil/kdb5_create.c - Create a KDC database */2/*3* Copyright 1990,1991,2001, 2002, 2008 by the Massachusetts Institute of4* Technology. All Rights Reserved.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/25/*26* Copyright (C) 1998 by the FundsXpress, INC.27*28* All rights reserved.29*30* Export of this software from the United States of America may require31* a specific license from the United States Government. It is the32* responsibility of any person or organization contemplating export to33* obtain such a license before exporting.34*35* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and36* distribute this software and its documentation for any purpose and37* without fee is hereby granted, provided that the above copyright38* notice appear in all copies and that both that copyright notice and39* this permission notice appear in supporting documentation, and that40* the name of FundsXpress. not be used in advertising or publicity pertaining41* to distribution of the software without specific, written prior42* permission. FundsXpress makes no representations about the suitability of43* this software for any purpose. It is provided "as is" without express44* or implied warranty.45*46* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR47* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED48* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.49*/50/*51* Copyright 2004 Sun Microsystems, Inc. All rights reserved.52* Use is subject to license terms.53*/5455#include <k5-int.h>56#include <kdb.h>57#include <kadm5/server_internal.h>58#include <kadm5/admin.h>59#include <adm_proto.h>60#include "kdb5_util.h"6162enum ap_op {63NULL_KEY, /* setup null keys */64MASTER_KEY, /* use master key as new key */65TGT_KEY /* special handling for tgt key */66};6768struct realm_info {69krb5_deltat max_life;70krb5_deltat max_rlife;71krb5_timestamp expiration;72krb5_flags flags;73krb5_keyblock *key;74krb5_int32 nkslist;75krb5_key_salt_tuple *kslist;76} rblock;7778struct iterate_args {79krb5_context ctx;80struct realm_info *rblock;81krb5_db_entry *dbentp;82};8384static krb5_error_code add_principal85(krb5_context,86krb5_principal,87enum ap_op,88struct realm_info *);8990/*91* Steps in creating a database:92*93* 1) use the db calls to open/create a new database94*95* 2) get a realm name for the new db96*97* 3) get a master password for the new db; convert to an encryption key.98*99* 4) create various required entries in the database100*101* 5) close & exit102*/103104extern krb5_keyblock master_keyblock;105extern krb5_principal master_princ;106extern char *mkey_fullname;107krb5_data master_salt;108109krb5_data tgt_princ_entries[] = {110{0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},111{0, 0, 0} };112113krb5_data db_creator_entries[] = {114{0, sizeof("db_creation")-1, "db_creation"} };115116/* XXX knows about contents of krb5_principal, and that tgt names117are of form TGT/REALM@REALM */118krb5_principal_data tgt_princ = {1190, /* magic number */120{0, 0, 0}, /* krb5_data realm */121tgt_princ_entries, /* krb5_data *data */1222, /* int length */123KRB5_NT_SRV_INST /* int type */124};125126krb5_principal_data db_create_princ = {1270, /* magic number */128{0, 0, 0}, /* krb5_data realm */129db_creator_entries, /* krb5_data *data */1301, /* int length */131KRB5_NT_SRV_INST /* int type */132};133134extern char *mkey_password;135136extern char *progname;137extern int exit_status;138extern kadm5_config_params global_params;139extern krb5_context util_context;140141void142kdb5_create(int argc, char *argv[])143{144int optchar;145146krb5_error_code retval;147char *pw_str = 0;148unsigned int pw_size = 0;149int do_stash = 0;150krb5_data pwd, seed;151kdb_log_context *log_ctx;152krb5_kvno mkey_kvno;153154while ((optchar = getopt(argc, argv, "sW")) != -1) {155switch(optchar) {156case 's':157do_stash++;158break;159case 'W':160/* Ignore (deprecated weak random option). */161break;162case '?':163default:164usage();165return;166}167}168169rblock.max_life = global_params.max_life;170rblock.max_rlife = global_params.max_rlife;171rblock.expiration = global_params.expiration;172rblock.flags = global_params.flags;173rblock.nkslist = global_params.num_keysalts;174rblock.kslist = global_params.keysalts;175176log_ctx = util_context->kdblog_context;177178/* assemble & parse the master key name */179180if ((retval = krb5_db_setup_mkey_name(util_context,181global_params.mkey_name,182global_params.realm,183&mkey_fullname, &master_princ))) {184com_err(progname, retval, _("while setting up master key name"));185exit_status++; return;186}187188krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm);189krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm));190krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm);191krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm));192krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm;193krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm);194195printf(_("Initializing database '%s' for realm '%s',\n"196"master key name '%s'\n"),197global_params.dbname, global_params.realm, mkey_fullname);198199if (!mkey_password) {200printf(_("You will be prompted for the database Master Password.\n"));201printf(_("It is important that you NOT FORGET this password.\n"));202fflush(stdout);203204pw_size = 1024;205pw_str = malloc(pw_size);206if (pw_str == NULL) {207com_err(progname, ENOMEM, _("while creating new master key"));208exit_status++; return;209}210211retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,212pw_str, &pw_size);213if (retval) {214com_err(progname, retval,215_("while reading master key from keyboard"));216exit_status++; return;217}218mkey_password = pw_str;219}220221pwd.data = mkey_password;222pwd.length = strlen(mkey_password);223retval = krb5_principal2salt(util_context, master_princ, &master_salt);224if (retval) {225com_err(progname, retval, _("while calculating master key salt"));226exit_status++; return;227}228229retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,230&pwd, &master_salt, &master_keyblock);231if (retval) {232com_err(progname, retval,233_("while transforming master key from password"));234exit_status++; return;235}236237rblock.key = &master_keyblock;238239seed = make_data(master_keyblock.contents, master_keyblock.length);240241if ((retval = krb5_c_random_seed(util_context, &seed))) {242com_err(progname, retval,243_("while initializing random key generator"));244exit_status++; return;245}246if ((retval = krb5_db_create(util_context,247db5util_db_args))) {248com_err(progname, retval, _("while creating database '%s'"),249global_params.dbname);250exit_status++; return;251}252/* if ((retval = krb5_db_fini(util_context))) { */253/* com_err(progname, retval, "while closing current database"); */254/* exit_status++; return; */255/* } */256/* if ((retval = krb5_db_open(util_context, db5util_db_args, KRB5_KDB_OPEN_RW))) { */257/* com_err(progname, retval, "while initializing the database '%s'", */258/* global_params.dbname); */259/* exit_status++; return; */260/* } */261262if (log_ctx && log_ctx->iproprole) {263retval = ulog_map(util_context, global_params.iprop_logfile,264global_params.iprop_ulogsize);265if (retval) {266com_err(argv[0], retval, _("while creating update log"));267exit_status++;268return;269}270271/*272* We're reinitializing the update log in case one already273* existed, but this should never happen.274*/275retval = ulog_init_header(util_context);276if (retval) {277com_err(argv[0], retval, _("while initializing update log"));278exit_status++;279return;280}281282/*283* Since we're creating a new db we shouldn't worry about284* adding the initial principals since any replica might as285* well do full resyncs from this newly created db.286*/287log_ctx->iproprole = IPROP_NULL;288}289290if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) ||291(retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) {292com_err(progname, retval, _("while adding entries to the database"));293exit_status++; return;294}295296297298/*299* Always stash the master key so kadm5_create does not prompt for300* it; delete the file below if it was not requested. DO NOT EXIT301* BEFORE DELETING THE KEYFILE if do_stash is not set.302*/303304/*305* Determine the kvno to use, it must be that used to create the master key306* princ.307*/308if (global_params.mask & KADM5_CONFIG_KVNO)309mkey_kvno = global_params.kvno; /* user specified */310else311mkey_kvno = 1; /* Default */312313retval = krb5_db_store_master_key(util_context,314global_params.stash_file,315master_princ,316mkey_kvno,317&master_keyblock,318mkey_password);319if (retval) {320com_err(progname, retval, _("while storing key"));321printf(_("Warning: couldn't stash master key.\n"));322}323/* clean up */324zapfree(pw_str, pw_size);325free(master_salt.data);326327if (kadm5_create(&global_params)) {328if (!do_stash) unlink(global_params.stash_file);329exit_status++;330return;331}332if (!do_stash) unlink(global_params.stash_file);333334return;335}336337static krb5_error_code338tgt_keysalt_iterate(krb5_key_salt_tuple *ksent, krb5_pointer ptr)339{340krb5_context context;341krb5_error_code kret;342struct iterate_args *iargs;343krb5_keyblock key;344krb5_int32 ind;345krb5_data pwd;346347iargs = (struct iterate_args *) ptr;348kret = 0;349350context = iargs->ctx;351352/*353* Convert the master key password into a key for this particular354* encryption system.355*/356pwd.data = mkey_password;357pwd.length = strlen(mkey_password);358kret = krb5_c_random_seed(context, &pwd);359if (kret)360return kret;361362if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {363ind = iargs->dbentp->n_key_data-1;364if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype,365&key))) {366kret = krb5_dbe_encrypt_key_data(context, iargs->rblock->key,367&key, NULL, 1,368&iargs->dbentp->key_data[ind]);369krb5_free_keyblock_contents(context, &key);370}371}372373return(kret);374}375376static krb5_error_code377add_principal(krb5_context context, krb5_principal princ, enum ap_op op,378struct realm_info *pblock)379{380krb5_error_code retval;381krb5_db_entry *entry = NULL;382krb5_kvno mkey_kvno;383krb5_timestamp now;384struct iterate_args iargs;385krb5_actkvno_node actkvno;386387entry = calloc(1, sizeof(*entry));388if (entry == NULL)389return ENOMEM;390391entry->len = KRB5_KDB_V1_BASE_LENGTH;392entry->attributes = pblock->flags;393entry->max_life = pblock->max_life;394entry->max_renewable_life = pblock->max_rlife;395entry->expiration = pblock->expiration;396397if ((retval = krb5_copy_principal(context, princ, &entry->princ)))398goto cleanup;399400if ((retval = krb5_timeofday(context, &now)))401goto cleanup;402403if ((retval = krb5_dbe_update_mod_princ_data(context, entry,404now, &db_create_princ)))405goto cleanup;406407switch (op) {408case MASTER_KEY:409if ((entry->key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data)))410== NULL)411goto cleanup;412memset(entry->key_data, 0, sizeof(krb5_key_data));413entry->n_key_data = 1;414415if (global_params.mask & KADM5_CONFIG_KVNO)416mkey_kvno = global_params.kvno; /* user specified */417else418mkey_kvno = 1; /* Default */419entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;420if ((retval = krb5_dbe_encrypt_key_data(context, pblock->key,421&master_keyblock, NULL,422mkey_kvno, entry->key_data)))423goto cleanup;424/*425* There should always be at least one "active" mkey so creating the426* KRB5_TL_ACTKVNO entry now so the initial mkey is active.427*/428actkvno.next = NULL;429actkvno.act_kvno = mkey_kvno;430/* earliest possible time in case system clock is set back */431actkvno.act_time = 0;432if ((retval = krb5_dbe_update_actkvno(context, entry, &actkvno)))433goto cleanup;434435/* so getprinc shows the right kvno */436if ((retval = krb5_dbe_update_mkvno(context, entry, mkey_kvno)))437goto cleanup;438439break;440case TGT_KEY:441iargs.ctx = context;442iargs.rblock = pblock;443iargs.dbentp = entry;444/*445* Iterate through the key/salt list, ignoring salt types.446*/447if ((retval = krb5_keysalt_iterate(pblock->kslist,448pblock->nkslist,4491,450tgt_keysalt_iterate,451(krb5_pointer) &iargs)))452goto cleanup;453break;454case NULL_KEY:455retval = EOPNOTSUPP;456goto cleanup;457default:458break;459}460461entry->mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |462KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA |463KADM5_PRINC_EXPIRE_TIME);464entry->attributes |= KRB5_KDB_LOCKDOWN_KEYS;465466retval = krb5_db_put_principal(context, entry);467468cleanup:469krb5_db_free_principal(context, entry);470return retval;471}472473474