1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* kadmin/dbutil/kdb5_util.c - Administer a KDC database */
3 /*
4 * (C) Copyright 1990,1991, 1996, 2008, 2009 by the Massachusetts Institute of Technology.
5 * 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 2009 Sun Microsystems, Inc. All rights reserved.
53 * Use is subject to license terms.
54 */
55
56 #include <k5-int.h>
57 #include <kadm5/admin.h>
58 #include <locale.h>
59 #include <adm_proto.h>
60 #include <time.h>
61 #include "kdb5_util.h"
62
63 /*
64 * XXX Ick, ick, ick. These global variables shouldn't be global....
65 */
66 char *mkey_password = 0;
67
68 /*
69 * I can't figure out any way for this not to be global, given how ss
70 * works.
71 */
72
73 int exit_status = 0;
74 krb5_context util_context;
75 kadm5_config_params global_params;
76
usage(void)77 void usage(void)
78 {
79 fprintf(stderr,
80 _("Usage: kdb5_util [-r realm] [-d dbname] "
81 "[-k mkeytype] [-kv mkeyVNO]\n"
82 "\t [-M mkeyname] [-m] [-sf stashfilename] "
83 "[-P password]\n"
84 "\t [-x db_args]* cmd [cmd_options]\n"
85 "\tcreate [-s]\n"
86 "\tdestroy [-f]\n"
87 "\tstash [-f keyfile]\n"
88 "\tdump [-b7|-r13|-r18] [-verbose]\n"
89 "\t [-mkey_convert] [-new_mkey_file mkey_file]\n"
90 "\t [-rev] [-recurse] [filename [princs...]]\n"
91 "\tload [-b7|-r13|-r18] [-hash] [-verbose] [-update] "
92 "filename\n"
93 "\tark [-e etype_list] principal\n"
94 "\tadd_mkey [-e etype] [-s]\n"
95 "\tuse_mkey kvno [time]\n"
96 "\tlist_mkeys\n"));
97 /* avoid a string length compiler warning */
98 fprintf(stderr,
99 _("\tupdate_princ_encryption [-f] [-n] [-v] [princ-pattern]\n"
100 "\tpurge_mkeys [-f] [-n] [-v]\n"
101 "\ttabdump [-H] [-c] [-e] [-n] [-o outfile] dumptype\n"
102 "\nwhere,\n\t[-x db_args]* - any number of database specific "
103 "arguments.\n"
104 "\t\t\tLook at each database documentation for supported "
105 "arguments\n"));
106 exit(1);
107 }
108
109 krb5_keyblock master_keyblock;
110 krb5_kvno master_kvno; /* fetched */
111 extern krb5_principal master_princ;
112 char *mkey_fullname;
113 krb5_db_entry *master_entry = NULL;
114 int valid_master_key = 0;
115
116 char *progname;
117 krb5_boolean manual_mkey = FALSE;
118 krb5_boolean dbactive = FALSE;
119
120 static int open_db_and_mkey(void);
121
122 static void add_random_key(int, char **);
123
124 typedef void (*cmd_func)(int, char **);
125
126 struct _cmd_table {
127 char *name;
128 cmd_func func;
129 int opendb;
130 } cmd_table[] = {
131 {"create", kdb5_create, 0},
132 {"destroy", kdb5_destroy, 1}, /* 1 opens the kdb */
133 {"stash", kdb5_stash, 1},
134 {"dump", dump_db, 1},
135 {"load", load_db, 0},
136 {"ark", add_random_key, 1},
137 {"add_mkey", kdb5_add_mkey, 1},
138 {"use_mkey", kdb5_use_mkey, 1},
139 {"list_mkeys", kdb5_list_mkeys, 1},
140 {"update_princ_encryption", kdb5_update_princ_encryption, 1},
141 {"purge_mkeys", kdb5_purge_mkeys, 1},
142 {"tabdump", tabdump, 1},
143 {NULL, NULL, 0},
144 };
145
146 static struct _cmd_table *
cmd_lookup(char * name)147 cmd_lookup(char *name)
148 {
149 struct _cmd_table *cmd = cmd_table;
150 while (cmd->name) {
151 if (strcmp(cmd->name, name) == 0)
152 return cmd;
153 else
154 cmd++;
155 }
156
157 return NULL;
158 }
159
160 #define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL))
161
162 char **db5util_db_args = NULL;
163 size_t db5util_db_args_size = 0;
164
165 static void
extended_com_err_fn(const char * myprog,errcode_t code,const char * fmt,va_list args)166 extended_com_err_fn(const char *myprog, errcode_t code, const char *fmt,
167 va_list args)
168 {
169 const char *emsg;
170 if (code) {
171 emsg = krb5_get_error_message (util_context, code);
172 fprintf (stderr, "%s: %s ", myprog, emsg);
173 krb5_free_error_message (util_context, emsg);
174 } else {
175 fprintf (stderr, "%s: ", myprog);
176 }
177 vfprintf (stderr, fmt, args);
178 fprintf (stderr, "\n");
179 }
180
181 int
add_db_arg(char * arg)182 add_db_arg(char *arg)
183 {
184 char **temp;
185 db5util_db_args_size++;
186 temp = realloc(db5util_db_args,
187 sizeof(char *) * (db5util_db_args_size + 1));
188 if (temp == NULL)
189 return 0;
190 db5util_db_args = temp;
191 db5util_db_args[db5util_db_args_size-1] = arg;
192 db5util_db_args[db5util_db_args_size] = NULL;
193 return 1;
194 }
195
196 int
main(int argc,char * argv[])197 main(int argc, char *argv[])
198 {
199 struct _cmd_table *cmd = NULL;
200 char *koptarg, **cmd_argv;
201 char *db_name_tmp = NULL;
202 int cmd_argc;
203 krb5_error_code retval;
204
205 setlocale(LC_ALL, "");
206 set_com_err_hook(extended_com_err_fn);
207
208 /*
209 * Ensure that "progname" is set before calling com_err.
210 */
211 progname = (strrchr(argv[0], '/') ?
212 strrchr(argv[0], '/') + 1 : argv[0]);
213
214 retval = kadm5_init_krb5_context(&util_context);
215 if (retval) {
216 com_err (progname, retval, _("while initializing Kerberos code"));
217 exit(1);
218 }
219
220 cmd_argv = (char **) malloc(sizeof(char *)*argc);
221 if (cmd_argv == NULL) {
222 com_err(progname, ENOMEM, _("while creating sub-command arguments"));
223 exit(1);
224 }
225 memset(cmd_argv, 0, sizeof(char *)*argc);
226 cmd_argc = 0;
227
228 argv++; argc--;
229 while (*argv) {
230 if (strcmp(*argv, "-P") == 0 && ARG_VAL) {
231 mkey_password = koptarg;
232 manual_mkey = TRUE;
233 } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) {
234 global_params.dbname = koptarg;
235 global_params.mask |= KADM5_CONFIG_DBNAME;
236
237 if (asprintf(&db_name_tmp, "dbname=%s", global_params.dbname) < 0)
238 {
239 com_err(progname, ENOMEM,
240 _("while parsing command arguments"));
241 exit(1);
242 }
243
244 if (!add_db_arg(db_name_tmp)) {
245 com_err(progname, ENOMEM,
246 _("while parsing command arguments\n"));
247 exit(1);
248 }
249
250 } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) {
251 if (!add_db_arg(koptarg)) {
252 com_err(progname, ENOMEM,
253 _("while parsing command arguments\n"));
254 exit(1);
255 }
256
257 } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) {
258 global_params.realm = koptarg;
259 global_params.mask |= KADM5_CONFIG_REALM;
260 /* not sure this is really necessary */
261 if ((retval = krb5_set_default_realm(util_context,
262 global_params.realm))) {
263 com_err(progname, retval,
264 _("while setting default realm name"));
265 exit(1);
266 }
267 } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) {
268 if (krb5_string_to_enctype(koptarg, &global_params.enctype)) {
269 com_err(progname, EINVAL, _(": %s is an invalid enctype"),
270 koptarg);
271 exit(1);
272 } else
273 global_params.mask |= KADM5_CONFIG_ENCTYPE;
274 } else if (strcmp(*argv, "-kv") == 0 && ARG_VAL) {
275 global_params.kvno = (krb5_kvno) atoi(koptarg);
276 if (global_params.kvno == IGNORE_VNO) {
277 com_err(progname, EINVAL, _(": %s is an invalid mkeyVNO"),
278 koptarg);
279 exit(1);
280 } else
281 global_params.mask |= KADM5_CONFIG_KVNO;
282 } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) {
283 global_params.mkey_name = koptarg;
284 global_params.mask |= KADM5_CONFIG_MKEY_NAME;
285 } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) {
286 global_params.stash_file = koptarg;
287 global_params.mask |= KADM5_CONFIG_STASH_FILE;
288 } else if (strcmp(*argv, "-m") == 0) {
289 manual_mkey = TRUE;
290 global_params.mkey_from_kbd = 1;
291 global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
292 } else {
293 cmd_argv[cmd_argc++] = *argv;
294 }
295 argv++; argc--;
296 }
297
298 if (cmd_argv[0] == NULL)
299 usage();
300 cmd = cmd_lookup(cmd_argv[0]);
301 if (cmd == NULL)
302 usage();
303
304 if( !util_context->default_realm )
305 {
306 char *temp = NULL;
307 retval = krb5_get_default_realm(util_context, &temp);
308 if( retval )
309 {
310 com_err(progname, retval, _("while getting default realm"));
311 exit(1);
312 }
313 krb5_free_default_realm(util_context, temp);
314 }
315
316 retval = kadm5_get_config_params(util_context, 1,
317 &global_params, &global_params);
318 if (retval) {
319 com_err(progname, retval,
320 _("while retrieving configuration parameters"));
321 exit(1);
322 }
323
324 /*
325 * Dump creates files which should not be world-readable. It is
326 * easiest to do a single umask call here.
327 */
328 (void) umask(077);
329
330 master_keyblock.enctype = global_params.enctype;
331 if ((master_keyblock.enctype != ENCTYPE_UNKNOWN) &&
332 (!krb5_c_valid_enctype(master_keyblock.enctype))) {
333 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
334 "while setting up enctype %d", master_keyblock.enctype);
335 }
336
337 if (cmd->opendb && open_db_and_mkey())
338 return exit_status;
339
340 if (global_params.iprop_enabled == TRUE)
341 ulog_set_role(util_context, IPROP_PRIMARY);
342 else
343 ulog_set_role(util_context, IPROP_NULL);
344
345 (*cmd->func)(cmd_argc, cmd_argv);
346
347 if( db_name_tmp )
348 free( db_name_tmp );
349
350 if( db5util_db_args )
351 free(db5util_db_args);
352
353 quit();
354 kadm5_free_config_params(util_context, &global_params);
355 krb5_free_context(util_context);
356 free(cmd_argv);
357 return exit_status;
358 }
359
360 /*
361 * open_db_and_mkey: Opens the KDC and policy database, and sets the
362 * global master_* variables. Sets dbactive to TRUE if the databases
363 * are opened, and valid_master_key to 1 if the global master
364 * variables are set properly. Returns 0 on success, and 1 on
365 * failure, but it is not considered a failure if the master key
366 * cannot be fetched (the master key stash file may not exist when the
367 * program is run).
368 */
369 static int
open_db_and_mkey(void)370 open_db_and_mkey(void)
371 {
372 krb5_error_code retval;
373 krb5_data scratch, pwd, seed;
374
375 dbactive = FALSE;
376 valid_master_key = 0;
377
378 if ((retval = krb5_db_open(util_context, db5util_db_args,
379 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) {
380 com_err(progname, retval, _("while initializing database"));
381 exit_status++;
382 return(1);
383 }
384
385 /* assemble & parse the master key name */
386
387 if ((retval = krb5_db_setup_mkey_name(util_context,
388 global_params.mkey_name,
389 global_params.realm,
390 &mkey_fullname, &master_princ))) {
391 com_err(progname, retval, _("while setting up master key name"));
392 exit_status++;
393 return(1);
394 }
395 if ((retval = krb5_db_get_principal(util_context, master_princ, 0,
396 &master_entry))) {
397 com_err(progname, retval, _("while retrieving master entry"));
398 exit_status++;
399 (void) krb5_db_fini(util_context);
400 return(1);
401 }
402
403 if (global_params.mask & KADM5_CONFIG_KVNO)
404 master_kvno = global_params.kvno; /* user specified */
405 else
406 master_kvno = IGNORE_VNO;
407
408 /* the databases are now open, and the master principal exists */
409 dbactive = TRUE;
410
411 if (mkey_password) {
412 pwd.data = mkey_password;
413 pwd.length = strlen(mkey_password);
414 retval = krb5_principal2salt(util_context, master_princ, &scratch);
415 if (retval) {
416 com_err(progname, retval, _("while calculated master key salt"));
417 exit_status++;
418 return(1);
419 }
420
421 /* If no encryption type is set, use the default */
422 if (master_keyblock.enctype == ENCTYPE_UNKNOWN)
423 master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
424 if (!krb5_c_valid_enctype(master_keyblock.enctype))
425 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
426 "while setting up enctype %d",
427 master_keyblock.enctype);
428
429 retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,
430 &pwd, &scratch, &master_keyblock);
431 if (retval) {
432 com_err(progname, retval,
433 _("while transforming master key from password"));
434 exit_status++;
435 return(1);
436 }
437 free(scratch.data);
438 mkey_password = 0;
439
440 } else {
441 if ((retval = krb5_db_fetch_mkey(util_context, master_princ,
442 master_keyblock.enctype,
443 manual_mkey, FALSE,
444 global_params.stash_file,
445 &master_kvno,
446 0, &master_keyblock))) {
447 com_err(progname, retval, _("while reading master key"));
448 com_err(progname, 0, _("Warning: proceeding without master key"));
449 exit_status++;
450 return(0);
451 }
452 }
453
454 if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ,
455 &master_keyblock))) {
456 com_err(progname, retval, "while getting master key list");
457 com_err(progname, 0, "Warning: proceeding without master key list");
458 exit_status++;
459 return(0);
460 }
461
462 seed.length = master_keyblock.length;
463 seed.data = (char *) master_keyblock.contents;
464
465 if ((retval = krb5_c_random_seed(util_context, &seed))) {
466 com_err(progname, retval, _("while seeding random number generator"));
467 exit_status++;
468 memset(master_keyblock.contents, 0, master_keyblock.length);
469 krb5_free_keyblock_contents(util_context, &master_keyblock);
470 return(1);
471 }
472
473 if (global_params.iprop_enabled) {
474 if (ulog_map(util_context, global_params.iprop_logfile,
475 global_params.iprop_ulogsize)) {
476 fprintf(stderr, _("%s: Could not map log\n"), progname);
477 exit_status++;
478 return(1);
479 }
480 }
481
482 valid_master_key = 1;
483 dbactive = TRUE;
484 return 0;
485 }
486
487 #ifdef HAVE_GETCWD
488 #undef getwd
489 #endif
490
491 int
quit(void)492 quit(void)
493 {
494 krb5_error_code retval;
495 static krb5_boolean finished = 0;
496
497 if (finished)
498 return 0;
499 ulog_fini(util_context);
500 retval = krb5_db_fini(util_context);
501 zapfree(master_keyblock.contents, master_keyblock.length);
502 krb5_free_principal(util_context, master_princ);
503 finished = TRUE;
504 if (retval && retval != KRB5_KDB_DBNOTINITED) {
505 com_err(progname, retval, _("while closing database"));
506 exit_status++;
507 return 1;
508 }
509 return 0;
510 }
511
512 static void
add_random_key(int argc,char ** argv)513 add_random_key(int argc, char **argv)
514 {
515 krb5_error_code ret;
516 krb5_principal princ;
517 krb5_db_entry *dbent;
518 krb5_timestamp now;
519
520 krb5_key_salt_tuple *keysalts = NULL;
521 krb5_int32 num_keysalts = 0;
522
523 int free_keysalts;
524 char *me = progname;
525 char *ks_str = "";
526 char *pr_str;
527 krb5_keyblock *tmp_mkey;
528
529 if (argc < 2)
530 usage();
531 for (argv++, argc--; *argv; argv++, argc--) {
532 if (!strcmp(*argv, "-e")) {
533 argv++; argc--;
534 ks_str = *argv;
535 continue;
536 } else
537 break;
538 }
539 if (argc < 1)
540 usage();
541 pr_str = *argv;
542 ret = krb5_parse_name(util_context, pr_str, &princ);
543 if (ret) {
544 com_err(me, ret, _("while parsing principal name %s"), pr_str);
545 exit_status++;
546 return;
547 }
548 ret = krb5_db_get_principal(util_context, princ, 0, &dbent);
549 if (ret) {
550 com_err(me, ret, _("while fetching principal %s"), pr_str);
551 exit_status++;
552 return;
553 }
554 ret = krb5_string_to_keysalts(ks_str,
555 NULL, NULL, 0,
556 &keysalts,
557 &num_keysalts);
558 if (ret) {
559 com_err(me, ret, _("while parsing keysalts %s"), ks_str);
560 exit_status++;
561 return;
562 }
563 if (!num_keysalts || keysalts == NULL) {
564 num_keysalts = global_params.num_keysalts;
565 keysalts = global_params.keysalts;
566 free_keysalts = 0;
567 } else
568 free_keysalts = 1;
569
570 /* Find the mkey used to protect the existing keys */
571 ret = krb5_dbe_find_mkey(util_context, dbent, &tmp_mkey);
572 if (ret) {
573 com_err(me, ret, _("while finding mkey"));
574 krb5_db_free_principal(util_context, dbent);
575 exit_status++;
576 return;
577 }
578
579 ret = krb5_dbe_ark(util_context, tmp_mkey, keysalts, num_keysalts, dbent);
580 if (free_keysalts)
581 free(keysalts);
582 if (ret) {
583 com_err(me, ret, "while randomizing principal %s", pr_str);
584 krb5_db_free_principal(util_context, dbent);
585 exit_status++;
586 return;
587 }
588 dbent->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
589 ret = krb5_timeofday(util_context, &now);
590 if (ret) {
591 com_err(me, ret, _("while getting time"));
592 krb5_db_free_principal(util_context, dbent);
593 exit_status++;
594 return;
595 }
596 ret = krb5_dbe_update_last_pwd_change(util_context, dbent, now);
597 if (ret) {
598 com_err(me, ret, _("while setting changetime"));
599 krb5_db_free_principal(util_context, dbent);
600 exit_status++;
601 return;
602 }
603
604 dbent->mask |= KADM5_ATTRIBUTES | KADM5_KEY_DATA | KADM5_TL_DATA;
605
606 ret = krb5_db_put_principal(util_context, dbent);
607 krb5_db_free_principal(util_context, dbent);
608 if (ret) {
609 com_err(me, ret, _("while saving principal %s"), pr_str);
610 exit_status++;
611 return;
612 }
613 printf(_("%s changed\n"), pr_str);
614 }
615