1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* tests/verify/kdb5_verify.c */
3 /*
4 * Copyright 1990,1991 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 #include "k5-int.h"
28 #include "kdb.h"
29 #include "com_err.h"
30 #include <ss/ss.h>
31 #include <stdio.h>
32
33 #define REALM_SEP '@'
34 #define REALM_SEP_STR "@"
35
36 struct mblock {
37 krb5_deltat max_life;
38 krb5_deltat max_rlife;
39 krb5_timestamp expiration;
40 krb5_flags flags;
41 krb5_kvno mkvno;
42 } mblock = { /* XXX */
43 KRB5_KDB_MAX_LIFE,
44 KRB5_KDB_MAX_RLIFE,
45 KRB5_KDB_EXPIRATION,
46 KRB5_KDB_DEF_FLAGS,
47 0
48 };
49
50 int set_dbname_help (krb5_context, char *, char *);
51
52 static void
usage(char * who,int status)53 usage(char *who, int status)
54 {
55 fprintf(stderr,
56 "usage: %s -p prefix -n num_to_check [-d dbpathname] [-r realmname]\n",
57 who);
58 fprintf(stderr, "\t [-D depth] [-k enctype] [-M mkeyname]\n");
59
60 exit(status);
61 }
62
63 krb5_keyblock master_keyblock;
64 krb5_principal master_princ;
65 krb5_encrypt_block master_encblock;
66 krb5_pointer master_random;
67 char *str_master_princ;
68
69 static char *progname;
70 static char *cur_realm = 0;
71 static char *mkey_name = 0;
72 static char *mkey_password = 0;
73 static krb5_boolean manual_mkey = FALSE;
74
75
76 int check_princ (krb5_context, char *);
77
78 int
main(int argc,char * argv[])79 main(int argc, char *argv[])
80 {
81 extern char *optarg;
82 int optchar, i, n;
83 char tmp[4096], tmp2[BUFSIZ], *str_princ;
84
85 krb5_context context;
86 krb5_error_code retval;
87 char *dbname = 0;
88 int enctypedone = 0;
89 int num_to_check;
90 char principal_string[BUFSIZ];
91 char *suffix = 0;
92 size_t suffix_size = 0;
93 int depth, errors;
94
95 krb5_init_context(&context);
96
97 if (strrchr(argv[0], '/'))
98 argv[0] = strrchr(argv[0], '/')+1;
99
100 progname = argv[0];
101
102 memset(principal_string, 0, sizeof(principal_string));
103 num_to_check = 0;
104 depth = 1;
105
106 while ((optchar = getopt(argc, argv, "D:P:p:n:d:r:R:k:M:e:m")) != -1) {
107 switch(optchar) {
108 case 'D':
109 depth = atoi(optarg); /* how deep to go */
110 break;
111 case 'P': /* Only used for testing!!! */
112 mkey_password = optarg;
113 break;
114 case 'p': /* prefix name to check */
115 strncpy(principal_string, optarg, sizeof(principal_string) - 1);
116 principal_string[sizeof(principal_string) - 1] = '\0';
117 suffix = principal_string + strlen(principal_string);
118 suffix_size = sizeof(principal_string) -
119 (suffix - principal_string);
120 break;
121 case 'n': /* how many to check */
122 num_to_check = atoi(optarg);
123 break;
124 case 'd': /* set db name */
125 dbname = optarg;
126 break;
127 case 'r':
128 cur_realm = optarg;
129 break;
130 case 'k':
131 master_keyblock.enctype = atoi(optarg);
132 enctypedone++;
133 break;
134 case 'M': /* master key name in DB */
135 mkey_name = optarg;
136 break;
137 case 'm':
138 manual_mkey = TRUE;
139 break;
140 case '?':
141 default:
142 usage(progname, 1);
143 /*NOTREACHED*/
144 }
145 }
146
147 if (!(num_to_check && suffix)) usage(progname, 1);
148
149 if (!enctypedone)
150 master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
151
152 if (!krb5_c_valid_enctype(master_keyblock.enctype)) {
153 com_err(progname, KRB5_PROG_ETYPE_NOSUPP,
154 "while setting up enctype %d", master_keyblock.enctype);
155 exit(1);
156 }
157
158 krb5_use_enctype(context, &master_encblock, master_keyblock.enctype);
159
160 if (!dbname)
161 dbname = DEFAULT_KDB_FILE; /* XXX? */
162
163 if (!cur_realm) {
164 if ((retval = krb5_get_default_realm(context, &cur_realm))) {
165 com_err(progname, retval, "while retrieving default realm name");
166 exit(1);
167 }
168 }
169 if ((retval = set_dbname_help(context, progname, dbname)))
170 exit(retval);
171
172 errors = 0;
173
174 fprintf(stdout, "\nChecking ");
175
176 for (n = 1; n <= num_to_check; n++) {
177 /* build the new principal name */
178 /* we can't pick random names because we need to generate all the names
179 again given a prefix and count to test the db lib and kdb */
180 (void) snprintf(suffix, suffix_size, "%d", n);
181 (void) snprintf(tmp, sizeof(tmp), "%s-DEPTH-1", principal_string);
182 str_princ = tmp;
183 if (check_princ(context, str_princ)) errors++;
184
185 for (i = 2; i <= depth; i++) {
186 (void) snprintf(tmp2, sizeof(tmp2), "/%s-DEPTH-%d",
187 principal_string, i);
188 tmp2[sizeof(tmp2) - 1] = '\0';
189 strncat(tmp, tmp2, sizeof(tmp) - 1 - strlen(tmp));
190 str_princ = tmp;
191 if (check_princ(context, str_princ)) errors++;
192 }
193 }
194
195 if (errors)
196 fprintf(stdout, "\n%d errors/principals failed.\n", errors);
197 else
198 fprintf(stdout, "\nNo errors.\n");
199
200 krb5_finish_random_key(context, &master_encblock, &master_random);
201 krb5_finish_key(context, &master_encblock);
202
203 retval = krb5_db_fini(context);
204 memset(master_keyblock.contents, 0, (size_t) master_keyblock.length);
205 if (retval && retval != KRB5_KDB_DBNOTINITED) {
206 com_err(progname, retval, "while closing database");
207 exit(1);
208 }
209 krb5_free_keyblock_contents(context, &master_keyblock);
210
211 if (str_master_princ) {
212 krb5_free_unparsed_name(context, str_master_princ);
213 }
214 krb5_free_principal(context, master_princ);
215 krb5_free_context(context);
216 exit(0);
217 }
218
219 int
check_princ(krb5_context context,char * str_princ)220 check_princ(krb5_context context, char *str_princ)
221 {
222 krb5_error_code retval;
223 krb5_db_entry *kdbe = NULL;
224 krb5_keyblock pwd_key, db_key;
225 krb5_data pwd, salt;
226 krb5_principal princ;
227 /* char *str_mod_name; */
228 char princ_name[4096];
229
230 snprintf(princ_name, sizeof(princ_name), "%s@%s", str_princ, cur_realm);
231
232 if ((retval = krb5_parse_name(context, princ_name, &princ))) {
233 com_err(progname, retval, "while parsing '%s'", princ_name);
234 goto out;
235 }
236
237 pwd.data = princ_name; /* must be able to regenerate */
238 pwd.length = strlen(princ_name);
239
240 if ((retval = krb5_principal2salt(context, princ, &salt))) {
241 com_err(progname, retval, "while converting principal to salt for '%s'", princ_name);
242 krb5_free_principal(context, princ);
243 goto out;
244 }
245
246 if ((retval = krb5_string_to_key(context, &master_encblock,
247 &pwd_key, &pwd, &salt))) {
248 com_err(progname, retval, "while converting password to key for '%s'",
249 princ_name);
250 krb5_free_data_contents(context, &salt);
251 krb5_free_principal(context, princ);
252 goto out;
253 }
254 krb5_free_data_contents(context, &salt);
255
256 if ((retval = krb5_db_get_principal(context, princ, 0, &kdbe))) {
257 com_err(progname, retval, "while attempting to verify principal's existence");
258 krb5_free_principal(context, princ);
259 goto out;
260 }
261 krb5_free_principal(context, princ);
262
263 if ((retval = krb5_dbe_decrypt_key_data(context, NULL,
264 kdbe->key_data, &db_key, NULL))) {
265 com_err(progname, retval, "while decrypting key for '%s'", princ_name);
266 goto errout;
267 }
268
269 if ((pwd_key.enctype != db_key.enctype) ||
270 (pwd_key.length != db_key.length)) {
271 fprintf (stderr, "\tKey types do not agree (%d expected, %d from db)\n",
272 pwd_key.enctype, db_key.enctype);
273 errout:
274 krb5_db_free_principal(context, kdbe);
275 return(-1);
276 }
277 else {
278 if (memcmp((char *)pwd_key.contents, (char *) db_key.contents,
279 (size_t) pwd_key.length)) {
280 fprintf(stderr, "\t key did not match stored value for %s\n",
281 princ_name);
282 goto errout;
283 }
284 }
285
286 free(pwd_key.contents);
287 free(db_key.contents);
288
289 if (kdbe->key_data[0].key_data_kvno != 1) {
290 fprintf(stderr,"\tkvno did not match stored value for %s.\n", princ_name);
291 goto errout;
292 }
293
294 if (kdbe->max_life != mblock.max_life) {
295 fprintf(stderr, "\tmax life did not match stored value for %s.\n",
296 princ_name);
297 goto errout;
298 }
299
300 if (kdbe->max_renewable_life != mblock.max_rlife) {
301 fprintf(stderr,
302 "\tmax renewable life did not match stored value for %s.\n",
303 princ_name);
304 goto errout;
305 }
306
307 if (kdbe->expiration != mblock.expiration) {
308 fprintf(stderr, "\texpiration time did not match stored value for %s.\n",
309 princ_name);
310 goto errout;
311 }
312
313 /*
314 if ((retval = krb5_unparse_name(context, kdbe.mod_name, &str_mod_name)))
315 com_err(progname, retval, "while unparsing mode name");
316 else {
317 if (strcmp(str_mod_name, str_master_princ) != 0) {
318 fprintf(stderr, "\tmod name isn't the master princ (%s not %s).\n",
319 str_mod_name, str_master_princ);
320 free(str_mod_name);
321 goto errout;
322 }
323 else free(str_mod_name);
324 }
325 */
326
327 if (kdbe->attributes != mblock.flags) {
328 fprintf(stderr, "\tAttributes did not match stored value for %s.\n",
329 princ_name);
330 goto errout;
331 }
332
333 out:
334 krb5_db_free_principal(context, kdbe);
335
336 return(0);
337 }
338
339 int
set_dbname_help(krb5_context context,char * pname,char * dbname)340 set_dbname_help(krb5_context context, char *pname, char *dbname)
341 {
342 krb5_error_code retval;
343 krb5_data pwd, scratch;
344 char *args[2];
345 krb5_db_entry *master_entry;
346
347 /* assemble & parse the master key name */
348
349 if ((retval = krb5_db_setup_mkey_name(context, mkey_name, cur_realm, 0,
350 &master_princ))) {
351 com_err(pname, retval, "while setting up master key name");
352 return(1);
353 }
354 if (mkey_password) {
355 pwd.data = mkey_password;
356 pwd.length = strlen(mkey_password);
357 retval = krb5_principal2salt(context, master_princ, &scratch);
358 if (retval) {
359 com_err(pname, retval, "while calculated master key salt");
360 return(1);
361 }
362 if ((retval = krb5_string_to_key(context, &master_encblock,
363 &master_keyblock, &pwd, &scratch))) {
364 com_err(pname, retval,
365 "while transforming master key from password");
366 return(1);
367 }
368 free(scratch.data);
369 } else {
370 if ((retval = krb5_db_fetch_mkey(context, master_princ,
371 master_keyblock.enctype,
372 manual_mkey, FALSE, (char *) NULL,
373 NULL, NULL,
374 &master_keyblock))) {
375 com_err(pname, retval, "while reading master key");
376 return(1);
377 }
378 }
379
380 /* Ick! Current DAL interface requires that the default_realm
381 field be set in the krb5_context. */
382 if ((retval = krb5_set_default_realm(context, cur_realm))) {
383 com_err(pname, retval, "setting default realm");
384 return 1;
385 }
386 /* Pathname is passed to db2 via 'args' parameter. */
387 args[1] = NULL;
388 if (asprintf(&args[0], "dbname=%s", dbname) < 0) {
389 com_err(pname, errno, "while setting up db parameters");
390 return 1;
391 }
392
393 if ((retval = krb5_db_open(context, args, KRB5_KDB_OPEN_RO))) {
394 com_err(pname, retval, "while initializing database");
395 return(1);
396 }
397 if ((retval = krb5_db_fetch_mkey_list(context, master_princ,
398 &master_keyblock))) {
399 com_err(pname, retval, "while verifying master key");
400 (void) krb5_db_fini(context);
401 return(1);
402 }
403 if ((retval = krb5_db_get_principal(context, master_princ, 0,
404 &master_entry))) {
405 com_err(pname, retval, "while retrieving master entry");
406 (void) krb5_db_fini(context);
407 return(1);
408 }
409
410 if ((retval = krb5_unparse_name(context, master_princ,
411 &str_master_princ))) {
412 com_err(pname, retval, "while unparsing master principal");
413 krb5_db_fini(context);
414 return(1);
415 }
416
417 if ((retval = krb5_process_key(context,
418 &master_encblock, &master_keyblock))) {
419 com_err(pname, retval, "while processing master key");
420 (void) krb5_db_fini(context);
421 return(1);
422 }
423 if ((retval = krb5_init_random_key(context,
424 &master_encblock, &master_keyblock,
425 &master_random))) {
426 com_err(pname, retval, "while initializing random key generator");
427 krb5_finish_key(context, &master_encblock);
428 (void) krb5_db_fini(context);
429 return(1);
430 }
431 mblock.max_life = master_entry->max_life;
432 mblock.max_rlife = master_entry->max_renewable_life;
433 mblock.expiration = master_entry->expiration;
434 /* don't set flags, master has some extra restrictions */
435 mblock.mkvno = master_entry->key_data[0].key_data_kvno;
436
437 krb5_db_free_principal(context, master_entry);
438 free(args[0]);
439 return 0;
440 }
441