Home | History | Annotate | Download | only in dbutil
      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/edit/kdb5_edit.c
     26  *
     27  * (C) Copyright 1990,1991, 1996 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  * Edit a KDC database.
     51  */
     52 
     53 /*
     54  * Copyright (C) 1998 by the FundsXpress, INC.
     55  *
     56  * All rights reserved.
     57  *
     58  * Export of this software from the United States of America may require
     59  * a specific license from the United States Government.  It is the
     60  * responsibility of any person or organization contemplating export to
     61  * obtain such a license before exporting.
     62  *
     63  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     64  * distribute this software and its documentation for any purpose and
     65  * without fee is hereby granted, provided that the above copyright
     66  * notice appear in all copies and that both that copyright notice and
     67  * this permission notice appear in supporting documentation, and that
     68  * the name of FundsXpress. not be used in advertising or publicity pertaining
     69  * to distribution of the software without specific, written prior
     70  * permission.  FundsXpress makes no representations about the suitability of
     71  * this software for any purpose.  It is provided "as is" without express
     72  * or implied warranty.
     73  *
     74  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     75  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     76  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     77  */
     78 
     79 /*
     80  *  Yes, I know this is a hack, but we need admin.h without including the
     81  *  rpc.h header. Additionally, our rpc.h header brings in
     82  *  a des.h header which causes other problems.
     83  */
     84 #define	_RPC_RPC_H
     85 
     86 #include <stdio.h>
     87 #include <k5-int.h>
     88 #include <kadm5/admin.h>
     89 #include <rpc/types.h>
     90 #include <krb5/adm_proto.h>
     91 #include <rpc/xdr.h>
     92 #include <time.h>
     93 #include <libintl.h>
     94 #include <locale.h>
     95 #include "kdb5_util.h"
     96 
     97 char	*Err_no_master_msg = "Master key not entered!\n";
     98 char	*Err_no_database = "Database not currently opened!\n";
     99 
    100 /*
    101  * XXX Ick, ick, ick.  These global variables shouldn't be global....
    102  */
    103 char *mkey_password = 0;
    104 
    105 /*
    106  * I can't figure out any way for this not to be global, given how ss
    107  * works.
    108  */
    109 
    110 int exit_status = 0;
    111 krb5_context util_context;
    112 kadm5_config_params global_params;
    113 
    114 void usage()
    115 {
    116      fprintf(stderr, "%s: "
    117 	   "kdb5_util [-x db_args]* [-r realm] [-d dbname] [-k mkeytype] [-M mkeyname]\n"
    118 	     "\t        [-sf stashfilename] [-P password] [-m] cmd [cmd_options]\n"
    119 	     "\tcreate	[-s]\n"
    120 	     "\tdestroy	[-f]\n"
    121 	     "\tstash	[-f keyfile]\n"
    122 	     "\tdump	[-old] [-ov] [-b6] [-verbose] [filename	[princs...]]\n"
    123 	     "\t	[-mkey_convert] [-new_mkey_file mkey_file]\n"
    124 	     "\t	[-rev] [-recurse] [filename [princs...]]\n"
    125 	     "\tload	[-old] [-ov] [-b6] [-verbose] [-update] filename\n"
    126 	     "\tark	[-e etype_list] principal\n"
    127 	     "\nwhere,\n\t[-x db_args]* - any number of database specific arguments.\n"
    128 	     "\t\t\tLook at each database documentation for supported arguments\n",
    129 		gettext("Usage"));
    130      exit(1);
    131 }
    132 
    133 krb5_keyblock master_key;
    134 extern krb5_principal master_princ;
    135 krb5_db_entry master_entry;
    136 int	valid_master_key = 0;
    137 
    138 char *progname;
    139 krb5_boolean manual_mkey = FALSE;
    140 krb5_boolean dbactive = FALSE;
    141 
    142 static int open_db_and_mkey(void);
    143 
    144 static void add_random_key(int, char **);
    145 
    146 typedef void (*cmd_func)(int, char **);
    147 
    148 struct _cmd_table {
    149      char *name;
    150      cmd_func func;
    151      int opendb;
    152 } cmd_table[] = {
    153      {"create", kdb5_create, 0},
    154      {"destroy", kdb5_destroy, 1},
    155      {"stash", kdb5_stash, 1},
    156      {"dump", dump_db, 1},
    157      {"load", load_db, 0},
    158      {"ark", add_random_key, 1},
    159      {NULL, NULL, 0},
    160 };
    161 
    162 static struct _cmd_table *cmd_lookup(name)
    163    char *name;
    164 {
    165      struct _cmd_table *cmd = cmd_table;
    166      while (cmd->name) {
    167 	  if (strcmp(cmd->name, name) == 0)
    168 	       return cmd;
    169 	  else
    170 	       cmd++;
    171      }
    172 
    173      return NULL;
    174 }
    175 
    176 #define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL))
    177 
    178 char **db5util_db_args = NULL;
    179 int    db5util_db_args_size = 0;
    180 
    181 static void extended_com_err_fn (const char *myprog, errcode_t code,
    182 				 const char *fmt, va_list args)
    183 {
    184     const char *emsg;
    185     if (code) {
    186 	emsg = krb5_get_error_message (util_context, code);
    187 	fprintf (stderr, "%s: %s ", myprog, emsg);
    188 	krb5_free_error_message (util_context, emsg);
    189     } else {
    190 	fprintf (stderr, "%s: ", myprog);
    191     }
    192     vfprintf (stderr, fmt, args);
    193     fprintf (stderr, "\n");
    194 }
    195 
    196 int add_db_arg(char *arg)
    197 {
    198     char **temp;
    199     db5util_db_args_size++;
    200     temp = realloc(db5util_db_args,
    201 		   sizeof(char *) * (db5util_db_args_size + 1));
    202     if (temp == NULL)
    203 	return 0;
    204     db5util_db_args = temp;
    205     db5util_db_args[db5util_db_args_size-1] = arg;
    206     db5util_db_args[db5util_db_args_size]   = NULL;
    207     return 1;
    208 }
    209 
    210 int main(argc, argv)
    211     int argc;
    212     char *argv[];
    213 {
    214     struct _cmd_table *cmd = NULL;
    215     char *koptarg, **cmd_argv;
    216     char *db_name_tmp = NULL;
    217     int cmd_argc;
    218     krb5_error_code retval;
    219 
    220 	(void) setlocale(LC_ALL, "");
    221     set_com_err_hook(extended_com_err_fn);
    222 
    223 #if !defined(TEXT_DOMAIN)  /* Should be defined by cc -D */
    224 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
    225 #endif
    226 
    227 	(void) textdomain(TEXT_DOMAIN);
    228 
    229 	Err_no_master_msg = gettext("Master key not entered!\n");
    230 	Err_no_database = gettext("Database not currently opened!\n");
    231 
    232 	/*
    233 	 * Solaris Kerberos:
    234 	 * Ensure that "progname" is set before calling com_err.
    235 	 */
    236 	progname = (strrchr(argv[0], '/') ?
    237 		    strrchr(argv[0], '/') + 1 : argv[0]);
    238 
    239     retval = kadm5_init_krb5_context(&util_context);
    240     if (retval) {
    241 	    com_err (progname, retval,
    242 		gettext("while initializing Kerberos code"));
    243 	    exit(1);
    244     }
    245 
    246     cmd_argv = (char **) malloc(sizeof(char *)*argc);
    247     if (cmd_argv == NULL) {
    248 		com_err(progname, ENOMEM,
    249 		    gettext("while creating sub-command arguments"));
    250 	 exit(1);
    251     }
    252     memset(cmd_argv, 0, sizeof(char *)*argc);
    253     cmd_argc = 1;
    254 
    255     argv++; argc--;
    256     while (*argv) {
    257        if (strcmp(*argv, "-P") == 0 && ARG_VAL) {
    258 	    mkey_password = koptarg;
    259 	    manual_mkey = TRUE;
    260        } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) {
    261 	    global_params.dbname = koptarg;
    262 	    global_params.mask |= KADM5_CONFIG_DBNAME;
    263 
    264 	    db_name_tmp = malloc( strlen(global_params.dbname) + sizeof("dbname="));
    265 	    if( db_name_tmp == NULL )
    266 	    {
    267 		com_err(progname, ENOMEM, "while parsing command arguments");
    268 		exit(1);
    269 	    }
    270 
    271 	    strcpy( db_name_tmp, "dbname=");
    272 	    strcat( db_name_tmp, global_params.dbname );
    273 
    274 	    if (!add_db_arg(db_name_tmp)) {
    275 		com_err(progname, ENOMEM, "while parsing command arguments\n");
    276 		exit(1);
    277 	    }
    278 
    279        } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) {
    280 	   if (!add_db_arg(koptarg)) {
    281 		com_err(progname, ENOMEM, "while parsing command arguments\n");
    282 		exit(1);
    283 	   }
    284 
    285        } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) {
    286 	    global_params.realm = koptarg;
    287 	    global_params.mask |= KADM5_CONFIG_REALM;
    288 	    /* not sure this is really necessary */
    289 	    if ((retval = krb5_set_default_realm(util_context,
    290 						 global_params.realm))) {
    291 				com_err(progname, retval,
    292 					gettext("while setting default "
    293 						"realm name"));
    294 		 exit(1);
    295 	    }
    296        } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) {
    297 	    if (krb5_string_to_enctype(koptarg, &global_params.enctype)) {
    298 		/* Solaris Kerberos */
    299 		 com_err(progname, 0, gettext("%s is an invalid enctype"), koptarg);
    300 	    }
    301 	    else
    302 		 global_params.mask |= KADM5_CONFIG_ENCTYPE;
    303        } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) {
    304 	    global_params.mkey_name = koptarg;
    305 	    global_params.mask |= KADM5_CONFIG_MKEY_NAME;
    306        } else if (((strcmp(*argv, "-sf") == 0)
    307 		/* SUNWresync121 - carry the old -f forward too */
    308 		|| (strcmp(*argv, "-f") == 0)) && ARG_VAL) {
    309 	    global_params.stash_file = koptarg;
    310 	    global_params.mask |= KADM5_CONFIG_STASH_FILE;
    311        } else if (strcmp(*argv, "-m") == 0) {
    312 	    manual_mkey = TRUE;
    313 	    global_params.mkey_from_kbd = 1;
    314 	    global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
    315        } else if (cmd_lookup(*argv) != NULL) {
    316 	    if (cmd_argv[0] == NULL)
    317 		 cmd_argv[0] = *argv;
    318 	    else
    319 		 usage();
    320        } else {
    321 	    cmd_argv[cmd_argc++] = *argv;
    322        }
    323        argv++; argc--;
    324     }
    325 
    326     if (cmd_argv[0] == NULL)
    327 	 usage();
    328 
    329     if( !util_context->default_realm )
    330     {
    331 	char *temp = NULL;
    332 	retval = krb5_get_default_realm(util_context, &temp);
    333 	if( retval )
    334 	{
    335 	    com_err (progname, retval, "while getting default realm");
    336 	    exit(1);
    337 	}
    338 	util_context->default_realm = temp;
    339     }
    340 
    341     retval = kadm5_get_config_params(util_context, 1,
    342 				     &global_params, &global_params);
    343     if (retval) {
    344 		/* Solaris Kerberos */
    345 		com_err(progname, retval,
    346 		    gettext("while retreiving configuration parameters"));
    347 	 exit(1);
    348     }
    349 
    350     /*
    351      * Dump creates files which should not be world-readable.  It is
    352      * easiest to do a single umask call here.
    353      */
    354     (void) umask(077);
    355 
    356     (void) memset(&master_key, 0, sizeof (krb5_keyblock));
    357 
    358     if ((global_params.enctype != ENCTYPE_UNKNOWN) &&
    359 	(!krb5_c_valid_enctype(global_params.enctype))) {
    360 	/* Solaris Kerberos */
    361 	com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
    362 	    gettext("while setting up enctype %d"), global_params.enctype);
    363 	exit(1);
    364     }
    365 
    366     cmd = cmd_lookup(cmd_argv[0]);
    367     if (cmd->opendb && open_db_and_mkey())
    368 	 return exit_status;
    369 
    370 	if (global_params.iprop_enabled == TRUE)
    371 		ulog_set_role(util_context, IPROP_MASTER);
    372 	else
    373 		ulog_set_role(util_context, IPROP_NULL);
    374 
    375     (*cmd->func)(cmd_argc, cmd_argv);
    376 
    377     if( db_name_tmp )
    378 	free( db_name_tmp );
    379 
    380     if( db5util_db_args )
    381 	free(db5util_db_args);
    382 
    383     kadm5_free_config_params(util_context, &global_params);
    384     krb5_free_context(util_context);
    385     return exit_status;
    386 }
    387 
    388 #if 0
    389 /*
    390  * This function is no longer used in kdb5_util (and it would no
    391  * longer work, anyway).
    392  */
    393 void set_dbname(argc, argv)
    394     int argc;
    395     char *argv[];
    396 {
    397     krb5_error_code retval;
    398 
    399     if (argc < 3) {
    400 		/* Solaris Kerberos */
    401 		com_err(progname, 0, gettext("Too few arguments"));
    402 		com_err(progname, 0, gettext("Usage: %s dbpathname realmname"),
    403 			progname);
    404 	exit_status++;
    405 	return;
    406     }
    407     if (dbactive) {
    408 	if ((retval = krb5_db_fini(util_context)) && retval!= KRB5_KDB_DBNOTINITED) {
    409 	    /* Solaris Kerberos */
    410 	    com_err(progname, retval, gettext("while closing previous database"));
    411 	    exit_status++;
    412 	    return;
    413 	}
    414 	if (valid_master_key) {
    415 	    krb5_free_keyblock_contents(util_context, &master_key);
    416 	    master_key.contents = NULL;
    417 	    valid_master_key = 0;
    418 	}
    419 	krb5_free_principal(util_context, master_princ);
    420 	dbactive = FALSE;
    421     }
    422 
    423     /* Solaris Kerberos */
    424     (void) set_dbname_help(progname, argv[1]);
    425     return;
    426 }
    427 #endif
    428 
    429 /*
    430  * open_db_and_mkey: Opens the KDC and policy database, and sets the
    431  * global master_* variables.  Sets dbactive to TRUE if the databases
    432  * are opened, and valid_master_key to 1 if the global master
    433  * variables are set properly.  Returns 0 on success, and 1 on
    434  * failure, but it is not considered a failure if the master key
    435  * cannot be fetched (the master key stash file may not exist when the
    436  * program is run).
    437  */
    438 static int open_db_and_mkey()
    439 {
    440     krb5_error_code retval;
    441     int nentries;
    442     krb5_boolean more;
    443     krb5_data scratch, pwd, seed;
    444 
    445     dbactive = FALSE;
    446     valid_master_key = 0;
    447 
    448     if ((retval = krb5_db_open(util_context, db5util_db_args,
    449 			       KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) {
    450 	com_err(progname, retval, "while initializing database");
    451 	exit_status++;
    452 	return(1);
    453     }
    454 
    455    /* assemble & parse the master key name */
    456 
    457     if ((retval = krb5_db_setup_mkey_name(util_context,
    458 					  global_params.mkey_name,
    459 					  global_params.realm,
    460 					  0, &master_princ))) {
    461 		com_err(progname, retval,
    462 		    gettext("while setting up master key name"));
    463 	exit_status++;
    464 	return(1);
    465     }
    466     nentries = 1;
    467     if ((retval = krb5_db_get_principal(util_context, master_princ,
    468 					&master_entry, &nentries, &more))) {
    469 		com_err(progname, retval,
    470 		    gettext("while retrieving master entry"));
    471 	exit_status++;
    472 	(void) krb5_db_fini(util_context);
    473 	return(1);
    474     } else if (more) {
    475 	com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
    476 		    gettext("while retrieving master entry"));
    477 	exit_status++;
    478 	(void) krb5_db_fini(util_context);
    479 	return(1);
    480     } else if (!nentries) {
    481 		com_err(progname, KRB5_KDB_NOENTRY,
    482 		    gettext("while retrieving master entry"));
    483 	exit_status++;
    484 	(void) krb5_db_fini(util_context);
    485 	return(1);
    486     }
    487 
    488     krb5_db_free_principal(util_context, &master_entry, nentries);
    489 
    490     /* the databases are now open, and the master principal exists */
    491     dbactive = TRUE;
    492 
    493     if (mkey_password) {
    494 	pwd.data = mkey_password;
    495 	pwd.length = strlen(mkey_password);
    496 	retval = krb5_principal2salt(util_context, master_princ, &scratch);
    497 	if (retval) {
    498 		com_err(progname, retval,
    499 		    gettext("while calculated master key salt"));
    500 	    /* Solaris Kerberos */
    501 	    exit_status++;
    502 	    return(1);
    503 	}
    504 
    505 	/* If no encryption type is set, use the default */
    506 	if (global_params.enctype == ENCTYPE_UNKNOWN) {
    507 	    global_params.enctype = DEFAULT_KDC_ENCTYPE;
    508 	    if (!krb5_c_valid_enctype(global_params.enctype))
    509 		com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
    510 			gettext("while setting up enctype %d"),
    511 			global_params.enctype);
    512 	}
    513 
    514 	retval = krb5_c_string_to_key(util_context, global_params.enctype,
    515 				      &pwd, &scratch, &master_key);
    516 	if (retval) {
    517 	    com_err(progname, retval,
    518 		gettext("while transforming master key from password"));
    519 	    /* Solaris Kerberos */
    520 	    exit_status++;
    521 	    return(1);
    522 	}
    523 	free(scratch.data);
    524 	mkey_password = 0;
    525     } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ,
    526 					    global_params.enctype,
    527 					    manual_mkey, FALSE,
    528 					    global_params.stash_file,
    529 					    0, &master_key))) {
    530 	com_err(progname, retval,
    531 	    gettext("while reading master key"));
    532 	com_err(progname, 0,
    533 	    gettext("Warning: proceeding without master key"));
    534 	/*
    535 	 * Solaris Kerberos: We don't want to count as an error if for instance
    536 	 * the stash file is not present and we are trying to automate
    537 	 * propagation, which really doesn't need a master key to do so.
    538 	 */
    539 	if (retval != KRB5_KDB_CANTREAD_STORED)
    540 		exit_status++;
    541 	return(0);
    542     }
    543     if ((retval = krb5_db_verify_master_key(util_context, master_princ,
    544 		&master_key))) {
    545 	com_err(progname, retval,
    546 		gettext("while verifying master key"));
    547 	exit_status++;
    548 	krb5_free_keyblock_contents(util_context, &master_key);
    549 	return(1);
    550     }
    551 
    552     seed.length = master_key.length;
    553     seed.data = (char *)master_key.contents;
    554 
    555     if ((retval = krb5_c_random_seed(util_context, &seed))) {
    556 	com_err(progname, retval,
    557 		gettext("while initializing random key generator"));
    558 	exit_status++;
    559 	krb5_free_keyblock_contents(util_context, &master_key);
    560 	return(1);
    561     }
    562 
    563     valid_master_key = 1;
    564     dbactive = TRUE;
    565     return 0;
    566 }
    567 
    568 #ifdef HAVE_GETCWD
    569 #undef getwd
    570 #endif
    571 
    572 int
    573 quit()
    574 {
    575     krb5_error_code retval;
    576     static krb5_boolean finished = 0;
    577 
    578     if (finished)
    579 	return 0;
    580     retval = krb5_db_fini(util_context);
    581     krb5_free_keyblock_contents(util_context, &master_key);
    582     finished = TRUE;
    583     krb5_free_context(util_context);
    584     if (retval && retval != KRB5_KDB_DBNOTINITED) {
    585 		com_err(progname, retval, gettext("while closing database"));
    586 	exit_status++;
    587 	return 1;
    588     }
    589     return 0;
    590 }
    591 
    592 static void
    593 add_random_key(argc, argv)
    594     int argc;
    595     char **argv;
    596 {
    597     krb5_error_code ret;
    598     krb5_principal princ;
    599     krb5_db_entry dbent;
    600     int n;
    601     krb5_boolean more;
    602     krb5_timestamp now;
    603 
    604     krb5_key_salt_tuple *keysalts = NULL;
    605     krb5_int32 num_keysalts = 0;
    606 
    607     int free_keysalts;
    608     /* Solaris Kerberos */
    609     char *me = progname;
    610     char *ks_str = NULL;
    611     char *pr_str;
    612 
    613     if (argc < 2)
    614 	usage();
    615     for (argv++, argc--; *argv; argv++, argc--) {
    616 	if (!strcmp(*argv, "-e")) {
    617 	    argv++; argc--;
    618 	    ks_str = *argv;
    619 	    continue;
    620 	} else
    621 	    break;
    622     }
    623     if (argc < 1)
    624 	usage();
    625     pr_str = *argv;
    626     ret = krb5_parse_name(util_context, pr_str, &princ);
    627     if (ret) {
    628 	com_err(me, ret, gettext("while parsing principal name %s"), pr_str);
    629 	exit_status++;
    630 	return;
    631     }
    632     n = 1;
    633     ret = krb5_db_get_principal(util_context, princ, &dbent,
    634 				&n, &more);
    635     if (ret) {
    636 	com_err(me, ret, gettext("while fetching principal %s"), pr_str);
    637 	exit_status++;
    638 	return;
    639     }
    640     if (n != 1) {
    641 	fprintf(stderr, gettext("principal %s not found\n"), pr_str);
    642 	exit_status++;
    643 	return;
    644     }
    645     if (more) {
    646 	fprintf(stderr, gettext("principal %s not unique\n"), pr_str);
    647 	krb5_db_free_principal(util_context, &dbent, 1);
    648 	exit_status++;
    649 	return;
    650     }
    651     ret = krb5_string_to_keysalts(ks_str,
    652 				  ", \t", ":.-", 0,
    653 				  &keysalts,
    654 				  &num_keysalts);
    655     if (ret) {
    656 	com_err(me, ret, gettext("while parsing keysalts %s"), ks_str);
    657 	exit_status++;
    658 	return;
    659     }
    660     if (!num_keysalts || keysalts == NULL) {
    661 	num_keysalts = global_params.num_keysalts;
    662 	keysalts = global_params.keysalts;
    663 	free_keysalts = 0;
    664     } else
    665 	free_keysalts = 1;
    666     ret = krb5_dbe_ark(util_context, &master_key,
    667 		       keysalts, num_keysalts,
    668 		       &dbent);
    669     if (free_keysalts)
    670 	free(keysalts);
    671     if (ret) {
    672 	com_err(me, ret, gettext("while randomizing principal %s"), pr_str);
    673 	krb5_db_free_principal(util_context, &dbent, 1);
    674 	exit_status++;
    675 	return;
    676     }
    677     dbent.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
    678     ret = krb5_timeofday(util_context, &now);
    679     if (ret) {
    680 	com_err(me, ret, gettext("while getting time"));
    681 	krb5_db_free_principal(util_context, &dbent, 1);
    682 	exit_status++;
    683 	return;
    684     }
    685     ret = krb5_dbe_update_last_pwd_change(util_context, &dbent, now);
    686     if (ret) {
    687 	com_err(me, ret, gettext("while setting changetime"));
    688 	krb5_db_free_principal(util_context, &dbent, 1);
    689 	exit_status++;
    690 	return;
    691     }
    692     ret = krb5_db_put_principal(util_context, &dbent, &n);
    693     krb5_db_free_principal(util_context, &dbent, 1);
    694     if (ret) {
    695 	com_err(me, ret, gettext("while saving principal %s"), pr_str);
    696 	exit_status++;
    697 	return;
    698     }
    699     printf("%s changed\n", pr_str);
    700 }
    701