1 /*
2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #define HC_DEPRECATED_CRYPTO
35
36 #include "kuser_locl.h"
37
38 #include <kdigest-commands.h>
39 #include <hex.h>
40 #include <base64.h>
41 #include <heimntlm.h>
42 #include "crypto-headers.h"
43
44 static int version_flag = 0;
45 static int help_flag = 0;
46 static char *ccache_string;
47 static krb5_ccache id;
48
49 static struct getargs args[] = {
50 {"ccache", 0, arg_string, &ccache_string, "credential cache", NULL },
51 {"version", 0, arg_flag, &version_flag, "print version", NULL },
52 {"help", 0, arg_flag, &help_flag, NULL, NULL }
53 };
54
55 static void
usage(int ret)56 usage (int ret)
57 {
58 arg_printusage (args, sizeof(args)/sizeof(*args),
59 NULL, "");
60 exit (ret);
61 }
62
63 static krb5_context context;
64
65 int
digest_probe(struct digest_probe_options * opt,int argc,char ** argv)66 digest_probe(struct digest_probe_options *opt,
67 int argc, char ** argv)
68 {
69 krb5_error_code ret;
70 krb5_realm realm;
71 unsigned flags;
72
73 realm = opt->realm_string;
74
75 if (realm == NULL)
76 errx(1, "realm missing");
77
78 ret = krb5_digest_probe(context, realm, id, &flags);
79 if (ret)
80 krb5_err(context, 1, ret, "digest_probe");
81
82 printf("flags: %u\n", flags);
83
84 return 0;
85 }
86
87 int
digest_server_init(struct digest_server_init_options * opt,int argc,char ** argv)88 digest_server_init(struct digest_server_init_options *opt,
89 int argc, char ** argv)
90 {
91 krb5_error_code ret;
92 krb5_digest digest;
93
94 ret = krb5_digest_alloc(context, &digest);
95 if (ret)
96 krb5_err(context, 1, ret, "digest_alloc");
97
98 ret = krb5_digest_set_type(context, digest, opt->type_string);
99 if (ret)
100 krb5_err(context, 1, ret, "krb5_digest_set_type");
101
102 if (opt->cb_type_string && opt->cb_value_string) {
103 ret = krb5_digest_set_server_cb(context, digest,
104 opt->cb_type_string,
105 opt->cb_value_string);
106 if (ret)
107 krb5_err(context, 1, ret, "krb5_digest_set_server_cb");
108 }
109 ret = krb5_digest_init_request(context,
110 digest,
111 opt->kerberos_realm_string,
112 id);
113 if (ret)
114 krb5_err(context, 1, ret, "krb5_digest_init_request");
115
116 printf("type=%s\n", opt->type_string);
117 printf("server-nonce=%s\n",
118 krb5_digest_get_server_nonce(context, digest));
119 {
120 const char *s = krb5_digest_get_identifier(context, digest);
121 if (s)
122 printf("identifier=%s\n", s);
123 }
124 printf("opaque=%s\n", krb5_digest_get_opaque(context, digest));
125
126 krb5_digest_free(digest);
127
128 return 0;
129 }
130
131 int
digest_server_request(struct digest_server_request_options * opt,int argc,char ** argv)132 digest_server_request(struct digest_server_request_options *opt,
133 int argc, char **argv)
134 {
135 krb5_error_code ret;
136 krb5_digest digest;
137 const char *status, *rsp;
138 krb5_data session_key;
139
140 if (opt->server_nonce_string == NULL)
141 errx(1, "server nonce missing");
142 if (opt->type_string == NULL)
143 errx(1, "type missing");
144 if (opt->opaque_string == NULL)
145 errx(1, "opaque missing");
146 if (opt->client_response_string == NULL)
147 errx(1, "client response missing");
148
149 ret = krb5_digest_alloc(context, &digest);
150 if (ret)
151 krb5_err(context, 1, ret, "digest_alloc");
152
153 if (strcasecmp(opt->type_string, "CHAP") == 0) {
154 if (opt->server_identifier_string == NULL)
155 errx(1, "server identifier missing");
156
157 ret = krb5_digest_set_identifier(context, digest,
158 opt->server_identifier_string);
159 if (ret)
160 krb5_err(context, 1, ret, "krb5_digest_set_type");
161 }
162
163 ret = krb5_digest_set_type(context, digest, opt->type_string);
164 if (ret)
165 krb5_err(context, 1, ret, "krb5_digest_set_type");
166
167 ret = krb5_digest_set_username(context, digest, opt->username_string);
168 if (ret)
169 krb5_err(context, 1, ret, "krb5_digest_set_username");
170
171 ret = krb5_digest_set_server_nonce(context, digest,
172 opt->server_nonce_string);
173 if (ret)
174 krb5_err(context, 1, ret, "krb5_digest_set_server_nonce");
175
176 if(opt->client_nonce_string) {
177 ret = krb5_digest_set_client_nonce(context, digest,
178 opt->client_nonce_string);
179 if (ret)
180 krb5_err(context, 1, ret, "krb5_digest_set_client_nonce");
181 }
182
183
184 ret = krb5_digest_set_opaque(context, digest, opt->opaque_string);
185 if (ret)
186 krb5_err(context, 1, ret, "krb5_digest_set_opaque");
187
188 ret = krb5_digest_set_responseData(context, digest,
189 opt->client_response_string);
190 if (ret)
191 krb5_err(context, 1, ret, "krb5_digest_set_responseData");
192
193 ret = krb5_digest_request(context, digest,
194 opt->kerberos_realm_string, id);
195 if (ret)
196 krb5_err(context, 1, ret, "krb5_digest_request");
197
198 status = krb5_digest_rep_get_status(context, digest) ? "ok" : "failed";
199 rsp = krb5_digest_get_rsp(context, digest);
200
201 printf("status=%s\n", status);
202 if (rsp)
203 printf("rsp=%s\n", rsp);
204 printf("tickets=no\n");
205
206 ret = krb5_digest_get_session_key(context, digest, &session_key);
207 if (ret)
208 krb5_err(context, 1, ret, "krb5_digest_get_session_key");
209
210 if (session_key.length) {
211 char *key;
212 hex_encode(session_key.data, session_key.length, &key);
213 if (key == NULL)
214 krb5_errx(context, 1, "hex_encode");
215 krb5_data_free(&session_key);
216 printf("session-key=%s\n", key);
217 free(key);
218 }
219
220 krb5_digest_free(digest);
221
222 return 0;
223 }
224
225 static void
client_chap(const void * server_nonce,size_t snoncelen,unsigned char server_identifier,const char * password)226 client_chap(const void *server_nonce, size_t snoncelen,
227 unsigned char server_identifier,
228 const char *password)
229 {
230 EVP_MD_CTX *ctx;
231 unsigned char md[MD5_DIGEST_LENGTH];
232 char *h;
233
234 ctx = EVP_MD_CTX_create();
235 EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
236
237 EVP_DigestUpdate(ctx, &server_identifier, 1);
238 EVP_DigestUpdate(ctx, password, strlen(password));
239 EVP_DigestUpdate(ctx, server_nonce, snoncelen);
240 EVP_DigestFinal_ex(ctx, md, NULL);
241
242 EVP_MD_CTX_destroy(ctx);
243
244 hex_encode(md, 16, &h);
245
246 printf("responseData=%s\n", h);
247 free(h);
248 }
249
250 static const unsigned char ms_chap_v2_magic1[39] = {
251 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
252 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
253 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
254 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
255 };
256 static const unsigned char ms_chap_v2_magic2[41] = {
257 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
258 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
259 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
260 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
261 0x6E
262 };
263 static const unsigned char ms_rfc3079_magic1[27] = {
264 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
265 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
266 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
267 };
268
269 static void
client_mschapv2(const void * server_nonce,size_t snoncelen,const void * client_nonce,size_t cnoncelen,const char * username,const char * password)270 client_mschapv2(const void *server_nonce, size_t snoncelen,
271 const void *client_nonce, size_t cnoncelen,
272 const char *username,
273 const char *password)
274 {
275 EVP_MD_CTX *hctx, *ctx;
276 unsigned char md[SHA_DIGEST_LENGTH], challenge[SHA_DIGEST_LENGTH];
277 unsigned char hmd[MD4_DIGEST_LENGTH];
278 struct ntlm_buf answer;
279 int i, len, ret;
280 char *h;
281
282 ctx = EVP_MD_CTX_create();
283 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
284
285 EVP_DigestUpdate(ctx, client_nonce, cnoncelen);
286 EVP_DigestUpdate(ctx, server_nonce, snoncelen);
287 EVP_DigestUpdate(ctx, username, strlen(username));
288 EVP_DigestFinal_ex(ctx, md, NULL);
289
290
291 hctx = EVP_MD_CTX_create();
292 EVP_DigestInit_ex(hctx, EVP_md4(), NULL);
293 len = strlen(password);
294 for (i = 0; i < len; i++) {
295 EVP_DigestUpdate(hctx, &password[i], 1);
296 EVP_DigestUpdate(hctx, &password[len], 1);
297 }
298 EVP_DigestFinal_ex(hctx, hmd, NULL);
299
300
301 /* ChallengeResponse */
302 ret = heim_ntlm_calculate_ntlm1(hmd, sizeof(hmd), md, &answer);
303 if (ret)
304 errx(1, "heim_ntlm_calculate_ntlm1");
305
306 hex_encode(answer.data, answer.length, &h);
307 printf("responseData=%s\n", h);
308 free(h);
309
310 /* PasswordHash */
311 EVP_DigestInit_ex(hctx, EVP_md4(), NULL);
312 EVP_DigestUpdate(hctx, hmd, sizeof(hmd));
313 EVP_DigestFinal_ex(hctx, hmd, NULL);
314
315
316 /* GenerateAuthenticatorResponse */
317 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
318 EVP_DigestUpdate(ctx, hmd, sizeof(hmd));
319 EVP_DigestUpdate(ctx, answer.data, answer.length);
320 EVP_DigestUpdate(ctx, ms_chap_v2_magic1, sizeof(ms_chap_v2_magic1));
321 EVP_DigestFinal_ex(ctx, md, NULL);
322
323 /* ChallengeHash */
324 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
325 EVP_DigestUpdate(ctx, client_nonce, cnoncelen);
326 EVP_DigestUpdate(ctx, server_nonce, snoncelen);
327 EVP_DigestUpdate(ctx, username, strlen(username));
328 EVP_DigestFinal_ex(ctx, challenge, NULL);
329
330 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
331 EVP_DigestUpdate(ctx, md, sizeof(md));
332 EVP_DigestUpdate(ctx, challenge, 8);
333 EVP_DigestUpdate(ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
334 EVP_DigestFinal_ex(ctx, md, NULL);
335
336 hex_encode(md, sizeof(md), &h);
337 printf("AuthenticatorResponse=%s\n", h);
338 free(h);
339
340 /* get_master, rfc 3079 3.4 */
341 EVP_DigestInit_ex(ctx, EVP_sha1(), NULL);
342 EVP_DigestUpdate(ctx, hmd, sizeof(hmd));
343 EVP_DigestUpdate(ctx, answer.data, answer.length);
344 EVP_DigestUpdate(ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
345 EVP_DigestFinal_ex(ctx, md, NULL);
346
347 free(answer.data);
348
349 hex_encode(md, 16, &h);
350 printf("session-key=%s\n", h);
351 free(h);
352
353 EVP_MD_CTX_destroy(hctx);
354 EVP_MD_CTX_destroy(ctx);
355 }
356
357
358 int
digest_client_request(struct digest_client_request_options * opt,int argc,char ** argv)359 digest_client_request(struct digest_client_request_options *opt,
360 int argc, char **argv)
361 {
362 char *server_nonce, *client_nonce = NULL, server_identifier;
363 ssize_t snoncelen, cnoncelen = 0;
364
365 if (opt->server_nonce_string == NULL)
366 errx(1, "server nonce missing");
367 if (opt->password_string == NULL)
368 errx(1, "password missing");
369
370 if (opt->opaque_string == NULL)
371 errx(1, "opaque missing");
372
373 snoncelen = strlen(opt->server_nonce_string);
374 server_nonce = malloc(snoncelen);
375 if (server_nonce == NULL)
376 errx(1, "server_nonce");
377
378 snoncelen = hex_decode(opt->server_nonce_string, server_nonce, snoncelen);
379 if (snoncelen <= 0)
380 errx(1, "server nonce wrong");
381
382 if (opt->client_nonce_string) {
383 cnoncelen = strlen(opt->client_nonce_string);
384 client_nonce = malloc(cnoncelen);
385 if (client_nonce == NULL)
386 errx(1, "client_nonce");
387
388 cnoncelen = hex_decode(opt->client_nonce_string,
389 client_nonce, cnoncelen);
390 if (cnoncelen <= 0)
391 errx(1, "client nonce wrong");
392 }
393
394 if (opt->server_identifier_string) {
395 int ret;
396
397 ret = hex_decode(opt->server_identifier_string, &server_identifier, 1);
398 if (ret != 1)
399 errx(1, "server identifier wrong length");
400 }
401
402 if (strcasecmp(opt->type_string, "CHAP") == 0) {
403 if (opt->server_identifier_string == NULL)
404 errx(1, "server identifier missing");
405
406 client_chap(server_nonce, snoncelen, server_identifier,
407 opt->password_string);
408
409 } else if (strcasecmp(opt->type_string, "MS-CHAP-V2") == 0) {
410 if (opt->client_nonce_string == NULL)
411 errx(1, "client nonce missing");
412 if (opt->username_string == NULL)
413 errx(1, "client nonce missing");
414
415 client_mschapv2(server_nonce, snoncelen,
416 client_nonce, cnoncelen,
417 opt->username_string,
418 opt->password_string);
419 }
420 if (client_nonce)
421 free(client_nonce);
422 free(server_nonce);
423
424 return 0;
425 }
426
427 #include <heimntlm.h>
428
429 int
ntlm_server_init(struct ntlm_server_init_options * opt,int argc,char ** argv)430 ntlm_server_init(struct ntlm_server_init_options *opt,
431 int argc, char ** argv)
432 {
433 krb5_error_code ret;
434 krb5_ntlm ntlm;
435 struct ntlm_type2 type2;
436 krb5_data challenge, opaque;
437 struct ntlm_buf data;
438 char *s;
439 static char zero2[] = "\x00\x00";
440
441 memset(&type2, 0, sizeof(type2));
442
443 ret = krb5_ntlm_alloc(context, &ntlm);
444 if (ret)
445 krb5_err(context, 1, ret, "krb5_ntlm_alloc");
446
447 ret = krb5_ntlm_init_request(context,
448 ntlm,
449 opt->kerberos_realm_string,
450 id,
451 NTLM_NEG_UNICODE|NTLM_NEG_NTLM,
452 "NUTCRACKER",
453 "L");
454 if (ret)
455 krb5_err(context, 1, ret, "krb5_ntlm_init_request");
456
457 /*
458 *
459 */
460
461 ret = krb5_ntlm_init_get_challange(context, ntlm, &challenge);
462 if (ret)
463 krb5_err(context, 1, ret, "krb5_ntlm_init_get_challange");
464
465 if (challenge.length != sizeof(type2.challenge))
466 krb5_errx(context, 1, "ntlm challenge have wrong length");
467 memcpy(type2.challenge, challenge.data, sizeof(type2.challenge));
468 krb5_data_free(&challenge);
469
470 ret = krb5_ntlm_init_get_flags(context, ntlm, &type2.flags);
471 if (ret)
472 krb5_err(context, 1, ret, "krb5_ntlm_init_get_flags");
473
474 krb5_ntlm_init_get_targetname(context, ntlm, &type2.targetname);
475 type2.targetinfo.data = zero2;
476 type2.targetinfo.length = 2;
477
478 ret = heim_ntlm_encode_type2(&type2, &data);
479 if (ret)
480 krb5_errx(context, 1, "heim_ntlm_encode_type2");
481
482 free(type2.targetname);
483
484 /*
485 *
486 */
487
488 base64_encode(data.data, data.length, &s);
489 free(data.data);
490 printf("type2=%s\n", s);
491 free(s);
492
493 /*
494 *
495 */
496
497 ret = krb5_ntlm_init_get_opaque(context, ntlm, &opaque);
498 if (ret)
499 krb5_err(context, 1, ret, "krb5_ntlm_init_get_opaque");
500
501 base64_encode(opaque.data, opaque.length, &s);
502 krb5_data_free(&opaque);
503 printf("opaque=%s\n", s);
504 free(s);
505
506 /*
507 *
508 */
509
510 krb5_ntlm_free(context, ntlm);
511
512 return 0;
513 }
514
515
516 /*
517 *
518 */
519
520 int
help(void * opt,int argc,char ** argv)521 help(void *opt, int argc, char **argv)
522 {
523 sl_slc_help(commands, argc, argv);
524 return 0;
525 }
526
527 int
main(int argc,char ** argv)528 main(int argc, char **argv)
529 {
530 krb5_error_code ret;
531 int optidx = 0;
532
533 setprogname(argv[0]);
534
535 ret = krb5_init_context (&context);
536 if (ret == KRB5_CONFIG_BADFORMAT)
537 errx (1, "krb5_init_context failed to parse configuration file");
538 else if (ret)
539 errx(1, "krb5_init_context failed: %d", ret);
540
541 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
542 usage(1);
543
544 if (help_flag)
545 usage (0);
546
547 if(version_flag){
548 print_version(NULL);
549 exit(0);
550 }
551
552 argc -= optidx;
553 argv += optidx;
554
555 if (argc == 0) {
556 help(NULL, argc, argv);
557 return 1;
558 }
559
560 if (ccache_string) {
561 ret = krb5_cc_resolve(context, ccache_string, &id);
562 if (ret)
563 krb5_err(context, 1, ret, "krb5_cc_resolve");
564 }
565
566 ret = sl_command (commands, argc, argv);
567 if (ret == -1) {
568 help(NULL, argc, argv);
569 return 1;
570 }
571 return ret;
572 }
573