xref: /freebsd/crypto/krb5/src/tests/hammer/kdc5_hammer.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* tests/hammer/kdc5_hammer.c */
2 /*
3  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
4  * All Rights Reserved.
5  *
6  * Export of this software from the United States of America may
7  *   require a specific license from the United States Government.
8  *   It is the responsibility of any person or organization contemplating
9  *   export to obtain such a license before exporting.
10  *
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  Furthermore if you modify this software you must label
19  * your software as modified software and not distribute it in such a
20  * fashion that it might be confused with the original M.I.T. software.
21  * M.I.T. makes no representations about the suitability of
22  * this software for any purpose.  It is provided "as is" without express
23  * or implied warranty.
24  */
25 
26 #include "k5-int.h"
27 #include "com_err.h"
28 #include <sys/time.h>
29 
30 #define KRB5_DEFAULT_OPTIONS 0
31 #define KRB5_DEFAULT_LIFE 60*60*8 /* 8 hours */
32 #define KRB5_RENEWABLE_LIFE 60*60*2 /* 2 hours */
33 
34 struct h_timer {
35     float	ht_cumulative;
36     float	ht_min;
37     float	ht_max;
38     krb5_int32	ht_observations;
39 };
40 
41 extern int optind;
42 extern char *optarg;
43 char *prog;
44 
45 static int brief;
46 static char *cur_realm = 0;
47 static int do_timer = 0;
48 
49 krb5_data tgtname = {
50     0,
51     KRB5_TGS_NAME_SIZE,
52     KRB5_TGS_NAME
53 };
54 
55 int verify_cs_pair
56 	(krb5_context,
57 		   char *,
58 		   krb5_principal,
59 		   char *,
60 		   char *,
61 		   int, int, int,
62 		   krb5_ccache);
63 
64 int get_tgt
65 	(krb5_context,
66 		   char *,
67 		   krb5_principal *,
68 		   krb5_ccache);
69 
70 static void
usage(who,status)71 usage(who, status)
72 char *who;
73 int status;
74 {
75     fprintf(stderr,
76 	    "usage: %s -p prefix -n num_to_check [-c cachename] [-r realmname]\n",
77 	    who);
78     fprintf(stderr, "\t [-D depth]\n");
79     fprintf(stderr, "\t [-P preauth type] [-R repeat_count] [-t] [-b] [-v] \n");
80 
81     exit(status);
82 }
83 
84 static krb5_preauthtype * patype = NULL, patypedata[2] = { 0, -1 };
85 static krb5_context test_context;
86 
87 struct timeval	tstart_time, tend_time;
88 struct timezone	dontcare;
89 
90 struct h_timer in_tkt_times = { 0.0, 1000000.0, -1.0, 0 };
91 struct h_timer tgs_req_times = { 0.0, 1000000.0, -1.0, 0 };
92 /*
93  * Timer macros.
94  */
95 #define	swatch_on()	((void) gettimeofday(&tstart_time, &dontcare))
96 #define	swatch_eltime()	((gettimeofday(&tend_time, &dontcare)) ? -1.0 :	\
97 			 (((float) (tend_time.tv_sec -			\
98 				    tstart_time.tv_sec)) +		\
99 			  (((float) (tend_time.tv_usec -		\
100 				     tstart_time.tv_usec))/1000000.0)))
101 
102 int
main(argc,argv)103 main(argc, argv)
104     int argc;
105     char **argv;
106 {
107     krb5_ccache ccache = NULL;
108     char *cache_name = NULL;		/* -f option */
109     int option;
110     int errflg = 0;
111     krb5_error_code code;
112     int num_to_check, n, i, j, repeat_count, counter;
113     int n_tried, errors;
114     char prefix[BUFSIZ], client[4096], server[4096];
115     int depth;
116     char ctmp[4096], ctmp2[BUFSIZ], stmp[4096], stmp2[BUFSIZ];
117     krb5_principal client_princ;
118     krb5_error_code retval;
119 
120     krb5_init_context(&test_context);
121 
122     if (strrchr(argv[0], '/'))
123 	prog = strrchr(argv[0], '/')+1;
124     else
125 	prog = argv[0];
126 
127     num_to_check = 0;
128     depth = 1;
129     repeat_count = 1;
130     brief = 0;
131     n_tried = 0;
132     errors = 0;
133 
134     while ((option = getopt(argc, argv, "D:p:n:c:R:P:e:bvr:t")) != -1) {
135 	switch (option) {
136 	case 't':
137 	    do_timer = 1;
138 	    break;
139 	case 'b':
140 	    brief = 1;
141 	    break;
142 	case 'v':
143 	    brief = 0;
144 	    break;
145 	case 'R':
146 	    repeat_count = atoi(optarg); /* how many times? */
147 	    break;
148 	case 'r':
149 	    cur_realm = optarg;
150 	    break;
151 	case 'D':
152 	    depth = atoi(optarg);       /* how deep to go */
153 	    break;
154 	case 'p':                       /* prefix name to check */
155 	    strncpy(prefix, optarg, sizeof(prefix) - 1);
156 	    prefix[sizeof(prefix) - 1] = '\0';
157 	    break;
158        case 'n':                        /* how many to check */
159 	    num_to_check = atoi(optarg);
160 	    break;
161 	case 'P':
162 	    patypedata[0] = atoi(optarg);
163 	    patype = patypedata;
164 	    break;
165 	case 'c':
166 	    if (ccache == NULL) {
167 		cache_name = optarg;
168 
169 		code = krb5_cc_resolve (test_context, cache_name, &ccache);
170 		if (code != 0) {
171 		    com_err (prog, code, "resolving %s", cache_name);
172 		    errflg++;
173 		}
174 	    } else {
175 		fprintf(stderr, "Only one -c option allowed\n");
176 		errflg++;
177 	    }
178 	    break;
179 	case '?':
180 	default:
181 	    errflg++;
182 	    break;
183 	}
184     }
185 
186     if (!(num_to_check && prefix[0])) usage(prog, 1);
187 
188     if (!cur_realm) {
189 	if ((retval = krb5_get_default_realm(test_context, &cur_realm))) {
190 	    com_err(prog, retval, "while retrieving default realm name");
191 	    exit(1);
192 	}
193     }
194 
195     if (ccache == NULL) {
196 	if ((code = krb5_cc_default(test_context, &ccache))) {
197 	    com_err(prog, code, "while getting default ccache");
198 	    exit(1);
199 	}
200     }
201 
202     memset(ctmp, 0, sizeof(ctmp));
203     memset(stmp, 0, sizeof(stmp));
204 
205     for (counter = 0; counter < repeat_count; counter++) {
206       fprintf(stderr, "\nRound %d\n", counter);
207 
208       for (n = 1; n <= num_to_check; n++) {
209 	/* build the new principal name */
210 	/* we can't pick random names because we need to generate all the names
211 	   again given a prefix and count to test the db lib and kdb */
212 	ctmp[0] = '\0';
213 	for (i = 1; i <= depth; i++) {
214 	  (void) snprintf(ctmp2, sizeof(ctmp2), "%s%s%d-DEPTH-%d",
215 			  (i != 1) ? "/" : "", prefix, n, i);
216 	  ctmp2[sizeof(ctmp2) - 1] = '\0';
217 	  strncat(ctmp, ctmp2, sizeof(ctmp) - 1 - strlen(ctmp));
218 	  ctmp[sizeof(ctmp) - 1] = '\0';
219 	  snprintf(client, sizeof(client), "%s@%s", ctmp, cur_realm);
220 
221 	  if (get_tgt (test_context, client, &client_princ, ccache)) {
222 	    errors++;
223 	    n_tried++;
224 	    continue;
225 	  }
226 	  n_tried++;
227 
228 	  stmp[0] = '\0';
229 	  for (j = 1; j <= depth; j++) {
230 	    (void) snprintf(stmp2, sizeof(stmp2), "%s%s%d-DEPTH-%d",
231 			    (j != 1) ? "/" : "", prefix, n, j);
232 	    stmp2[sizeof (stmp2) - 1] = '\0';
233 	    strncat(stmp, stmp2, sizeof(stmp) - 1 - strlen(stmp));
234 	    stmp[sizeof(stmp) - 1] = '\0';
235 	    snprintf(server, sizeof(server), "%s@%s", stmp, cur_realm);
236 	    if (verify_cs_pair(test_context, client, client_princ,
237 			       stmp, cur_realm, n, i, j, ccache))
238 	      errors++;
239 	    n_tried++;
240 	  }
241 	  krb5_free_principal(test_context, client_princ);
242 	}
243       }
244     }
245     fprintf (stderr, "\nTried %d.  Got %d errors.\n", n_tried, errors);
246     if (do_timer) {
247 	if (in_tkt_times.ht_observations)
248 	    fprintf(stderr,
249 		    "%8d  AS_REQ requests: %9.6f average (min: %9.6f, max:%9.6f)\n",
250 		    in_tkt_times.ht_observations,
251 		    in_tkt_times.ht_cumulative /
252 		    (float) in_tkt_times.ht_observations,
253 		    in_tkt_times.ht_min,
254 		    in_tkt_times.ht_max);
255 	if (tgs_req_times.ht_observations)
256 	    fprintf(stderr,
257 		    "%8d TGS_REQ requests: %9.6f average (min: %9.6f, max:%9.6f)\n",
258 		    tgs_req_times.ht_observations,
259 		    tgs_req_times.ht_cumulative /
260 		    (float) tgs_req_times.ht_observations,
261 		    tgs_req_times.ht_min,
262 		    tgs_req_times.ht_max);
263     }
264 
265     (void) krb5_cc_close(test_context, ccache);
266 
267     krb5_free_context(test_context);
268 
269     exit(errors);
270   }
271 
272 
273 static krb5_error_code
get_server_key(context,server,enctype,key)274 get_server_key(context, server, enctype, key)
275     krb5_context context;
276     krb5_principal server;
277     krb5_enctype enctype;
278     krb5_keyblock ** key;
279 {
280     krb5_error_code retval;
281     krb5_encrypt_block eblock;
282     char * string;
283     krb5_data salt;
284     krb5_data pwd;
285 
286     *key = NULL;
287 
288     if ((retval = krb5_principal2salt(context, server, &salt)))
289 	return retval;
290 
291     if ((retval = krb5_unparse_name(context, server, &string)))
292 	goto cleanup_salt;
293 
294     pwd.data = string;
295     pwd.length = strlen(string);
296 
297     if ((*key = (krb5_keyblock *)malloc(sizeof(krb5_keyblock)))) {
298     	krb5_use_enctype(context, &eblock, enctype);
299 	retval = krb5_string_to_key(context, &eblock, *key, &pwd, &salt);
300 	if (retval) {
301 	    free(*key);
302 	    *key = NULL;
303 	}
304     } else
305         retval = ENOMEM;
306 
307     free(string);
308 
309 cleanup_salt:
310     free(salt.data);
311     return retval;
312 }
313 
verify_cs_pair(context,p_client_str,p_client,service,hostname,p_num,c_depth,s_depth,ccache)314 int verify_cs_pair(context, p_client_str, p_client, service, hostname,
315 		   p_num, c_depth, s_depth, ccache)
316     krb5_context context;
317     char *p_client_str;
318     krb5_principal p_client;
319     char * service;
320     char * hostname;
321     int p_num, c_depth, s_depth;
322     krb5_ccache ccache;
323 {
324     krb5_error_code 	  retval;
325     krb5_creds 	 	  creds;
326     krb5_creds 		* credsp = NULL;
327     krb5_ticket 	* ticket = NULL;
328     krb5_keyblock 	* keyblock = NULL;
329     krb5_auth_context 	  auth_context = NULL;
330     krb5_data		  request_data = empty_data();
331     char		* sname;
332     float		  dt;
333 
334     if (brief)
335       fprintf(stderr, "\tprinc (%d) client (%d) for server (%d)\n",
336 	      p_num, c_depth, s_depth);
337     else
338       fprintf(stderr, "\tclient %s for server %s\n", p_client_str,
339 	      service);
340 
341     /* Initialize variables */
342     memset(&creds, 0, sizeof(creds));
343 
344     /* Do client side */
345     if (asprintf(&sname, "%s@%s", service, hostname) >= 0) {
346 	retval = krb5_parse_name(context, sname, &creds.server);
347 	free(sname);
348     }
349     else
350 	retval = ENOMEM;
351     if (retval)
352 	return(retval);
353 
354     /* obtain ticket & session key */
355     if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
356 	com_err(prog, retval, "while getting client princ for %s", hostname);
357 	return retval;
358     }
359 
360     if ((retval = krb5_get_credentials(context, 0,
361                                       ccache, &creds, &credsp))) {
362 	com_err(prog, retval, "while getting creds for %s", hostname);
363 	return retval;
364     }
365 
366     if (do_timer)
367 	swatch_on();
368 
369     if ((retval = krb5_mk_req_extended(context, &auth_context, 0, NULL,
370 			            credsp, &request_data))) {
371 	com_err(prog, retval, "while preparing AP_REQ for %s", hostname);
372 	goto cleanup;
373     }
374 
375     krb5_auth_con_free(context, auth_context);
376     auth_context = NULL;
377 
378     /* Do server side now */
379     if ((retval = get_server_key(context, credsp->server,
380 				credsp->keyblock.enctype, &keyblock))) {
381 	com_err(prog, retval, "while getting server key for %s", hostname);
382 	goto cleanup;
383     }
384 
385     if (krb5_auth_con_init(context, &auth_context)) {
386 	com_err(prog, retval, "while creating auth_context for %s", hostname);
387 	goto cleanup;
388     }
389 
390     if (krb5_auth_con_setuseruserkey(context, auth_context, keyblock)) {
391 	com_err(prog, retval, "while setting auth_context key %s", hostname);
392 	goto cleanup;
393     }
394 
395     if ((retval = krb5_rd_req(context, &auth_context, &request_data,
396 			     NULL /* server */, 0, NULL, &ticket))) {
397 	com_err(prog, retval, "while decoding AP_REQ for %s", hostname);
398 	goto cleanup;
399     }
400 
401     if (do_timer) {
402 	dt = swatch_eltime();
403 	tgs_req_times.ht_cumulative += dt;
404 	tgs_req_times.ht_observations++;
405 	if (dt > tgs_req_times.ht_max)
406 	    tgs_req_times.ht_max = dt;
407 	if (dt < tgs_req_times.ht_min)
408 	    tgs_req_times.ht_min = dt;
409     }
410 
411     if (!(krb5_principal_compare(context,ticket->enc_part2->client,p_client))){
412     	char *returned_client;
413         if ((retval = krb5_unparse_name(context, ticket->enc_part2->client,
414 			       	       &returned_client)))
415 	    com_err (prog, retval,
416 		     "Client not as expected, but cannot unparse client name");
417       	else
418 	    com_err (prog, 0, "Client not as expected (%s).", returned_client);
419 	retval = KRB5_PRINC_NOMATCH;
420       	free(returned_client);
421     } else {
422 	retval = 0;
423     }
424 
425 cleanup:
426     krb5_free_cred_contents(context, &creds);
427     krb5_free_ticket(context, ticket);
428     krb5_auth_con_free(context, auth_context);
429     krb5_free_keyblock(context, keyblock);
430     krb5_free_data_contents(context, &request_data);
431     krb5_free_creds(context, credsp);
432 
433     return retval;
434 }
435 
get_tgt(context,p_client_str,p_client,ccache)436 int get_tgt (context, p_client_str, p_client, ccache)
437     krb5_context context;
438     char *p_client_str;
439     krb5_principal *p_client;
440     krb5_ccache ccache;
441 {
442     long lifetime = KRB5_DEFAULT_LIFE;	/* -l option */
443     krb5_error_code code;
444     krb5_creds my_creds;
445     krb5_timestamp start;
446     float dt;
447     krb5_get_init_creds_opt *options;
448 
449     if (!brief)
450       fprintf(stderr, "\tgetting TGT for %s\n", p_client_str);
451 
452     if ((code = krb5_timeofday(context, &start))) {
453 	com_err(prog, code, "while getting time of day");
454 	return(-1);
455     }
456 
457     memset(&my_creds, 0, sizeof(my_creds));
458 
459     if ((code = krb5_parse_name (context, p_client_str, p_client))) {
460 	com_err (prog, code, "when parsing name %s", p_client_str);
461 	return(-1);
462     }
463 
464     code = krb5_cc_initialize (context, ccache, *p_client);
465     if (code != 0) {
466 	com_err (prog, code, "when initializing cache");
467 	return(-1);
468     }
469 
470     if (do_timer)
471 	swatch_on();
472 
473     code = krb5_get_init_creds_opt_alloc(context, &options);
474     if (code != 0) {
475 	com_err(prog, code, "when allocating init cred options");
476 	return(-1);
477     }
478 
479     krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
480 
481     code = krb5_get_init_creds_opt_set_out_ccache(context, options, ccache);
482     if (code != 0) {
483 	com_err(prog, code, "when setting init cred output ccache");
484 	return(-1);
485     }
486 
487     code = krb5_get_init_creds_password(context, &my_creds, *p_client,
488 					p_client_str, NULL, NULL, 0, NULL,
489 					options);
490     if (do_timer) {
491 	dt = swatch_eltime();
492 	in_tkt_times.ht_cumulative += dt;
493 	in_tkt_times.ht_observations++;
494 	if (dt > in_tkt_times.ht_max)
495 	    in_tkt_times.ht_max = dt;
496 	if (dt < in_tkt_times.ht_min)
497 	    in_tkt_times.ht_min = dt;
498     }
499     krb5_get_init_creds_opt_free(context, options);
500     krb5_free_cred_contents(context, &my_creds);
501     if (code != 0) {
502 	com_err (prog, code, "while getting initial credentials");
503 	return(-1);
504       }
505 
506     return(0);
507 }
508