xref: /freebsd/crypto/heimdal/kuser/kdigest.c (revision 2a58b312b62f908ec92311d1bd8536dbaeb8e55b)
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
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
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
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
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
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
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
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
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
521 help(void *opt, int argc, char **argv)
522 {
523     sl_slc_help(commands, argc, argv);
524     return 0;
525 }
526 
527 int
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