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