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
44static int version_flag = 0;
45static int help_flag	= 0;
46static char *ccache_string;
47static krb5_ccache id;
48
49static 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
55static void
56usage (int ret)
57{
58    arg_printusage (args, sizeof(args)/sizeof(*args),
59		    NULL, "");
60    exit (ret);
61}
62
63static krb5_context context;
64
65int
66digest_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
87int
88digest_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
131int
132digest_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
225static void
226client_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
250static 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};
256static 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};
263static 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
269static void
270client_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
358int
359digest_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
429int
430ntlm_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_challenge");
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
520int
521help(void *opt, int argc, char **argv)
522{
523    sl_slc_help(commands, argc, argv);
524    return 0;
525}
526
527int
528main(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