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