xref: /freebsd/crypto/heimdal/lib/krb5/digest.c (revision aa0a1e58)
1 /*
2  * Copyright (c) 2006 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 "krb5_locl.h"
35 RCSID("$Id: digest.c 22156 2007-12-04 20:02:49Z lha $");
36 #include "digest_asn1.h"
37 
38 struct krb5_digest_data {
39     char *cbtype;
40     char *cbbinding;
41 
42     DigestInit init;
43     DigestInitReply initReply;
44     DigestRequest request;
45     DigestResponse response;
46 };
47 
48 krb5_error_code
49 krb5_digest_alloc(krb5_context context, krb5_digest *digest)
50 {
51     krb5_digest d;
52 
53     d = calloc(1, sizeof(*d));
54     if (d == NULL) {
55 	*digest = NULL;
56 	krb5_set_error_string(context, "out of memory");
57 	return ENOMEM;
58     }
59     *digest = d;
60 
61     return 0;
62 }
63 
64 void
65 krb5_digest_free(krb5_digest digest)
66 {
67     if (digest == NULL)
68 	return;
69     free_DigestInit(&digest->init);
70     free_DigestInitReply(&digest->initReply);
71     free_DigestRequest(&digest->request);
72     free_DigestResponse(&digest->response);
73     memset(digest, 0, sizeof(*digest));
74     free(digest);
75     return;
76 }
77 
78 krb5_error_code
79 krb5_digest_set_server_cb(krb5_context context,
80 			  krb5_digest digest,
81 			  const char *type,
82 			  const char *binding)
83 {
84     if (digest->init.channel) {
85 	krb5_set_error_string(context, "server channel binding already set");
86 	return EINVAL;
87     }
88     digest->init.channel = calloc(1, sizeof(*digest->init.channel));
89     if (digest->init.channel == NULL)
90 	goto error;
91 
92     digest->init.channel->cb_type = strdup(type);
93     if (digest->init.channel->cb_type == NULL)
94 	goto error;
95 
96     digest->init.channel->cb_binding = strdup(binding);
97     if (digest->init.channel->cb_binding == NULL)
98 	goto error;
99     return 0;
100 error:
101     if (digest->init.channel) {
102 	free(digest->init.channel->cb_type);
103 	free(digest->init.channel->cb_binding);
104 	free(digest->init.channel);
105 	digest->init.channel = NULL;
106     }
107     krb5_set_error_string(context, "out of memory");
108     return ENOMEM;
109 }
110 
111 krb5_error_code
112 krb5_digest_set_type(krb5_context context,
113 		     krb5_digest digest,
114 		     const char *type)
115 {
116     if (digest->init.type) {
117 	krb5_set_error_string(context, "client type already set");
118 	return EINVAL;
119     }
120     digest->init.type = strdup(type);
121     if (digest->init.type == NULL) {
122 	krb5_set_error_string(context, "out of memory");
123 	return ENOMEM;
124     }
125     return 0;
126 }
127 
128 krb5_error_code
129 krb5_digest_set_hostname(krb5_context context,
130 			 krb5_digest digest,
131 			 const char *hostname)
132 {
133     if (digest->init.hostname) {
134 	krb5_set_error_string(context, "server hostname already set");
135 	return EINVAL;
136     }
137     digest->init.hostname = malloc(sizeof(*digest->init.hostname));
138     if (digest->init.hostname == NULL) {
139 	krb5_set_error_string(context, "out of memory");
140 	return ENOMEM;
141     }
142     *digest->init.hostname = strdup(hostname);
143     if (*digest->init.hostname == NULL) {
144 	krb5_set_error_string(context, "out of memory");
145 	free(digest->init.hostname);
146 	digest->init.hostname = NULL;
147 	return ENOMEM;
148     }
149     return 0;
150 }
151 
152 const char *
153 krb5_digest_get_server_nonce(krb5_context context,
154 			     krb5_digest digest)
155 {
156     return digest->initReply.nonce;
157 }
158 
159 krb5_error_code
160 krb5_digest_set_server_nonce(krb5_context context,
161 			     krb5_digest digest,
162 			     const char *nonce)
163 {
164     if (digest->request.serverNonce) {
165 	krb5_set_error_string(context, "nonce already set");
166 	return EINVAL;
167     }
168     digest->request.serverNonce = strdup(nonce);
169     if (digest->request.serverNonce == NULL) {
170 	krb5_set_error_string(context, "out of memory");
171 	return ENOMEM;
172     }
173     return 0;
174 }
175 
176 const char *
177 krb5_digest_get_opaque(krb5_context context,
178 		       krb5_digest digest)
179 {
180     return digest->initReply.opaque;
181 }
182 
183 krb5_error_code
184 krb5_digest_set_opaque(krb5_context context,
185 		       krb5_digest digest,
186 		       const char *opaque)
187 {
188     if (digest->request.opaque) {
189 	krb5_set_error_string(context, "opaque already set");
190 	return EINVAL;
191     }
192     digest->request.opaque = strdup(opaque);
193     if (digest->request.opaque == NULL) {
194 	krb5_set_error_string(context, "out of memory");
195 	return ENOMEM;
196     }
197     return 0;
198 }
199 
200 const char *
201 krb5_digest_get_identifier(krb5_context context,
202 			   krb5_digest digest)
203 {
204     if (digest->initReply.identifier == NULL)
205 	return NULL;
206     return *digest->initReply.identifier;
207 }
208 
209 krb5_error_code
210 krb5_digest_set_identifier(krb5_context context,
211 			   krb5_digest digest,
212 			   const char *id)
213 {
214     if (digest->request.identifier) {
215 	krb5_set_error_string(context, "identifier already set");
216 	return EINVAL;
217     }
218     digest->request.identifier = calloc(1, sizeof(*digest->request.identifier));
219     if (digest->request.identifier == NULL) {
220 	krb5_set_error_string(context, "out of memory");
221 	return ENOMEM;
222     }
223     *digest->request.identifier = strdup(id);
224     if (*digest->request.identifier == NULL) {
225 	krb5_set_error_string(context, "out of memory");
226 	free(digest->request.identifier);
227 	digest->request.identifier = NULL;
228 	return ENOMEM;
229     }
230     return 0;
231 }
232 
233 static krb5_error_code
234 digest_request(krb5_context context,
235 	       krb5_realm realm,
236 	       krb5_ccache ccache,
237 	       krb5_key_usage usage,
238 	       const DigestReqInner *ireq,
239 	       DigestRepInner *irep)
240 {
241     DigestREQ req;
242     DigestREP rep;
243     krb5_error_code ret;
244     krb5_data data, data2;
245     size_t size;
246     krb5_crypto crypto = NULL;
247     krb5_auth_context ac = NULL;
248     krb5_principal principal = NULL;
249     krb5_ccache id = NULL;
250     krb5_realm r = NULL;
251 
252     krb5_data_zero(&data);
253     krb5_data_zero(&data2);
254     memset(&req, 0, sizeof(req));
255     memset(&rep, 0, sizeof(rep));
256 
257     if (ccache == NULL) {
258 	ret = krb5_cc_default(context, &id);
259 	if (ret)
260 	    goto out;
261     } else
262 	id = ccache;
263 
264     if (realm == NULL) {
265 	ret = krb5_get_default_realm(context, &r);
266 	if (ret)
267 	    goto out;
268     } else
269 	r = realm;
270 
271     /*
272      *
273      */
274 
275     ret = krb5_make_principal(context, &principal,
276 			      r, KRB5_DIGEST_NAME, r, NULL);
277     if (ret)
278 	goto out;
279 
280     ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length,
281 		       ireq, &size, ret);
282     if (ret) {
283 	krb5_set_error_string(context,
284 			      "Failed to encode digest inner request");
285 	goto out;
286     }
287     if (size != data.length)
288 	krb5_abortx(context, "ASN.1 internal encoder error");
289 
290     ret = krb5_mk_req_exact(context, &ac,
291 			    AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED,
292 			    principal, NULL, id, &req.apReq);
293     if (ret)
294 	goto out;
295 
296     {
297 	krb5_keyblock *key;
298 
299 	ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
300 	if (ret)
301 	    goto out;
302 	if (key == NULL) {
303 	    krb5_set_error_string(context, "Digest failed to get local subkey");
304 	    ret = EINVAL;
305 	    goto out;
306 	}
307 
308 	ret = krb5_crypto_init(context, key, 0, &crypto);
309 	krb5_free_keyblock (context, key);
310 	if (ret)
311 	    goto out;
312     }
313 
314     ret = krb5_encrypt_EncryptedData(context, crypto, usage,
315 				     data.data, data.length, 0,
316 				     &req.innerReq);
317     if (ret)
318 	goto out;
319 
320     krb5_data_free(&data);
321 
322     ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length,
323 		       &req, &size, ret);
324     if (ret) {
325 	krb5_set_error_string(context, "Failed to encode DigestREQest");
326 	goto out;
327     }
328     if (size != data.length)
329 	krb5_abortx(context, "ASN.1 internal encoder error");
330 
331     ret = krb5_sendto_kdc(context, &data, &r, &data2);
332     if (ret)
333 	goto out;
334 
335     ret = decode_DigestREP(data2.data, data2.length, &rep, NULL);
336     if (ret) {
337 	krb5_set_error_string(context, "Failed to parse digest response");
338 	goto out;
339     }
340 
341     {
342 	krb5_ap_rep_enc_part *repl;
343 
344 	ret = krb5_rd_rep(context, ac, &rep.apRep, &repl);
345 	if (ret)
346 	    goto out;
347 
348 	krb5_free_ap_rep_enc_part(context, repl);
349     }
350     {
351 	krb5_keyblock *key;
352 
353 	ret = krb5_auth_con_getremotesubkey(context, ac, &key);
354 	if (ret)
355 	    goto out;
356 	if (key == NULL) {
357 	    ret = EINVAL;
358 	    krb5_set_error_string(context,
359 				  "Digest reply have no remote subkey");
360 	    goto out;
361 	}
362 
363 	krb5_crypto_destroy(context, crypto);
364 	ret = krb5_crypto_init(context, key, 0, &crypto);
365 	krb5_free_keyblock (context, key);
366 	if (ret)
367 	    goto out;
368     }
369 
370     krb5_data_free(&data);
371     ret = krb5_decrypt_EncryptedData(context, crypto, usage,
372 				     &rep.innerRep, &data);
373     if (ret)
374 	goto out;
375 
376     ret = decode_DigestRepInner(data.data, data.length, irep, NULL);
377     if (ret) {
378 	krb5_set_error_string(context, "Failed to decode digest inner reply");
379 	goto out;
380     }
381 
382 out:
383     if (ccache == NULL && id)
384 	krb5_cc_close(context, id);
385     if (realm == NULL && r)
386 	free(r);
387     if (crypto)
388 	krb5_crypto_destroy(context, crypto);
389     if (ac)
390 	krb5_auth_con_free(context, ac);
391     if (principal)
392 	krb5_free_principal(context, principal);
393 
394     krb5_data_free(&data);
395     krb5_data_free(&data2);
396 
397     free_DigestREQ(&req);
398     free_DigestREP(&rep);
399 
400     return ret;
401 }
402 
403 krb5_error_code
404 krb5_digest_init_request(krb5_context context,
405 			 krb5_digest digest,
406 			 krb5_realm realm,
407 			 krb5_ccache ccache)
408 {
409     DigestReqInner ireq;
410     DigestRepInner irep;
411     krb5_error_code ret;
412 
413     memset(&ireq, 0, sizeof(ireq));
414     memset(&irep, 0, sizeof(irep));
415 
416     if (digest->init.type == NULL) {
417 	krb5_set_error_string(context, "Type missing from init req");
418 	return EINVAL;
419     }
420 
421     ireq.element = choice_DigestReqInner_init;
422     ireq.u.init = digest->init;
423 
424     ret = digest_request(context, realm, ccache,
425 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
426     if (ret)
427 	goto out;
428 
429     if (irep.element == choice_DigestRepInner_error) {
430 	krb5_set_error_string(context, "Digest init error: %s",
431 			      irep.u.error.reason);
432 	ret = irep.u.error.code;
433 	goto out;
434     }
435 
436     if (irep.element != choice_DigestRepInner_initReply) {
437 	krb5_set_error_string(context, "digest reply not an initReply");
438 	ret = EINVAL;
439 	goto out;
440     }
441 
442     ret = copy_DigestInitReply(&irep.u.initReply, &digest->initReply);
443     if (ret) {
444 	krb5_set_error_string(context, "Failed to copy initReply");
445 	goto out;
446     }
447 
448 out:
449     free_DigestRepInner(&irep);
450 
451     return ret;
452 }
453 
454 
455 krb5_error_code
456 krb5_digest_set_client_nonce(krb5_context context,
457 			     krb5_digest digest,
458 			     const char *nonce)
459 {
460     if (digest->request.clientNonce) {
461 	krb5_set_error_string(context, "clientNonce already set");
462 	return EINVAL;
463     }
464     digest->request.clientNonce =
465 	calloc(1, sizeof(*digest->request.clientNonce));
466     if (digest->request.clientNonce == NULL) {
467 	krb5_set_error_string(context, "out of memory");
468 	return ENOMEM;
469     }
470     *digest->request.clientNonce = strdup(nonce);
471     if (*digest->request.clientNonce == NULL) {
472 	krb5_set_error_string(context, "out of memory");
473 	free(digest->request.clientNonce);
474 	digest->request.clientNonce = NULL;
475 	return ENOMEM;
476     }
477     return 0;
478 }
479 
480 krb5_error_code
481 krb5_digest_set_digest(krb5_context context,
482 		       krb5_digest digest,
483 		       const char *dgst)
484 {
485     if (digest->request.digest) {
486 	krb5_set_error_string(context, "digest already set");
487 	return EINVAL;
488     }
489     digest->request.digest = strdup(dgst);
490     if (digest->request.digest == NULL) {
491 	krb5_set_error_string(context, "out of memory");
492 	return ENOMEM;
493     }
494     return 0;
495 }
496 
497 krb5_error_code
498 krb5_digest_set_username(krb5_context context,
499 			 krb5_digest digest,
500 			 const char *username)
501 {
502     if (digest->request.username) {
503 	krb5_set_error_string(context, "username already set");
504 	return EINVAL;
505     }
506     digest->request.username = strdup(username);
507     if (digest->request.username == NULL) {
508 	krb5_set_error_string(context, "out of memory");
509 	return ENOMEM;
510     }
511     return 0;
512 }
513 
514 krb5_error_code
515 krb5_digest_set_authid(krb5_context context,
516 		       krb5_digest digest,
517 		       const char *authid)
518 {
519     if (digest->request.authid) {
520 	krb5_set_error_string(context, "authid already set");
521 	return EINVAL;
522     }
523     digest->request.authid = malloc(sizeof(*digest->request.authid));
524     if (digest->request.authid == NULL) {
525 	krb5_set_error_string(context, "out of memory");
526 	return ENOMEM;
527     }
528     *digest->request.authid = strdup(authid);
529     if (*digest->request.authid == NULL) {
530 	krb5_set_error_string(context, "out of memory");
531 	free(digest->request.authid);
532 	digest->request.authid = NULL;
533 	return ENOMEM;
534     }
535     return 0;
536 }
537 
538 krb5_error_code
539 krb5_digest_set_authentication_user(krb5_context context,
540 				    krb5_digest digest,
541 				    krb5_principal authentication_user)
542 {
543     krb5_error_code ret;
544 
545     if (digest->request.authentication_user) {
546 	krb5_set_error_string(context, "authentication_user already set");
547 	return EINVAL;
548     }
549     ret = krb5_copy_principal(context,
550 			      authentication_user,
551 			      &digest->request.authentication_user);
552     if (digest->request.authentication_user == NULL) {
553 	krb5_set_error_string(context, "out of memory");
554 	return ENOMEM;
555     }
556     return 0;
557 }
558 
559 krb5_error_code
560 krb5_digest_set_realm(krb5_context context,
561 		      krb5_digest digest,
562 		      const char *realm)
563 {
564     if (digest->request.realm) {
565 	krb5_set_error_string(context, "realm already set");
566 	return EINVAL;
567     }
568     digest->request.realm = malloc(sizeof(*digest->request.realm));
569     if (digest->request.realm == NULL) {
570 	krb5_set_error_string(context, "out of memory");
571 	return ENOMEM;
572     }
573     *digest->request.realm = strdup(realm);
574     if (*digest->request.realm == NULL) {
575 	krb5_set_error_string(context, "out of memory");
576 	free(digest->request.realm);
577 	digest->request.realm = NULL;
578 	return ENOMEM;
579     }
580     return 0;
581 }
582 
583 krb5_error_code
584 krb5_digest_set_method(krb5_context context,
585 		       krb5_digest digest,
586 		       const char *method)
587 {
588     if (digest->request.method) {
589 	krb5_set_error_string(context, "method already set");
590 	return EINVAL;
591     }
592     digest->request.method = malloc(sizeof(*digest->request.method));
593     if (digest->request.method == NULL) {
594 	krb5_set_error_string(context, "out of memory");
595 	return ENOMEM;
596     }
597     *digest->request.method = strdup(method);
598     if (*digest->request.method == NULL) {
599 	krb5_set_error_string(context, "out of memory");
600 	free(digest->request.method);
601 	digest->request.method = NULL;
602 	return ENOMEM;
603     }
604     return 0;
605 }
606 
607 krb5_error_code
608 krb5_digest_set_uri(krb5_context context,
609 		    krb5_digest digest,
610 		    const char *uri)
611 {
612     if (digest->request.uri) {
613 	krb5_set_error_string(context, "uri already set");
614 	return EINVAL;
615     }
616     digest->request.uri = malloc(sizeof(*digest->request.uri));
617     if (digest->request.uri == NULL) {
618 	krb5_set_error_string(context, "out of memory");
619 	return ENOMEM;
620     }
621     *digest->request.uri = strdup(uri);
622     if (*digest->request.uri == NULL) {
623 	krb5_set_error_string(context, "out of memory");
624 	free(digest->request.uri);
625 	digest->request.uri = NULL;
626 	return ENOMEM;
627     }
628     return 0;
629 }
630 
631 krb5_error_code
632 krb5_digest_set_nonceCount(krb5_context context,
633 			   krb5_digest digest,
634 			   const char *nonce_count)
635 {
636     if (digest->request.nonceCount) {
637 	krb5_set_error_string(context, "nonceCount already set");
638 	return EINVAL;
639     }
640     digest->request.nonceCount =
641 	malloc(sizeof(*digest->request.nonceCount));
642     if (digest->request.nonceCount == NULL) {
643 	krb5_set_error_string(context, "out of memory");
644 	return ENOMEM;
645     }
646     *digest->request.nonceCount = strdup(nonce_count);
647     if (*digest->request.nonceCount == NULL) {
648 	krb5_set_error_string(context, "out of memory");
649 	free(digest->request.nonceCount);
650 	digest->request.nonceCount = NULL;
651 	return ENOMEM;
652     }
653     return 0;
654 }
655 
656 krb5_error_code
657 krb5_digest_set_qop(krb5_context context,
658 		    krb5_digest digest,
659 		    const char *qop)
660 {
661     if (digest->request.qop) {
662 	krb5_set_error_string(context, "qop already set");
663 	return EINVAL;
664     }
665     digest->request.qop = malloc(sizeof(*digest->request.qop));
666     if (digest->request.qop == NULL) {
667 	krb5_set_error_string(context, "out of memory");
668 	return ENOMEM;
669     }
670     *digest->request.qop = strdup(qop);
671     if (*digest->request.qop == NULL) {
672 	krb5_set_error_string(context, "out of memory");
673 	free(digest->request.qop);
674 	digest->request.qop = NULL;
675 	return ENOMEM;
676     }
677     return 0;
678 }
679 
680 int
681 krb5_digest_set_responseData(krb5_context context,
682 			     krb5_digest digest,
683 			     const char *response)
684 {
685     digest->request.responseData = strdup(response);
686     if (digest->request.responseData == NULL) {
687 	krb5_set_error_string(context, "out of memory");
688 	return ENOMEM;
689     }
690     return 0;
691 }
692 
693 krb5_error_code
694 krb5_digest_request(krb5_context context,
695 		    krb5_digest digest,
696 		    krb5_realm realm,
697 		    krb5_ccache ccache)
698 {
699     DigestReqInner ireq;
700     DigestRepInner irep;
701     krb5_error_code ret;
702 
703     memset(&ireq, 0, sizeof(ireq));
704     memset(&irep, 0, sizeof(irep));
705 
706     ireq.element = choice_DigestReqInner_digestRequest;
707     ireq.u.digestRequest = digest->request;
708 
709     if (digest->request.type == NULL) {
710 	if (digest->init.type == NULL) {
711 	    krb5_set_error_string(context, "Type missing from req");
712 	    return EINVAL;
713 	}
714 	ireq.u.digestRequest.type = digest->init.type;
715     }
716 
717     if (ireq.u.digestRequest.digest == NULL)
718 	ireq.u.digestRequest.digest = "md5";
719 
720     ret = digest_request(context, realm, ccache,
721 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
722     if (ret)
723 	return ret;
724 
725     if (irep.element == choice_DigestRepInner_error) {
726 	krb5_set_error_string(context, "Digest response error: %s",
727 			      irep.u.error.reason);
728 	ret = irep.u.error.code;
729 	goto out;
730     }
731 
732     if (irep.element != choice_DigestRepInner_response) {
733 	krb5_set_error_string(context, "digest reply not an DigestResponse");
734 	ret = EINVAL;
735 	goto out;
736     }
737 
738     ret = copy_DigestResponse(&irep.u.response, &digest->response);
739     if (ret) {
740 	krb5_set_error_string(context, "Failed to copy initReply");
741 	goto out;
742     }
743 
744 out:
745     free_DigestRepInner(&irep);
746 
747     return ret;
748 }
749 
750 krb5_boolean
751 krb5_digest_rep_get_status(krb5_context context,
752 			   krb5_digest digest)
753 {
754     return digest->response.success ? TRUE : FALSE;
755 }
756 
757 const char *
758 krb5_digest_get_rsp(krb5_context context,
759 		    krb5_digest digest)
760 {
761     if (digest->response.rsp == NULL)
762 	return NULL;
763     return *digest->response.rsp;
764 }
765 
766 krb5_error_code
767 krb5_digest_get_tickets(krb5_context context,
768 			krb5_digest digest,
769 			Ticket **tickets)
770 {
771     *tickets = NULL;
772     return 0;
773 }
774 
775 
776 krb5_error_code
777 krb5_digest_get_client_binding(krb5_context context,
778 			       krb5_digest digest,
779 			       char **type,
780 			       char **binding)
781 {
782     if (digest->response.channel) {
783 	*type = strdup(digest->response.channel->cb_type);
784 	*binding = strdup(digest->response.channel->cb_binding);
785 	if (*type == NULL || *binding == NULL) {
786 	    free(*type);
787 	    free(*binding);
788 	    krb5_set_error_string(context, "out of memory");
789 	    return ENOMEM;
790 	}
791     } else {
792 	*type = NULL;
793 	*binding = NULL;
794     }
795     return 0;
796 }
797 
798 krb5_error_code
799 krb5_digest_get_session_key(krb5_context context,
800 			    krb5_digest digest,
801 			    krb5_data *data)
802 {
803     krb5_error_code ret;
804 
805     krb5_data_zero(data);
806     if (digest->response.session_key == NULL)
807 	return 0;
808     ret = der_copy_octet_string(digest->response.session_key, data);
809     if (ret)
810 	krb5_clear_error_string(context);
811 
812     return ret;
813 }
814 
815 struct krb5_ntlm_data {
816     NTLMInit init;
817     NTLMInitReply initReply;
818     NTLMRequest request;
819     NTLMResponse response;
820 };
821 
822 krb5_error_code
823 krb5_ntlm_alloc(krb5_context context,
824 		krb5_ntlm *ntlm)
825 {
826     *ntlm = calloc(1, sizeof(**ntlm));
827     if (*ntlm == NULL) {
828 	krb5_set_error_string(context, "out of memory");
829 	return ENOMEM;
830     }
831     return 0;
832 }
833 
834 krb5_error_code
835 krb5_ntlm_free(krb5_context context, krb5_ntlm ntlm)
836 {
837     free_NTLMInit(&ntlm->init);
838     free_NTLMInitReply(&ntlm->initReply);
839     free_NTLMRequest(&ntlm->request);
840     free_NTLMResponse(&ntlm->response);
841     memset(ntlm, 0, sizeof(*ntlm));
842     free(ntlm);
843     return 0;
844 }
845 
846 
847 krb5_error_code
848 krb5_ntlm_init_request(krb5_context context,
849 		       krb5_ntlm ntlm,
850 		       krb5_realm realm,
851 		       krb5_ccache ccache,
852 		       uint32_t flags,
853 		       const char *hostname,
854 		       const char *domainname)
855 {
856     DigestReqInner ireq;
857     DigestRepInner irep;
858     krb5_error_code ret;
859 
860     memset(&ireq, 0, sizeof(ireq));
861     memset(&irep, 0, sizeof(irep));
862 
863     ntlm->init.flags = flags;
864     if (hostname) {
865 	ALLOC(ntlm->init.hostname, 1);
866 	*ntlm->init.hostname = strdup(hostname);
867     }
868     if (domainname) {
869 	ALLOC(ntlm->init.domain, 1);
870 	*ntlm->init.domain = strdup(domainname);
871     }
872 
873     ireq.element = choice_DigestReqInner_ntlmInit;
874     ireq.u.ntlmInit = ntlm->init;
875 
876     ret = digest_request(context, realm, ccache,
877 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
878     if (ret)
879 	goto out;
880 
881     if (irep.element == choice_DigestRepInner_error) {
882 	krb5_set_error_string(context, "Digest init error: %s",
883 			      irep.u.error.reason);
884 	ret = irep.u.error.code;
885 	goto out;
886     }
887 
888     if (irep.element != choice_DigestRepInner_ntlmInitReply) {
889 	krb5_set_error_string(context, "ntlm reply not an initReply");
890 	ret = EINVAL;
891 	goto out;
892     }
893 
894     ret = copy_NTLMInitReply(&irep.u.ntlmInitReply, &ntlm->initReply);
895     if (ret) {
896 	krb5_set_error_string(context, "Failed to copy initReply");
897 	goto out;
898     }
899 
900 out:
901     free_DigestRepInner(&irep);
902 
903     return ret;
904 }
905 
906 krb5_error_code
907 krb5_ntlm_init_get_flags(krb5_context context,
908 			 krb5_ntlm ntlm,
909 			 uint32_t *flags)
910 {
911     *flags = ntlm->initReply.flags;
912     return 0;
913 }
914 
915 krb5_error_code
916 krb5_ntlm_init_get_challange(krb5_context context,
917 			     krb5_ntlm ntlm,
918 			     krb5_data *challange)
919 {
920     krb5_error_code ret;
921 
922     ret = der_copy_octet_string(&ntlm->initReply.challange, challange);
923     if (ret)
924 	krb5_clear_error_string(context);
925 
926     return ret;
927 }
928 
929 krb5_error_code
930 krb5_ntlm_init_get_opaque(krb5_context context,
931 			  krb5_ntlm ntlm,
932 			  krb5_data *opaque)
933 {
934     krb5_error_code ret;
935 
936     ret = der_copy_octet_string(&ntlm->initReply.opaque, opaque);
937     if (ret)
938 	krb5_clear_error_string(context);
939 
940     return ret;
941 }
942 
943 krb5_error_code
944 krb5_ntlm_init_get_targetname(krb5_context context,
945 			      krb5_ntlm ntlm,
946 			      char **name)
947 {
948     *name = strdup(ntlm->initReply.targetname);
949     if (*name == NULL) {
950 	krb5_clear_error_string(context);
951 	return ENOMEM;
952     }
953     return 0;
954 }
955 
956 krb5_error_code
957 krb5_ntlm_init_get_targetinfo(krb5_context context,
958 			      krb5_ntlm ntlm,
959 			      krb5_data *data)
960 {
961     krb5_error_code ret;
962 
963     if (ntlm->initReply.targetinfo == NULL) {
964 	krb5_data_zero(data);
965 	return 0;
966     }
967 
968     ret = krb5_data_copy(data,
969 			 ntlm->initReply.targetinfo->data,
970 			 ntlm->initReply.targetinfo->length);
971     if (ret) {
972 	krb5_clear_error_string(context);
973 	return ret;
974     }
975     return 0;
976 }
977 
978 
979 krb5_error_code
980 krb5_ntlm_request(krb5_context context,
981 		  krb5_ntlm ntlm,
982 		  krb5_realm realm,
983 		  krb5_ccache ccache)
984 {
985     DigestReqInner ireq;
986     DigestRepInner irep;
987     krb5_error_code ret;
988 
989     memset(&ireq, 0, sizeof(ireq));
990     memset(&irep, 0, sizeof(irep));
991 
992     ireq.element = choice_DigestReqInner_ntlmRequest;
993     ireq.u.ntlmRequest = ntlm->request;
994 
995     ret = digest_request(context, realm, ccache,
996 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
997     if (ret)
998 	return ret;
999 
1000     if (irep.element == choice_DigestRepInner_error) {
1001 	krb5_set_error_string(context, "NTLM response error: %s",
1002 			      irep.u.error.reason);
1003 	ret = irep.u.error.code;
1004 	goto out;
1005     }
1006 
1007     if (irep.element != choice_DigestRepInner_ntlmResponse) {
1008 	krb5_set_error_string(context, "NTLM reply not an NTLMResponse");
1009 	ret = EINVAL;
1010 	goto out;
1011     }
1012 
1013     ret = copy_NTLMResponse(&irep.u.ntlmResponse, &ntlm->response);
1014     if (ret) {
1015 	krb5_set_error_string(context, "Failed to copy NTLMResponse");
1016 	goto out;
1017     }
1018 
1019 out:
1020     free_DigestRepInner(&irep);
1021 
1022     return ret;
1023 }
1024 
1025 krb5_error_code
1026 krb5_ntlm_req_set_flags(krb5_context context,
1027 			krb5_ntlm ntlm,
1028 			uint32_t flags)
1029 {
1030     ntlm->request.flags = flags;
1031     return 0;
1032 }
1033 
1034 krb5_error_code
1035 krb5_ntlm_req_set_username(krb5_context context,
1036 			   krb5_ntlm ntlm,
1037 			   const char *username)
1038 {
1039     ntlm->request.username = strdup(username);
1040     if (ntlm->request.username == NULL) {
1041 	krb5_set_error_string(context, "out of memory");
1042 	return ENOMEM;
1043     }
1044     return 0;
1045 }
1046 
1047 krb5_error_code
1048 krb5_ntlm_req_set_targetname(krb5_context context,
1049 			     krb5_ntlm ntlm,
1050 			     const char *targetname)
1051 {
1052     ntlm->request.targetname = strdup(targetname);
1053     if (ntlm->request.targetname == NULL) {
1054 	krb5_set_error_string(context, "out of memory");
1055 	return ENOMEM;
1056     }
1057     return 0;
1058 }
1059 
1060 krb5_error_code
1061 krb5_ntlm_req_set_lm(krb5_context context,
1062 		     krb5_ntlm ntlm,
1063 		     void *hash, size_t len)
1064 {
1065     ntlm->request.lm.data = malloc(len);
1066     if (ntlm->request.lm.data == NULL) {
1067 	krb5_set_error_string(context, "out of memory");
1068 	return ENOMEM;
1069     }
1070     ntlm->request.lm.length = len;
1071     memcpy(ntlm->request.lm.data, hash, len);
1072     return 0;
1073 }
1074 
1075 krb5_error_code
1076 krb5_ntlm_req_set_ntlm(krb5_context context,
1077 		       krb5_ntlm ntlm,
1078 		       void *hash, size_t len)
1079 {
1080     ntlm->request.ntlm.data = malloc(len);
1081     if (ntlm->request.ntlm.data == NULL) {
1082 	krb5_set_error_string(context, "out of memory");
1083 	return ENOMEM;
1084     }
1085     ntlm->request.ntlm.length = len;
1086     memcpy(ntlm->request.ntlm.data, hash, len);
1087     return 0;
1088 }
1089 
1090 krb5_error_code
1091 krb5_ntlm_req_set_opaque(krb5_context context,
1092 			 krb5_ntlm ntlm,
1093 			 krb5_data *opaque)
1094 {
1095     ntlm->request.opaque.data = malloc(opaque->length);
1096     if (ntlm->request.opaque.data == NULL) {
1097 	krb5_set_error_string(context, "out of memory");
1098 	return ENOMEM;
1099     }
1100     ntlm->request.opaque.length = opaque->length;
1101     memcpy(ntlm->request.opaque.data, opaque->data, opaque->length);
1102     return 0;
1103 }
1104 
1105 krb5_error_code
1106 krb5_ntlm_req_set_session(krb5_context context,
1107 			  krb5_ntlm ntlm,
1108 			  void *sessionkey, size_t length)
1109 {
1110     ntlm->request.sessionkey = calloc(1, sizeof(*ntlm->request.sessionkey));
1111     if (ntlm->request.sessionkey == NULL) {
1112 	krb5_set_error_string(context, "out of memory");
1113 	return ENOMEM;
1114     }
1115     ntlm->request.sessionkey->data = malloc(length);
1116     if (ntlm->request.sessionkey->data == NULL) {
1117 	krb5_set_error_string(context, "out of memory");
1118 	return ENOMEM;
1119     }
1120     memcpy(ntlm->request.sessionkey->data, sessionkey, length);
1121     ntlm->request.sessionkey->length = length;
1122     return 0;
1123 }
1124 
1125 krb5_boolean
1126 krb5_ntlm_rep_get_status(krb5_context context,
1127 			 krb5_ntlm ntlm)
1128 {
1129     return ntlm->response.success ? TRUE : FALSE;
1130 }
1131 
1132 krb5_error_code
1133 krb5_ntlm_rep_get_sessionkey(krb5_context context,
1134 			     krb5_ntlm ntlm,
1135 			     krb5_data *data)
1136 {
1137     if (ntlm->response.sessionkey == NULL) {
1138 	krb5_set_error_string(context, "no ntlm session key");
1139 	return EINVAL;
1140     }
1141     krb5_clear_error_string(context);
1142     return krb5_data_copy(data,
1143 			  ntlm->response.sessionkey->data,
1144 			  ntlm->response.sessionkey->length);
1145 }
1146 
1147 /**
1148  * Get the supported/allowed mechanism for this principal.
1149  *
1150  * @param context A Keberos context.
1151  * @param realm The realm of the KDC.
1152  * @param ccache The credential cache to use when talking to the KDC.
1153  * @param flags The supported mechanism.
1154  *
1155  * @return Return an error code or 0.
1156  *
1157  * @ingroup krb5_digest
1158  */
1159 
1160 krb5_error_code
1161 krb5_digest_probe(krb5_context context,
1162 		  krb5_realm realm,
1163 		  krb5_ccache ccache,
1164 		  unsigned *flags)
1165 {
1166     DigestReqInner ireq;
1167     DigestRepInner irep;
1168     krb5_error_code ret;
1169 
1170     memset(&ireq, 0, sizeof(ireq));
1171     memset(&irep, 0, sizeof(irep));
1172 
1173     ireq.element = choice_DigestReqInner_supportedMechs;
1174 
1175     ret = digest_request(context, realm, ccache,
1176 			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
1177     if (ret)
1178 	goto out;
1179 
1180     if (irep.element == choice_DigestRepInner_error) {
1181 	krb5_set_error_string(context, "Digest probe error: %s",
1182 			      irep.u.error.reason);
1183 	ret = irep.u.error.code;
1184 	goto out;
1185     }
1186 
1187     if (irep.element != choice_DigestRepInner_supportedMechs) {
1188 	krb5_set_error_string(context, "Digest reply not an probe");
1189 	ret = EINVAL;
1190 	goto out;
1191     }
1192 
1193     *flags = DigestTypes2int(irep.u.supportedMechs);
1194 
1195 out:
1196     free_DigestRepInner(&irep);
1197 
1198     return ret;
1199 }
1200