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