/ kuser / kdigest.c
kdigest.c
  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_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  
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  }