xref: /freebsd/crypto/heimdal/kuser/kdigest.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
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 #include "kuser_locl.h"
35 RCSID("$Id: kdigest.c 22158 2007-12-04 20:04:01Z lha $");
36 #include <kdigest-commands.h>
37 #include <hex.h>
38 #include <base64.h>
39 #include <heimntlm.h>
40 #include "crypto-headers.h"
41 
42 static int version_flag = 0;
43 static int help_flag	= 0;
44 static char *ccache_string;
45 static krb5_ccache id;
46 
47 static struct getargs args[] = {
48     {"ccache",	0,	arg_string,	&ccache_string, "credential cache", NULL },
49     {"version",	0,	arg_flag,	&version_flag, "print version", NULL },
50     {"help",	0,	arg_flag,	&help_flag,  NULL, NULL }
51 };
52 
53 static void
54 usage (int ret)
55 {
56     arg_printusage (args, sizeof(args)/sizeof(*args),
57 		    NULL, "");
58     exit (ret);
59 }
60 
61 static krb5_context context;
62 
63 int
64 digest_probe(struct digest_probe_options *opt,
65 	     int argc, char ** argv)
66 {
67     krb5_error_code ret;
68     krb5_realm realm;
69     unsigned flags;
70 
71     realm = opt->realm_string;
72 
73     if (realm == NULL)
74 	errx(1, "realm missing");
75 
76     ret = krb5_digest_probe(context, realm, id, &flags);
77     if (ret)
78 	krb5_err(context, 1, ret, "digest_probe");
79 
80     printf("flags: %u\n", flags);
81 
82     return 0;
83 }
84 
85 int
86 digest_server_init(struct digest_server_init_options *opt,
87 		   int argc, char ** argv)
88 {
89     krb5_error_code ret;
90     krb5_digest digest;
91 
92     ret = krb5_digest_alloc(context, &digest);
93     if (ret)
94 	krb5_err(context, 1, ret, "digest_alloc");
95 
96     ret = krb5_digest_set_type(context, digest, opt->type_string);
97     if (ret)
98 	krb5_err(context, 1, ret, "krb5_digest_set_type");
99 
100     if (opt->cb_type_string && opt->cb_value_string) {
101 	ret = krb5_digest_set_server_cb(context, digest,
102 					opt->cb_type_string,
103 					opt->cb_value_string);
104 	if (ret)
105 	    krb5_err(context, 1, ret, "krb5_digest_set_server_cb");
106     }
107     ret = krb5_digest_init_request(context,
108 				   digest,
109 				   opt->kerberos_realm_string,
110 				   id);
111     if (ret)
112 	krb5_err(context, 1, ret, "krb5_digest_init_request");
113 
114     printf("type=%s\n", opt->type_string);
115     printf("server-nonce=%s\n",
116 	   krb5_digest_get_server_nonce(context, digest));
117     {
118 	const char *s = krb5_digest_get_identifier(context, digest);
119 	if (s)
120 	    printf("identifier=%s\n", s);
121     }
122     printf("opaque=%s\n", krb5_digest_get_opaque(context, digest));
123 
124     return 0;
125 }
126 
127 int
128 digest_server_request(struct digest_server_request_options *opt,
129 		      int argc, char **argv)
130 {
131     krb5_error_code ret;
132     krb5_digest digest;
133     const char *status, *rsp;
134     krb5_data session_key;
135 
136     if (opt->server_nonce_string == NULL)
137 	errx(1, "server nonce missing");
138     if (opt->type_string == NULL)
139 	errx(1, "type missing");
140     if (opt->opaque_string == NULL)
141 	errx(1, "opaque missing");
142     if (opt->client_response_string == NULL)
143 	errx(1, "client response missing");
144 
145     ret = krb5_digest_alloc(context, &digest);
146     if (ret)
147 	krb5_err(context, 1, ret, "digest_alloc");
148 
149     if (strcasecmp(opt->type_string, "CHAP") == 0) {
150 	if (opt->server_identifier_string == NULL)
151 	    errx(1, "server identifier missing");
152 
153 	ret = krb5_digest_set_identifier(context, digest,
154 					 opt->server_identifier_string);
155 	if (ret)
156 	    krb5_err(context, 1, ret, "krb5_digest_set_type");
157     }
158 
159     ret = krb5_digest_set_type(context, digest, opt->type_string);
160     if (ret)
161 	krb5_err(context, 1, ret, "krb5_digest_set_type");
162 
163     ret = krb5_digest_set_username(context, digest, opt->username_string);
164     if (ret)
165 	krb5_err(context, 1, ret, "krb5_digest_set_username");
166 
167     ret = krb5_digest_set_server_nonce(context, digest,
168 				       opt->server_nonce_string);
169     if (ret)
170 	krb5_err(context, 1, ret, "krb5_digest_set_server_nonce");
171 
172     if(opt->client_nonce_string) {
173 	ret = krb5_digest_set_client_nonce(context, digest,
174 					   opt->client_nonce_string);
175 	if (ret)
176 	    krb5_err(context, 1, ret, "krb5_digest_set_client_nonce");
177     }
178 
179 
180     ret = krb5_digest_set_opaque(context, digest, opt->opaque_string);
181     if (ret)
182 	krb5_err(context, 1, ret, "krb5_digest_set_opaque");
183 
184     ret = krb5_digest_set_responseData(context, digest,
185 				       opt->client_response_string);
186     if (ret)
187 	krb5_err(context, 1, ret, "krb5_digest_set_responseData");
188 
189     ret = krb5_digest_request(context, digest,
190 			      opt->kerberos_realm_string, id);
191     if (ret)
192 	krb5_err(context, 1, ret, "krb5_digest_request");
193 
194     status = krb5_digest_rep_get_status(context, digest) ? "ok" : "failed";
195     rsp = krb5_digest_get_rsp(context, digest);
196 
197     printf("status=%s\n", status);
198     if (rsp)
199 	printf("rsp=%s\n", rsp);
200     printf("tickets=no\n");
201 
202     ret = krb5_digest_get_session_key(context, digest, &session_key);
203     if (ret)
204 	krb5_err(context, 1, ret, "krb5_digest_get_session_key");
205 
206     if (session_key.length) {
207 	char *key;
208 	hex_encode(session_key.data, session_key.length, &key);
209 	if (key == NULL)
210 	    krb5_errx(context, 1, "hex_encode");
211 	krb5_data_free(&session_key);
212 	printf("session-key=%s\n", key);
213 	free(key);
214     }
215 
216     return 0;
217 }
218 
219 static void
220 client_chap(const void *server_nonce, size_t snoncelen,
221 	    unsigned char server_identifier,
222 	    const char *password)
223 {
224     MD5_CTX ctx;
225     unsigned char md[MD5_DIGEST_LENGTH];
226     char *h;
227 
228     MD5_Init(&ctx);
229     MD5_Update(&ctx, &server_identifier, 1);
230     MD5_Update(&ctx, password, strlen(password));
231     MD5_Update(&ctx, server_nonce, snoncelen);
232     MD5_Final(md, &ctx);
233 
234     hex_encode(md, 16, &h);
235 
236     printf("responseData=%s\n", h);
237     free(h);
238 }
239 
240 static const unsigned char ms_chap_v2_magic1[39] = {
241     0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
242     0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
243     0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
244     0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
245 };
246 static const unsigned char ms_chap_v2_magic2[41] = {
247     0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
248     0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
249     0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
250     0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
251     0x6E
252 };
253 static const unsigned char ms_rfc3079_magic1[27] = {
254     0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
255     0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
256     0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
257 };
258 
259 static void
260 client_mschapv2(const void *server_nonce, size_t snoncelen,
261 		const void *client_nonce, size_t cnoncelen,
262 		const char *username,
263 		const char *password)
264 {
265     SHA_CTX ctx;
266     MD4_CTX hctx;
267     unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
268     unsigned char hmd[MD4_DIGEST_LENGTH];
269     struct ntlm_buf answer;
270     int i, len, ret;
271     char *h;
272 
273     SHA1_Init(&ctx);
274     SHA1_Update(&ctx, client_nonce, cnoncelen);
275     SHA1_Update(&ctx, server_nonce, snoncelen);
276     SHA1_Update(&ctx, username, strlen(username));
277     SHA1_Final(md, &ctx);
278 
279     MD4_Init(&hctx);
280     len = strlen(password);
281     for (i = 0; i < len; i++) {
282 	MD4_Update(&hctx, &password[i], 1);
283 	MD4_Update(&hctx, &password[len], 1);
284     }
285     MD4_Final(hmd, &hctx);
286 
287     /* ChallengeResponse */
288     ret = heim_ntlm_calculate_ntlm1(hmd, sizeof(hmd), md, &answer);
289     if (ret)
290 	errx(1, "heim_ntlm_calculate_ntlm1");
291 
292     hex_encode(answer.data, answer.length, &h);
293     printf("responseData=%s\n", h);
294     free(h);
295 
296     /* PasswordHash */
297     MD4_Init(&hctx);
298     MD4_Update(&hctx, hmd, sizeof(hmd));
299     MD4_Final(hmd, &hctx);
300 
301     /* GenerateAuthenticatorResponse */
302     SHA1_Init(&ctx);
303     SHA1_Update(&ctx, hmd, sizeof(hmd));
304     SHA1_Update(&ctx, answer.data, answer.length);
305     SHA1_Update(&ctx, ms_chap_v2_magic1, sizeof(ms_chap_v2_magic1));
306     SHA1_Final(md, &ctx);
307 
308     /* ChallengeHash */
309     SHA1_Init(&ctx);
310     SHA1_Update(&ctx, client_nonce, cnoncelen);
311     SHA1_Update(&ctx, server_nonce, snoncelen);
312     SHA1_Update(&ctx, username, strlen(username));
313     SHA1_Final(challange, &ctx);
314 
315     SHA1_Init(&ctx);
316     SHA1_Update(&ctx, md, sizeof(md));
317     SHA1_Update(&ctx, challange, 8);
318     SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
319     SHA1_Final(md, &ctx);
320 
321     hex_encode(md, sizeof(md), &h);
322     printf("AuthenticatorResponse=%s\n", h);
323     free(h);
324 
325     /* get_master, rfc 3079 3.4 */
326     SHA1_Init(&ctx);
327     SHA1_Update(&ctx, hmd, sizeof(hmd));
328     SHA1_Update(&ctx, answer.data, answer.length);
329     SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
330     SHA1_Final(md, &ctx);
331 
332     free(answer.data);
333 
334     hex_encode(md, 16, &h);
335     printf("session-key=%s\n", h);
336     free(h);
337 }
338 
339 
340 int
341 digest_client_request(struct digest_client_request_options *opt,
342 		      int argc, char **argv)
343 {
344     char *server_nonce, *client_nonce = NULL, server_identifier;
345     ssize_t snoncelen, cnoncelen = 0;
346 
347     if (opt->server_nonce_string == NULL)
348 	errx(1, "server nonce missing");
349     if (opt->password_string == NULL)
350 	errx(1, "password missing");
351 
352     if (opt->opaque_string == NULL)
353 	errx(1, "opaque missing");
354 
355     snoncelen = strlen(opt->server_nonce_string);
356     server_nonce = malloc(snoncelen);
357     if (server_nonce == NULL)
358 	errx(1, "server_nonce");
359 
360     snoncelen = hex_decode(opt->server_nonce_string, server_nonce, snoncelen);
361     if (snoncelen <= 0)
362 	errx(1, "server nonce wrong");
363 
364     if (opt->client_nonce_string) {
365 	cnoncelen = strlen(opt->client_nonce_string);
366 	client_nonce = malloc(cnoncelen);
367 	if (client_nonce == NULL)
368 	    errx(1, "client_nonce");
369 
370 	cnoncelen = hex_decode(opt->client_nonce_string,
371 			       client_nonce, cnoncelen);
372 	if (cnoncelen <= 0)
373 	    errx(1, "client nonce wrong");
374     }
375 
376     if (opt->server_identifier_string) {
377 	int ret;
378 
379 	ret = hex_decode(opt->server_identifier_string, &server_identifier, 1);
380 	if (ret != 1)
381 	    errx(1, "server identifier wrong length");
382     }
383 
384     if (strcasecmp(opt->type_string, "CHAP") == 0) {
385 	if (opt->server_identifier_string == NULL)
386 	    errx(1, "server identifier missing");
387 
388 	client_chap(server_nonce, snoncelen, server_identifier,
389 		    opt->password_string);
390 
391     } else if (strcasecmp(opt->type_string, "MS-CHAP-V2") == 0) {
392 	if (opt->client_nonce_string == NULL)
393 	    errx(1, "client nonce missing");
394 	if (opt->username_string == NULL)
395 	    errx(1, "client nonce missing");
396 
397 	client_mschapv2(server_nonce, snoncelen,
398 			client_nonce, cnoncelen,
399 			opt->username_string,
400 			opt->password_string);
401     }
402 
403 
404     return 0;
405 }
406 
407 #include <heimntlm.h>
408 
409 int
410 ntlm_server_init(struct ntlm_server_init_options *opt,
411 		 int argc, char ** argv)
412 {
413     krb5_error_code ret;
414     krb5_ntlm ntlm;
415     struct ntlm_type2 type2;
416     krb5_data challange, opaque;
417     struct ntlm_buf data;
418     char *s;
419 
420     memset(&type2, 0, sizeof(type2));
421 
422     ret = krb5_ntlm_alloc(context, &ntlm);
423     if (ret)
424 	krb5_err(context, 1, ret, "krb5_ntlm_alloc");
425 
426     ret = krb5_ntlm_init_request(context,
427 				 ntlm,
428 				 opt->kerberos_realm_string,
429 				 id,
430 				 NTLM_NEG_UNICODE|NTLM_NEG_NTLM,
431 				 "NUTCRACKER",
432 				 "L");
433     if (ret)
434 	krb5_err(context, 1, ret, "krb5_ntlm_init_request");
435 
436     /*
437      *
438      */
439 
440     ret = krb5_ntlm_init_get_challange(context, ntlm, &challange);
441     if (ret)
442 	krb5_err(context, 1, ret, "krb5_ntlm_init_get_challange");
443 
444     if (challange.length != sizeof(type2.challange))
445 	krb5_errx(context, 1, "ntlm challange have wrong length");
446     memcpy(type2.challange, challange.data, sizeof(type2.challange));
447     krb5_data_free(&challange);
448 
449     ret = krb5_ntlm_init_get_flags(context, ntlm, &type2.flags);
450     if (ret)
451 	krb5_err(context, 1, ret, "krb5_ntlm_init_get_flags");
452 
453     krb5_ntlm_init_get_targetname(context, ntlm, &type2.targetname);
454     type2.targetinfo.data = "\x00\x00";
455     type2.targetinfo.length = 2;
456 
457     ret = heim_ntlm_encode_type2(&type2, &data);
458     if (ret)
459 	krb5_errx(context, 1, "heim_ntlm_encode_type2");
460 
461     free(type2.targetname);
462 
463     /*
464      *
465      */
466 
467     base64_encode(data.data, data.length, &s);
468     free(data.data);
469     printf("type2=%s\n", s);
470     free(s);
471 
472     /*
473      *
474      */
475 
476     ret = krb5_ntlm_init_get_opaque(context, ntlm, &opaque);
477     if (ret)
478 	krb5_err(context, 1, ret, "krb5_ntlm_init_get_opaque");
479 
480     base64_encode(opaque.data, opaque.length, &s);
481     krb5_data_free(&opaque);
482     printf("opaque=%s\n", s);
483     free(s);
484 
485     /*
486      *
487      */
488 
489     krb5_ntlm_free(context, ntlm);
490 
491     return 0;
492 }
493 
494 
495 /*
496  *
497  */
498 
499 int
500 help(void *opt, int argc, char **argv)
501 {
502     sl_slc_help(commands, argc, argv);
503     return 0;
504 }
505 
506 int
507 main(int argc, char **argv)
508 {
509     krb5_error_code ret;
510     int optidx = 0;
511 
512     setprogname(argv[0]);
513 
514     ret = krb5_init_context (&context);
515     if (ret == KRB5_CONFIG_BADFORMAT)
516 	errx (1, "krb5_init_context failed to parse configuration file");
517     else if (ret)
518 	errx(1, "krb5_init_context failed: %d", ret);
519 
520     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
521 	usage(1);
522 
523     if (help_flag)
524 	usage (0);
525 
526     if(version_flag){
527 	print_version(NULL);
528 	exit(0);
529     }
530 
531     argc -= optidx;
532     argv += optidx;
533 
534     if (argc == 0) {
535 	help(NULL, argc, argv);
536 	return 1;
537     }
538 
539     if (ccache_string) {
540 	ret = krb5_cc_resolve(context, ccache_string, &id);
541 	if (ret)
542 	    krb5_err(context, 1, ret, "krb5_cc_resolve");
543     }
544 
545     ret = sl_command (commands, argc, argv);
546     if (ret == -1) {
547 	help(NULL, argc, argv);
548 	return 1;
549     }
550     return ret;
551 }
552