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