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"
35RCSID("$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
42static int version_flag = 0;
43static int help_flag	= 0;
44static char *ccache_string;
45static krb5_ccache id;
46
47static 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
53static void
54usage (int ret)
55{
56    arg_printusage (args, sizeof(args)/sizeof(*args),
57		    NULL, "");
58    exit (ret);
59}
60
61static krb5_context context;
62
63int
64digest_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
85int
86digest_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
127int
128digest_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
219static void
220client_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
240static 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};
246static 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};
253static 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
259static void
260client_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
340int
341digest_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
409int
410ntlm_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
499int
500help(void *opt, int argc, char **argv)
501{
502    sl_slc_help(commands, argc, argv);
503    return 0;
504}
505
506int
507main(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