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