xref: /freebsd/crypto/krb5/src/tests/verify/kdb5_verify.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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