xref: /freebsd/crypto/krb5/src/tests/hammer/kdc5_hammer.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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(char * who,int status)71 usage(char *who, int status)
72 {
73     fprintf(stderr,
74 	    "usage: %s -p prefix -n num_to_check [-c cachename] [-r realmname]\n",
75 	    who);
76     fprintf(stderr, "\t [-D depth]\n");
77     fprintf(stderr, "\t [-P preauth type] [-R repeat_count] [-t] [-b] [-v] \n");
78 
79     exit(status);
80 }
81 
82 static krb5_preauthtype * patype = NULL, patypedata[2] = { 0, -1 };
83 static krb5_context test_context;
84 
85 struct timeval	tstart_time, tend_time;
86 struct timezone	dontcare;
87 
88 struct h_timer in_tkt_times = { 0.0, 1000000.0, -1.0, 0 };
89 struct h_timer tgs_req_times = { 0.0, 1000000.0, -1.0, 0 };
90 /*
91  * Timer macros.
92  */
93 #define	swatch_on()	((void) gettimeofday(&tstart_time, &dontcare))
94 #define	swatch_eltime()	((gettimeofday(&tend_time, &dontcare)) ? -1.0 :	\
95 			 (((float) (tend_time.tv_sec -			\
96 				    tstart_time.tv_sec)) +		\
97 			  (((float) (tend_time.tv_usec -		\
98 				     tstart_time.tv_usec))/1000000.0)))
99 
100 int
main(int argc,char ** argv)101 main(int argc, char **argv)
102 {
103     krb5_ccache ccache = NULL;
104     char *cache_name = NULL;		/* -f option */
105     int option;
106     int errflg = 0;
107     krb5_error_code code;
108     int num_to_check, n, i, j, repeat_count, counter;
109     int n_tried, errors;
110     char prefix[BUFSIZ], client[4096], server[4096];
111     int depth;
112     char ctmp[4096], ctmp2[BUFSIZ], stmp[4096], stmp2[BUFSIZ];
113     krb5_principal client_princ;
114     krb5_error_code retval;
115 
116     krb5_init_context(&test_context);
117 
118     if (strrchr(argv[0], '/'))
119 	prog = strrchr(argv[0], '/')+1;
120     else
121 	prog = argv[0];
122 
123     num_to_check = 0;
124     depth = 1;
125     repeat_count = 1;
126     brief = 0;
127     n_tried = 0;
128     errors = 0;
129 
130     while ((option = getopt(argc, argv, "D:p:n:c:R:P:e:bvr:t")) != -1) {
131 	switch (option) {
132 	case 't':
133 	    do_timer = 1;
134 	    break;
135 	case 'b':
136 	    brief = 1;
137 	    break;
138 	case 'v':
139 	    brief = 0;
140 	    break;
141 	case 'R':
142 	    repeat_count = atoi(optarg); /* how many times? */
143 	    break;
144 	case 'r':
145 	    cur_realm = optarg;
146 	    break;
147 	case 'D':
148 	    depth = atoi(optarg);       /* how deep to go */
149 	    break;
150 	case 'p':                       /* prefix name to check */
151 	    strncpy(prefix, optarg, sizeof(prefix) - 1);
152 	    prefix[sizeof(prefix) - 1] = '\0';
153 	    break;
154        case 'n':                        /* how many to check */
155 	    num_to_check = atoi(optarg);
156 	    break;
157 	case 'P':
158 	    patypedata[0] = atoi(optarg);
159 	    patype = patypedata;
160 	    break;
161 	case 'c':
162 	    if (ccache == NULL) {
163 		cache_name = optarg;
164 
165 		code = krb5_cc_resolve (test_context, cache_name, &ccache);
166 		if (code != 0) {
167 		    com_err (prog, code, "resolving %s", cache_name);
168 		    errflg++;
169 		}
170 	    } else {
171 		fprintf(stderr, "Only one -c option allowed\n");
172 		errflg++;
173 	    }
174 	    break;
175 	case '?':
176 	default:
177 	    errflg++;
178 	    break;
179 	}
180     }
181 
182     if (!(num_to_check && prefix[0]) || errflg)
183 	usage(prog, 1);
184 
185     if (!cur_realm) {
186 	if ((retval = krb5_get_default_realm(test_context, &cur_realm))) {
187 	    com_err(prog, retval, "while retrieving default realm name");
188 	    exit(1);
189 	}
190     }
191 
192     if (ccache == NULL) {
193 	if ((code = krb5_cc_default(test_context, &ccache))) {
194 	    com_err(prog, code, "while getting default ccache");
195 	    exit(1);
196 	}
197     }
198 
199     memset(ctmp, 0, sizeof(ctmp));
200     memset(stmp, 0, sizeof(stmp));
201 
202     for (counter = 0; counter < repeat_count; counter++) {
203       fprintf(stderr, "\nRound %d\n", counter);
204 
205       for (n = 1; n <= num_to_check; n++) {
206 	/* build the new principal name */
207 	/* we can't pick random names because we need to generate all the names
208 	   again given a prefix and count to test the db lib and kdb */
209 	ctmp[0] = '\0';
210 	for (i = 1; i <= depth; i++) {
211 	  (void) snprintf(ctmp2, sizeof(ctmp2), "%s%s%d-DEPTH-%d",
212 			  (i != 1) ? "/" : "", prefix, n, i);
213 	  ctmp2[sizeof(ctmp2) - 1] = '\0';
214 	  strncat(ctmp, ctmp2, sizeof(ctmp) - 1 - strlen(ctmp));
215 	  ctmp[sizeof(ctmp) - 1] = '\0';
216 	  snprintf(client, sizeof(client), "%s@%s", ctmp, cur_realm);
217 
218 	  if (get_tgt (test_context, client, &client_princ, ccache)) {
219 	    errors++;
220 	    n_tried++;
221 	    continue;
222 	  }
223 	  n_tried++;
224 
225 	  stmp[0] = '\0';
226 	  for (j = 1; j <= depth; j++) {
227 	    (void) snprintf(stmp2, sizeof(stmp2), "%s%s%d-DEPTH-%d",
228 			    (j != 1) ? "/" : "", prefix, n, j);
229 	    stmp2[sizeof (stmp2) - 1] = '\0';
230 	    strncat(stmp, stmp2, sizeof(stmp) - 1 - strlen(stmp));
231 	    stmp[sizeof(stmp) - 1] = '\0';
232 	    snprintf(server, sizeof(server), "%s@%s", stmp, cur_realm);
233 	    if (verify_cs_pair(test_context, client, client_princ,
234 			       stmp, cur_realm, n, i, j, ccache))
235 	      errors++;
236 	    n_tried++;
237 	  }
238 	  krb5_free_principal(test_context, client_princ);
239 	}
240       }
241     }
242     fprintf (stderr, "\nTried %d.  Got %d errors.\n", n_tried, errors);
243     if (do_timer) {
244 	if (in_tkt_times.ht_observations)
245 	    fprintf(stderr,
246 		    "%8d  AS_REQ requests: %9.6f average (min: %9.6f, max:%9.6f)\n",
247 		    in_tkt_times.ht_observations,
248 		    in_tkt_times.ht_cumulative /
249 		    (float) in_tkt_times.ht_observations,
250 		    in_tkt_times.ht_min,
251 		    in_tkt_times.ht_max);
252 	if (tgs_req_times.ht_observations)
253 	    fprintf(stderr,
254 		    "%8d TGS_REQ requests: %9.6f average (min: %9.6f, max:%9.6f)\n",
255 		    tgs_req_times.ht_observations,
256 		    tgs_req_times.ht_cumulative /
257 		    (float) tgs_req_times.ht_observations,
258 		    tgs_req_times.ht_min,
259 		    tgs_req_times.ht_max);
260     }
261 
262     (void) krb5_cc_close(test_context, ccache);
263 
264     krb5_free_context(test_context);
265 
266     exit(errors);
267   }
268 
269 
270 static krb5_error_code
get_server_key(krb5_context context,krb5_principal server,krb5_enctype enctype,krb5_keyblock ** key)271 get_server_key(krb5_context context, krb5_principal server,
272 	       krb5_enctype enctype, krb5_keyblock **key)
273 {
274     krb5_error_code retval;
275     krb5_encrypt_block eblock;
276     char * string;
277     krb5_data salt;
278     krb5_data pwd;
279 
280     *key = NULL;
281 
282     if ((retval = krb5_principal2salt(context, server, &salt)))
283 	return retval;
284 
285     if ((retval = krb5_unparse_name(context, server, &string)))
286 	goto cleanup_salt;
287 
288     pwd.data = string;
289     pwd.length = strlen(string);
290 
291     if ((*key = (krb5_keyblock *)malloc(sizeof(krb5_keyblock)))) {
292     	krb5_use_enctype(context, &eblock, enctype);
293 	retval = krb5_string_to_key(context, &eblock, *key, &pwd, &salt);
294 	if (retval) {
295 	    free(*key);
296 	    *key = NULL;
297 	}
298     } else
299         retval = ENOMEM;
300 
301     free(string);
302 
303 cleanup_salt:
304     free(salt.data);
305     return retval;
306 }
307 
308 int
verify_cs_pair(krb5_context context,char * p_client_str,krb5_principal p_client,char * service,char * hostname,int p_num,int c_depth,int s_depth,krb5_ccache ccache)309 verify_cs_pair(krb5_context context, char *p_client_str,
310 	       krb5_principal p_client, char *service, char *hostname,
311 	       int p_num, int c_depth, int s_depth, krb5_ccache ccache)
312 {
313     krb5_error_code 	  retval;
314     krb5_creds 	 	  creds;
315     krb5_creds 		* credsp = NULL;
316     krb5_ticket 	* ticket = NULL;
317     krb5_keyblock 	* keyblock = NULL;
318     krb5_auth_context 	  auth_context = NULL;
319     krb5_data		  request_data = empty_data();
320     char		* sname;
321     float		  dt;
322 
323     if (brief)
324       fprintf(stderr, "\tprinc (%d) client (%d) for server (%d)\n",
325 	      p_num, c_depth, s_depth);
326     else
327       fprintf(stderr, "\tclient %s for server %s\n", p_client_str,
328 	      service);
329 
330     /* Initialize variables */
331     memset(&creds, 0, sizeof(creds));
332 
333     /* Do client side */
334     if (asprintf(&sname, "%s@%s", service, hostname) >= 0) {
335 	retval = krb5_parse_name(context, sname, &creds.server);
336 	free(sname);
337     }
338     else
339 	retval = ENOMEM;
340     if (retval)
341 	return(retval);
342 
343     /* obtain ticket & session key */
344     if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
345 	com_err(prog, retval, "while getting client princ for %s", hostname);
346 	return retval;
347     }
348 
349     if ((retval = krb5_get_credentials(context, 0,
350                                       ccache, &creds, &credsp))) {
351 	com_err(prog, retval, "while getting creds for %s", hostname);
352 	return retval;
353     }
354 
355     if (do_timer)
356 	swatch_on();
357 
358     if ((retval = krb5_mk_req_extended(context, &auth_context, 0, NULL,
359 			            credsp, &request_data))) {
360 	com_err(prog, retval, "while preparing AP_REQ for %s", hostname);
361 	goto cleanup;
362     }
363 
364     krb5_auth_con_free(context, auth_context);
365     auth_context = NULL;
366 
367     /* Do server side now */
368     if ((retval = get_server_key(context, credsp->server,
369 				credsp->keyblock.enctype, &keyblock))) {
370 	com_err(prog, retval, "while getting server key for %s", hostname);
371 	goto cleanup;
372     }
373 
374     if (krb5_auth_con_init(context, &auth_context)) {
375 	com_err(prog, retval, "while creating auth_context for %s", hostname);
376 	goto cleanup;
377     }
378 
379     if (krb5_auth_con_setuseruserkey(context, auth_context, keyblock)) {
380 	com_err(prog, retval, "while setting auth_context key %s", hostname);
381 	goto cleanup;
382     }
383 
384     if ((retval = krb5_rd_req(context, &auth_context, &request_data,
385 			     NULL /* server */, 0, NULL, &ticket))) {
386 	com_err(prog, retval, "while decoding AP_REQ for %s", hostname);
387 	goto cleanup;
388     }
389 
390     if (do_timer) {
391 	dt = swatch_eltime();
392 	tgs_req_times.ht_cumulative += dt;
393 	tgs_req_times.ht_observations++;
394 	if (dt > tgs_req_times.ht_max)
395 	    tgs_req_times.ht_max = dt;
396 	if (dt < tgs_req_times.ht_min)
397 	    tgs_req_times.ht_min = dt;
398     }
399 
400     if (!(krb5_principal_compare(context,ticket->enc_part2->client,p_client))){
401     	char *returned_client;
402         if ((retval = krb5_unparse_name(context, ticket->enc_part2->client,
403 			       	       &returned_client)))
404 	    com_err (prog, retval,
405 		     "Client not as expected, but cannot unparse client name");
406       	else
407 	    com_err (prog, 0, "Client not as expected (%s).", returned_client);
408 	retval = KRB5_PRINC_NOMATCH;
409       	free(returned_client);
410     } else {
411 	retval = 0;
412     }
413 
414 cleanup:
415     krb5_free_cred_contents(context, &creds);
416     krb5_free_ticket(context, ticket);
417     krb5_auth_con_free(context, auth_context);
418     krb5_free_keyblock(context, keyblock);
419     krb5_free_data_contents(context, &request_data);
420     krb5_free_creds(context, credsp);
421 
422     return retval;
423 }
424 
425 int
get_tgt(krb5_context context,char * p_client_str,krb5_principal * p_client,krb5_ccache ccache)426 get_tgt(krb5_context context, char *p_client_str, krb5_principal *p_client,
427 	krb5_ccache ccache)
428 {
429     long lifetime = KRB5_DEFAULT_LIFE;	/* -l option */
430     krb5_error_code code;
431     krb5_creds my_creds;
432     krb5_timestamp start;
433     float dt;
434     krb5_get_init_creds_opt *options;
435 
436     if (!brief)
437       fprintf(stderr, "\tgetting TGT for %s\n", p_client_str);
438 
439     if ((code = krb5_timeofday(context, &start))) {
440 	com_err(prog, code, "while getting time of day");
441 	return(-1);
442     }
443 
444     memset(&my_creds, 0, sizeof(my_creds));
445 
446     if ((code = krb5_parse_name (context, p_client_str, p_client))) {
447 	com_err (prog, code, "when parsing name %s", p_client_str);
448 	return(-1);
449     }
450 
451     code = krb5_cc_initialize (context, ccache, *p_client);
452     if (code != 0) {
453 	com_err (prog, code, "when initializing cache");
454 	return(-1);
455     }
456 
457     if (do_timer)
458 	swatch_on();
459 
460     code = krb5_get_init_creds_opt_alloc(context, &options);
461     if (code != 0) {
462 	com_err(prog, code, "when allocating init cred options");
463 	return(-1);
464     }
465 
466     krb5_get_init_creds_opt_set_tkt_life(options, lifetime);
467 
468     code = krb5_get_init_creds_opt_set_out_ccache(context, options, ccache);
469     if (code != 0) {
470 	com_err(prog, code, "when setting init cred output ccache");
471 	return(-1);
472     }
473 
474     code = krb5_get_init_creds_password(context, &my_creds, *p_client,
475 					p_client_str, NULL, NULL, 0, NULL,
476 					options);
477     if (do_timer) {
478 	dt = swatch_eltime();
479 	in_tkt_times.ht_cumulative += dt;
480 	in_tkt_times.ht_observations++;
481 	if (dt > in_tkt_times.ht_max)
482 	    in_tkt_times.ht_max = dt;
483 	if (dt < in_tkt_times.ht_min)
484 	    in_tkt_times.ht_min = dt;
485     }
486     krb5_get_init_creds_opt_free(context, options);
487     krb5_free_cred_contents(context, &my_creds);
488     if (code != 0) {
489 	com_err (prog, code, "while getting initial credentials");
490 	return(-1);
491       }
492 
493     return(0);
494 }
495