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()77 void usage()
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 [-old|-b6|-b7|-r13|-r18] [-verbose]\n"
89 "\t [-mkey_convert] [-new_mkey_file mkey_file]\n"
90 "\t [-rev] [-recurse] [filename [princs...]]\n"
91 "\tload [-old|-b6|-b7|-r13|-r18] [-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
cmd_lookup(name)146 static struct _cmd_table *cmd_lookup(name)
147 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 int db5util_db_args_size = 0;
164
extended_com_err_fn(const char * myprog,errcode_t code,const char * fmt,va_list args)165 static void extended_com_err_fn (const char *myprog, errcode_t code,
166 const char *fmt, va_list args)
167 {
168 const char *emsg;
169 if (code) {
170 emsg = krb5_get_error_message (util_context, code);
171 fprintf (stderr, "%s: %s ", myprog, emsg);
172 krb5_free_error_message (util_context, emsg);
173 } else {
174 fprintf (stderr, "%s: ", myprog);
175 }
176 vfprintf (stderr, fmt, args);
177 fprintf (stderr, "\n");
178 }
179
add_db_arg(char * arg)180 int add_db_arg(char *arg)
181 {
182 char **temp;
183 db5util_db_args_size++;
184 temp = realloc(db5util_db_args,
185 sizeof(char *) * (db5util_db_args_size + 1));
186 if (temp == NULL)
187 return 0;
188 db5util_db_args = temp;
189 db5util_db_args[db5util_db_args_size-1] = arg;
190 db5util_db_args[db5util_db_args_size] = NULL;
191 return 1;
192 }
193
main(argc,argv)194 int main(argc, argv)
195 int argc;
196 char *argv[];
197 {
198 struct _cmd_table *cmd = NULL;
199 char *koptarg, **cmd_argv;
200 char *db_name_tmp = NULL;
201 int cmd_argc;
202 krb5_error_code retval;
203
204 setlocale(LC_ALL, "");
205 set_com_err_hook(extended_com_err_fn);
206
207 /*
208 * Ensure that "progname" is set before calling com_err.
209 */
210 progname = (strrchr(argv[0], '/') ?
211 strrchr(argv[0], '/') + 1 : argv[0]);
212
213 retval = kadm5_init_krb5_context(&util_context);
214 if (retval) {
215 com_err (progname, retval, _("while initializing Kerberos code"));
216 exit(1);
217 }
218
219 cmd_argv = (char **) malloc(sizeof(char *)*argc);
220 if (cmd_argv == NULL) {
221 com_err(progname, ENOMEM, _("while creating sub-command arguments"));
222 exit(1);
223 }
224 memset(cmd_argv, 0, sizeof(char *)*argc);
225 cmd_argc = 0;
226
227 argv++; argc--;
228 while (*argv) {
229 if (strcmp(*argv, "-P") == 0 && ARG_VAL) {
230 mkey_password = koptarg;
231 manual_mkey = TRUE;
232 } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) {
233 global_params.dbname = koptarg;
234 global_params.mask |= KADM5_CONFIG_DBNAME;
235
236 if (asprintf(&db_name_tmp, "dbname=%s", global_params.dbname) < 0)
237 {
238 com_err(progname, ENOMEM,
239 _("while parsing command arguments"));
240 exit(1);
241 }
242
243 if (!add_db_arg(db_name_tmp)) {
244 com_err(progname, ENOMEM,
245 _("while parsing command arguments\n"));
246 exit(1);
247 }
248
249 } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) {
250 if (!add_db_arg(koptarg)) {
251 com_err(progname, ENOMEM,
252 _("while parsing command arguments\n"));
253 exit(1);
254 }
255
256 } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) {
257 global_params.realm = koptarg;
258 global_params.mask |= KADM5_CONFIG_REALM;
259 /* not sure this is really necessary */
260 if ((retval = krb5_set_default_realm(util_context,
261 global_params.realm))) {
262 com_err(progname, retval,
263 _("while setting default realm name"));
264 exit(1);
265 }
266 } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) {
267 if (krb5_string_to_enctype(koptarg, &global_params.enctype)) {
268 com_err(progname, EINVAL, _(": %s is an invalid enctype"),
269 koptarg);
270 exit(1);
271 } else
272 global_params.mask |= KADM5_CONFIG_ENCTYPE;
273 } else if (strcmp(*argv, "-kv") == 0 && ARG_VAL) {
274 global_params.kvno = (krb5_kvno) atoi(koptarg);
275 if (global_params.kvno == IGNORE_VNO) {
276 com_err(progname, EINVAL, _(": %s is an invalid mkeyVNO"),
277 koptarg);
278 exit(1);
279 } else
280 global_params.mask |= KADM5_CONFIG_KVNO;
281 } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) {
282 global_params.mkey_name = koptarg;
283 global_params.mask |= KADM5_CONFIG_MKEY_NAME;
284 } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) {
285 global_params.stash_file = koptarg;
286 global_params.mask |= KADM5_CONFIG_STASH_FILE;
287 } else if (strcmp(*argv, "-m") == 0) {
288 manual_mkey = TRUE;
289 global_params.mkey_from_kbd = 1;
290 global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
291 } else {
292 cmd_argv[cmd_argc++] = *argv;
293 }
294 argv++; argc--;
295 }
296
297 if (cmd_argv[0] == NULL)
298 usage();
299 cmd = cmd_lookup(cmd_argv[0]);
300 if (cmd == NULL)
301 usage();
302
303 if( !util_context->default_realm )
304 {
305 char *temp = NULL;
306 retval = krb5_get_default_realm(util_context, &temp);
307 if( retval )
308 {
309 com_err(progname, retval, _("while getting default realm"));
310 exit(1);
311 }
312 krb5_free_default_realm(util_context, temp);
313 }
314
315 retval = kadm5_get_config_params(util_context, 1,
316 &global_params, &global_params);
317 if (retval) {
318 com_err(progname, retval,
319 _("while retreiving configuration parameters"));
320 exit(1);
321 }
322
323 /*
324 * Dump creates files which should not be world-readable. It is
325 * easiest to do a single umask call here.
326 */
327 (void) umask(077);
328
329 master_keyblock.enctype = global_params.enctype;
330 if ((master_keyblock.enctype != ENCTYPE_UNKNOWN) &&
331 (!krb5_c_valid_enctype(master_keyblock.enctype))) {
332 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
333 "while setting up enctype %d", master_keyblock.enctype);
334 }
335
336 if (cmd->opendb && open_db_and_mkey())
337 return exit_status;
338
339 if (global_params.iprop_enabled == TRUE)
340 ulog_set_role(util_context, IPROP_MASTER);
341 else
342 ulog_set_role(util_context, IPROP_NULL);
343
344 (*cmd->func)(cmd_argc, cmd_argv);
345
346 if( db_name_tmp )
347 free( db_name_tmp );
348
349 if( db5util_db_args )
350 free(db5util_db_args);
351
352 quit();
353 kadm5_free_config_params(util_context, &global_params);
354 krb5_free_context(util_context);
355 free(cmd_argv);
356 return exit_status;
357 }
358
359 /*
360 * open_db_and_mkey: Opens the KDC and policy database, and sets the
361 * global master_* variables. Sets dbactive to TRUE if the databases
362 * are opened, and valid_master_key to 1 if the global master
363 * variables are set properly. Returns 0 on success, and 1 on
364 * failure, but it is not considered a failure if the master key
365 * cannot be fetched (the master key stash file may not exist when the
366 * program is run).
367 */
open_db_and_mkey()368 static int open_db_and_mkey()
369 {
370 krb5_error_code retval;
371 krb5_data scratch, pwd, seed;
372
373 dbactive = FALSE;
374 valid_master_key = 0;
375
376 if ((retval = krb5_db_open(util_context, db5util_db_args,
377 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) {
378 com_err(progname, retval, _("while initializing database"));
379 exit_status++;
380 return(1);
381 }
382
383 /* assemble & parse the master key name */
384
385 if ((retval = krb5_db_setup_mkey_name(util_context,
386 global_params.mkey_name,
387 global_params.realm,
388 &mkey_fullname, &master_princ))) {
389 com_err(progname, retval, _("while setting up master key name"));
390 exit_status++;
391 return(1);
392 }
393 if ((retval = krb5_db_get_principal(util_context, master_princ, 0,
394 &master_entry))) {
395 com_err(progname, retval, _("while retrieving master entry"));
396 exit_status++;
397 (void) krb5_db_fini(util_context);
398 return(1);
399 }
400
401 if (global_params.mask & KADM5_CONFIG_KVNO)
402 master_kvno = global_params.kvno; /* user specified */
403 else
404 master_kvno = IGNORE_VNO;
405
406 /* the databases are now open, and the master principal exists */
407 dbactive = TRUE;
408
409 if (mkey_password) {
410 pwd.data = mkey_password;
411 pwd.length = strlen(mkey_password);
412 retval = krb5_principal2salt(util_context, master_princ, &scratch);
413 if (retval) {
414 com_err(progname, retval, _("while calculated master key salt"));
415 exit_status++;
416 return(1);
417 }
418
419 /* If no encryption type is set, use the default */
420 if (master_keyblock.enctype == ENCTYPE_UNKNOWN)
421 master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
422 if (!krb5_c_valid_enctype(master_keyblock.enctype))
423 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
424 "while setting up enctype %d",
425 master_keyblock.enctype);
426
427 retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,
428 &pwd, &scratch, &master_keyblock);
429 if (retval) {
430 com_err(progname, retval,
431 _("while transforming master key from password"));
432 exit_status++;
433 return(1);
434 }
435 free(scratch.data);
436 mkey_password = 0;
437
438 } else {
439 if ((retval = krb5_db_fetch_mkey(util_context, master_princ,
440 master_keyblock.enctype,
441 manual_mkey, FALSE,
442 global_params.stash_file,
443 &master_kvno,
444 0, &master_keyblock))) {
445 com_err(progname, retval, _("while reading master key"));
446 com_err(progname, 0, _("Warning: proceeding without master key"));
447 exit_status++;
448 return(0);
449 }
450 }
451
452 if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ,
453 &master_keyblock))) {
454 com_err(progname, retval, "while getting master key list");
455 com_err(progname, 0, "Warning: proceeding without master key list");
456 exit_status++;
457 return(0);
458 }
459
460 seed.length = master_keyblock.length;
461 seed.data = (char *) master_keyblock.contents;
462
463 if ((retval = krb5_c_random_seed(util_context, &seed))) {
464 com_err(progname, retval, _("while seeding random number generator"));
465 exit_status++;
466 memset(master_keyblock.contents, 0, master_keyblock.length);
467 krb5_free_keyblock_contents(util_context, &master_keyblock);
468 return(1);
469 }
470
471 if (global_params.iprop_enabled) {
472 if (ulog_map(util_context, global_params.iprop_logfile,
473 global_params.iprop_ulogsize)) {
474 fprintf(stderr, _("%s: Could not map log\n"), progname);
475 exit_status++;
476 return(1);
477 }
478 }
479
480 valid_master_key = 1;
481 dbactive = TRUE;
482 return 0;
483 }
484
485 #ifdef HAVE_GETCWD
486 #undef getwd
487 #endif
488
489 int
quit()490 quit()
491 {
492 krb5_error_code retval;
493 static krb5_boolean finished = 0;
494
495 if (finished)
496 return 0;
497 ulog_fini(util_context);
498 retval = krb5_db_fini(util_context);
499 zapfree(master_keyblock.contents, master_keyblock.length);
500 krb5_free_principal(util_context, master_princ);
501 finished = TRUE;
502 if (retval && retval != KRB5_KDB_DBNOTINITED) {
503 com_err(progname, retval, _("while closing database"));
504 exit_status++;
505 return 1;
506 }
507 return 0;
508 }
509
510 static void
add_random_key(argc,argv)511 add_random_key(argc, argv)
512 int argc;
513 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 = NULL;
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 ret = krb5_db_put_principal(util_context, dbent);
604 krb5_db_free_principal(util_context, dbent);
605 if (ret) {
606 com_err(me, ret, _("while saving principal %s"), pr_str);
607 exit_status++;
608 return;
609 }
610 printf(_("%s changed\n"), pr_str);
611 }
612