1178825Sdfr/*
2233294Sstas * Copyright (c) 2006 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
20178825Sdfr *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "krb5_locl.h"
35178825Sdfr#include "digest_asn1.h"
36178825Sdfr
37233294Sstas#ifndef HEIMDAL_SMALLER
38233294Sstas
39178825Sdfrstruct krb5_digest_data {
40178825Sdfr    char *cbtype;
41178825Sdfr    char *cbbinding;
42178825Sdfr
43178825Sdfr    DigestInit init;
44178825Sdfr    DigestInitReply initReply;
45178825Sdfr    DigestRequest request;
46178825Sdfr    DigestResponse response;
47178825Sdfr};
48178825Sdfr
49233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
50178825Sdfrkrb5_digest_alloc(krb5_context context, krb5_digest *digest)
51178825Sdfr{
52178825Sdfr    krb5_digest d;
53178825Sdfr
54178825Sdfr    d = calloc(1, sizeof(*d));
55178825Sdfr    if (d == NULL) {
56178825Sdfr	*digest = NULL;
57233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
58178825Sdfr	return ENOMEM;
59178825Sdfr    }
60178825Sdfr    *digest = d;
61178825Sdfr
62178825Sdfr    return 0;
63178825Sdfr}
64178825Sdfr
65233294SstasKRB5_LIB_FUNCTION void KRB5_LIB_CALL
66178825Sdfrkrb5_digest_free(krb5_digest digest)
67178825Sdfr{
68178825Sdfr    if (digest == NULL)
69178825Sdfr	return;
70178825Sdfr    free_DigestInit(&digest->init);
71178825Sdfr    free_DigestInitReply(&digest->initReply);
72178825Sdfr    free_DigestRequest(&digest->request);
73178825Sdfr    free_DigestResponse(&digest->response);
74178825Sdfr    memset(digest, 0, sizeof(*digest));
75178825Sdfr    free(digest);
76178825Sdfr    return;
77178825Sdfr}
78178825Sdfr
79233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
80178825Sdfrkrb5_digest_set_server_cb(krb5_context context,
81178825Sdfr			  krb5_digest digest,
82178825Sdfr			  const char *type,
83178825Sdfr			  const char *binding)
84178825Sdfr{
85178825Sdfr    if (digest->init.channel) {
86233294Sstas	krb5_set_error_message(context, EINVAL,
87233294Sstas			       N_("server channel binding already set", ""));
88178825Sdfr	return EINVAL;
89178825Sdfr    }
90178825Sdfr    digest->init.channel = calloc(1, sizeof(*digest->init.channel));
91178825Sdfr    if (digest->init.channel == NULL)
92178825Sdfr	goto error;
93178825Sdfr
94178825Sdfr    digest->init.channel->cb_type = strdup(type);
95178825Sdfr    if (digest->init.channel->cb_type == NULL)
96178825Sdfr	goto error;
97178825Sdfr
98178825Sdfr    digest->init.channel->cb_binding = strdup(binding);
99233294Sstas    if (digest->init.channel->cb_binding == NULL)
100178825Sdfr	goto error;
101178825Sdfr    return 0;
102233294Sstas error:
103178825Sdfr    if (digest->init.channel) {
104178825Sdfr	free(digest->init.channel->cb_type);
105178825Sdfr	free(digest->init.channel->cb_binding);
106178825Sdfr	free(digest->init.channel);
107178825Sdfr	digest->init.channel = NULL;
108178825Sdfr    }
109233294Sstas    krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
110178825Sdfr    return ENOMEM;
111178825Sdfr}
112178825Sdfr
113233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
114178825Sdfrkrb5_digest_set_type(krb5_context context,
115178825Sdfr		     krb5_digest digest,
116178825Sdfr		     const char *type)
117178825Sdfr{
118178825Sdfr    if (digest->init.type) {
119233294Sstas	krb5_set_error_message(context, EINVAL, "client type already set");
120178825Sdfr	return EINVAL;
121178825Sdfr    }
122178825Sdfr    digest->init.type = strdup(type);
123178825Sdfr    if (digest->init.type == NULL) {
124233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
125178825Sdfr	return ENOMEM;
126178825Sdfr    }
127178825Sdfr    return 0;
128178825Sdfr}
129178825Sdfr
130233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
131178825Sdfrkrb5_digest_set_hostname(krb5_context context,
132178825Sdfr			 krb5_digest digest,
133178825Sdfr			 const char *hostname)
134178825Sdfr{
135178825Sdfr    if (digest->init.hostname) {
136233294Sstas	krb5_set_error_message(context, EINVAL, "server hostname already set");
137178825Sdfr	return EINVAL;
138178825Sdfr    }
139178825Sdfr    digest->init.hostname = malloc(sizeof(*digest->init.hostname));
140178825Sdfr    if (digest->init.hostname == NULL) {
141233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
142178825Sdfr	return ENOMEM;
143178825Sdfr    }
144178825Sdfr    *digest->init.hostname = strdup(hostname);
145178825Sdfr    if (*digest->init.hostname == NULL) {
146233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
147178825Sdfr	free(digest->init.hostname);
148178825Sdfr	digest->init.hostname = NULL;
149178825Sdfr	return ENOMEM;
150178825Sdfr    }
151178825Sdfr    return 0;
152178825Sdfr}
153178825Sdfr
154233294SstasKRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
155178825Sdfrkrb5_digest_get_server_nonce(krb5_context context,
156178825Sdfr			     krb5_digest digest)
157178825Sdfr{
158178825Sdfr    return digest->initReply.nonce;
159178825Sdfr}
160178825Sdfr
161233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
162178825Sdfrkrb5_digest_set_server_nonce(krb5_context context,
163178825Sdfr			     krb5_digest digest,
164178825Sdfr			     const char *nonce)
165178825Sdfr{
166178825Sdfr    if (digest->request.serverNonce) {
167233294Sstas	krb5_set_error_message(context, EINVAL, N_("nonce already set", ""));
168178825Sdfr	return EINVAL;
169178825Sdfr    }
170178825Sdfr    digest->request.serverNonce = strdup(nonce);
171178825Sdfr    if (digest->request.serverNonce == NULL) {
172233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
173178825Sdfr	return ENOMEM;
174178825Sdfr    }
175178825Sdfr    return 0;
176178825Sdfr}
177178825Sdfr
178233294SstasKRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
179178825Sdfrkrb5_digest_get_opaque(krb5_context context,
180178825Sdfr		       krb5_digest digest)
181178825Sdfr{
182178825Sdfr    return digest->initReply.opaque;
183178825Sdfr}
184178825Sdfr
185233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
186178825Sdfrkrb5_digest_set_opaque(krb5_context context,
187178825Sdfr		       krb5_digest digest,
188178825Sdfr		       const char *opaque)
189178825Sdfr{
190178825Sdfr    if (digest->request.opaque) {
191233294Sstas	krb5_set_error_message(context, EINVAL, "opaque already set");
192178825Sdfr	return EINVAL;
193178825Sdfr    }
194178825Sdfr    digest->request.opaque = strdup(opaque);
195178825Sdfr    if (digest->request.opaque == NULL) {
196233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
197178825Sdfr	return ENOMEM;
198178825Sdfr    }
199178825Sdfr    return 0;
200178825Sdfr}
201178825Sdfr
202233294SstasKRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
203178825Sdfrkrb5_digest_get_identifier(krb5_context context,
204178825Sdfr			   krb5_digest digest)
205178825Sdfr{
206178825Sdfr    if (digest->initReply.identifier == NULL)
207178825Sdfr	return NULL;
208178825Sdfr    return *digest->initReply.identifier;
209178825Sdfr}
210178825Sdfr
211233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
212178825Sdfrkrb5_digest_set_identifier(krb5_context context,
213178825Sdfr			   krb5_digest digest,
214178825Sdfr			   const char *id)
215178825Sdfr{
216178825Sdfr    if (digest->request.identifier) {
217233294Sstas	krb5_set_error_message(context, EINVAL, N_("identifier already set", ""));
218178825Sdfr	return EINVAL;
219178825Sdfr    }
220178825Sdfr    digest->request.identifier = calloc(1, sizeof(*digest->request.identifier));
221178825Sdfr    if (digest->request.identifier == NULL) {
222233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
223178825Sdfr	return ENOMEM;
224178825Sdfr    }
225178825Sdfr    *digest->request.identifier = strdup(id);
226178825Sdfr    if (*digest->request.identifier == NULL) {
227233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
228178825Sdfr	free(digest->request.identifier);
229178825Sdfr	digest->request.identifier = NULL;
230178825Sdfr	return ENOMEM;
231178825Sdfr    }
232178825Sdfr    return 0;
233178825Sdfr}
234178825Sdfr
235178825Sdfrstatic krb5_error_code
236178825Sdfrdigest_request(krb5_context context,
237178825Sdfr	       krb5_realm realm,
238178825Sdfr	       krb5_ccache ccache,
239178825Sdfr	       krb5_key_usage usage,
240178825Sdfr	       const DigestReqInner *ireq,
241178825Sdfr	       DigestRepInner *irep)
242178825Sdfr{
243178825Sdfr    DigestREQ req;
244178825Sdfr    DigestREP rep;
245178825Sdfr    krb5_error_code ret;
246178825Sdfr    krb5_data data, data2;
247233294Sstas    size_t size = 0;
248178825Sdfr    krb5_crypto crypto = NULL;
249178825Sdfr    krb5_auth_context ac = NULL;
250178825Sdfr    krb5_principal principal = NULL;
251178825Sdfr    krb5_ccache id = NULL;
252178825Sdfr    krb5_realm r = NULL;
253178825Sdfr
254178825Sdfr    krb5_data_zero(&data);
255178825Sdfr    krb5_data_zero(&data2);
256178825Sdfr    memset(&req, 0, sizeof(req));
257178825Sdfr    memset(&rep, 0, sizeof(rep));
258178825Sdfr
259178825Sdfr    if (ccache == NULL) {
260178825Sdfr	ret = krb5_cc_default(context, &id);
261178825Sdfr	if (ret)
262178825Sdfr	    goto out;
263178825Sdfr    } else
264178825Sdfr	id = ccache;
265178825Sdfr
266178825Sdfr    if (realm == NULL) {
267178825Sdfr	ret = krb5_get_default_realm(context, &r);
268178825Sdfr	if (ret)
269178825Sdfr	    goto out;
270178825Sdfr    } else
271178825Sdfr	r = realm;
272178825Sdfr
273178825Sdfr    /*
274178825Sdfr     *
275178825Sdfr     */
276178825Sdfr
277233294Sstas    ret = krb5_make_principal(context, &principal,
278178825Sdfr			      r, KRB5_DIGEST_NAME, r, NULL);
279178825Sdfr    if (ret)
280178825Sdfr	goto out;
281178825Sdfr
282178825Sdfr    ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length,
283178825Sdfr		       ireq, &size, ret);
284178825Sdfr    if (ret) {
285233294Sstas	krb5_set_error_message(context, ret,
286233294Sstas			       N_("Failed to encode digest inner request", ""));
287178825Sdfr	goto out;
288178825Sdfr    }
289178825Sdfr    if (size != data.length)
290178825Sdfr	krb5_abortx(context, "ASN.1 internal encoder error");
291178825Sdfr
292233294Sstas    ret = krb5_mk_req_exact(context, &ac,
293178825Sdfr			    AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED,
294178825Sdfr			    principal, NULL, id, &req.apReq);
295178825Sdfr    if (ret)
296178825Sdfr	goto out;
297178825Sdfr
298178825Sdfr    {
299178825Sdfr	krb5_keyblock *key;
300178825Sdfr
301178825Sdfr	ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
302178825Sdfr	if (ret)
303178825Sdfr	    goto out;
304178825Sdfr	if (key == NULL) {
305178825Sdfr	    ret = EINVAL;
306233294Sstas	    krb5_set_error_message(context, ret,
307233294Sstas				   N_("Digest failed to get local subkey", ""));
308178825Sdfr	    goto out;
309178825Sdfr	}
310178825Sdfr
311178825Sdfr	ret = krb5_crypto_init(context, key, 0, &crypto);
312178825Sdfr	krb5_free_keyblock (context, key);
313178825Sdfr	if (ret)
314178825Sdfr	    goto out;
315178825Sdfr    }
316178825Sdfr
317178825Sdfr    ret = krb5_encrypt_EncryptedData(context, crypto, usage,
318233294Sstas				     data.data, data.length, 0,
319178825Sdfr				     &req.innerReq);
320178825Sdfr    if (ret)
321178825Sdfr	goto out;
322178825Sdfr
323178825Sdfr    krb5_data_free(&data);
324178825Sdfr
325178825Sdfr    ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length,
326178825Sdfr		       &req, &size, ret);
327178825Sdfr    if (ret) {
328233294Sstas	krb5_set_error_message(context, ret,
329233294Sstas			       N_("Failed to encode DigestREQest", ""));
330178825Sdfr	goto out;
331178825Sdfr    }
332178825Sdfr    if (size != data.length)
333178825Sdfr	krb5_abortx(context, "ASN.1 internal encoder error");
334178825Sdfr
335178825Sdfr    ret = krb5_sendto_kdc(context, &data, &r, &data2);
336178825Sdfr    if (ret)
337178825Sdfr	goto out;
338178825Sdfr
339178825Sdfr    ret = decode_DigestREP(data2.data, data2.length, &rep, NULL);
340178825Sdfr    if (ret) {
341233294Sstas	krb5_set_error_message(context, ret,
342233294Sstas			       N_("Failed to parse digest response", ""));
343178825Sdfr	goto out;
344178825Sdfr    }
345178825Sdfr
346178825Sdfr    {
347178825Sdfr	krb5_ap_rep_enc_part *repl;
348178825Sdfr
349178825Sdfr	ret = krb5_rd_rep(context, ac, &rep.apRep, &repl);
350178825Sdfr	if (ret)
351178825Sdfr	    goto out;
352178825Sdfr
353178825Sdfr	krb5_free_ap_rep_enc_part(context, repl);
354178825Sdfr    }
355178825Sdfr    {
356178825Sdfr	krb5_keyblock *key;
357178825Sdfr
358178825Sdfr	ret = krb5_auth_con_getremotesubkey(context, ac, &key);
359178825Sdfr	if (ret)
360178825Sdfr	    goto out;
361178825Sdfr	if (key == NULL) {
362178825Sdfr	    ret = EINVAL;
363233294Sstas	    krb5_set_error_message(context, ret,
364233294Sstas				   N_("Digest reply have no remote subkey", ""));
365178825Sdfr	    goto out;
366178825Sdfr	}
367178825Sdfr
368178825Sdfr	krb5_crypto_destroy(context, crypto);
369178825Sdfr	ret = krb5_crypto_init(context, key, 0, &crypto);
370178825Sdfr	krb5_free_keyblock (context, key);
371178825Sdfr	if (ret)
372178825Sdfr	    goto out;
373178825Sdfr    }
374178825Sdfr
375178825Sdfr    krb5_data_free(&data);
376178825Sdfr    ret = krb5_decrypt_EncryptedData(context, crypto, usage,
377178825Sdfr				     &rep.innerRep, &data);
378178825Sdfr    if (ret)
379178825Sdfr	goto out;
380233294Sstas
381178825Sdfr    ret = decode_DigestRepInner(data.data, data.length, irep, NULL);
382178825Sdfr    if (ret) {
383233294Sstas	krb5_set_error_message(context, ret,
384233294Sstas			       N_("Failed to decode digest inner reply", ""));
385178825Sdfr	goto out;
386178825Sdfr    }
387178825Sdfr
388233294Sstas out:
389178825Sdfr    if (ccache == NULL && id)
390178825Sdfr	krb5_cc_close(context, id);
391178825Sdfr    if (realm == NULL && r)
392178825Sdfr	free(r);
393178825Sdfr    if (crypto)
394178825Sdfr	krb5_crypto_destroy(context, crypto);
395178825Sdfr    if (ac)
396178825Sdfr	krb5_auth_con_free(context, ac);
397178825Sdfr    if (principal)
398178825Sdfr	krb5_free_principal(context, principal);
399178825Sdfr
400178825Sdfr    krb5_data_free(&data);
401178825Sdfr    krb5_data_free(&data2);
402178825Sdfr
403178825Sdfr    free_DigestREQ(&req);
404178825Sdfr    free_DigestREP(&rep);
405178825Sdfr
406178825Sdfr    return ret;
407178825Sdfr}
408178825Sdfr
409233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
410178825Sdfrkrb5_digest_init_request(krb5_context context,
411178825Sdfr			 krb5_digest digest,
412178825Sdfr			 krb5_realm realm,
413178825Sdfr			 krb5_ccache ccache)
414178825Sdfr{
415178825Sdfr    DigestReqInner ireq;
416178825Sdfr    DigestRepInner irep;
417178825Sdfr    krb5_error_code ret;
418178825Sdfr
419178825Sdfr    memset(&ireq, 0, sizeof(ireq));
420178825Sdfr    memset(&irep, 0, sizeof(irep));
421178825Sdfr
422178825Sdfr    if (digest->init.type == NULL) {
423233294Sstas	krb5_set_error_message(context, EINVAL,
424233294Sstas			       N_("Type missing from init req", ""));
425178825Sdfr	return EINVAL;
426178825Sdfr    }
427178825Sdfr
428178825Sdfr    ireq.element = choice_DigestReqInner_init;
429178825Sdfr    ireq.u.init = digest->init;
430178825Sdfr
431178825Sdfr    ret = digest_request(context, realm, ccache,
432178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
433178825Sdfr    if (ret)
434178825Sdfr	goto out;
435178825Sdfr
436178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
437178825Sdfr	ret = irep.u.error.code;
438233294Sstas	krb5_set_error_message(context, ret, N_("Digest init error: %s", ""),
439233294Sstas			       irep.u.error.reason);
440178825Sdfr	goto out;
441178825Sdfr    }
442178825Sdfr
443178825Sdfr    if (irep.element != choice_DigestRepInner_initReply) {
444178825Sdfr	ret = EINVAL;
445233294Sstas	krb5_set_error_message(context, ret,
446233294Sstas			       N_("digest reply not an initReply", ""));
447178825Sdfr	goto out;
448178825Sdfr    }
449178825Sdfr
450178825Sdfr    ret = copy_DigestInitReply(&irep.u.initReply, &digest->initReply);
451178825Sdfr    if (ret) {
452233294Sstas	krb5_set_error_message(context, ret,
453233294Sstas			       N_("Failed to copy initReply", ""));
454178825Sdfr	goto out;
455178825Sdfr    }
456178825Sdfr
457233294Sstas out:
458178825Sdfr    free_DigestRepInner(&irep);
459178825Sdfr
460178825Sdfr    return ret;
461178825Sdfr}
462178825Sdfr
463178825Sdfr
464233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
465178825Sdfrkrb5_digest_set_client_nonce(krb5_context context,
466178825Sdfr			     krb5_digest digest,
467178825Sdfr			     const char *nonce)
468178825Sdfr{
469178825Sdfr    if (digest->request.clientNonce) {
470233294Sstas	krb5_set_error_message(context, EINVAL,
471233294Sstas			       N_("clientNonce already set", ""));
472178825Sdfr	return EINVAL;
473178825Sdfr    }
474233294Sstas    digest->request.clientNonce =
475178825Sdfr	calloc(1, sizeof(*digest->request.clientNonce));
476178825Sdfr    if (digest->request.clientNonce == NULL) {
477233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
478178825Sdfr	return ENOMEM;
479178825Sdfr    }
480178825Sdfr    *digest->request.clientNonce = strdup(nonce);
481178825Sdfr    if (*digest->request.clientNonce == NULL) {
482233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
483178825Sdfr	free(digest->request.clientNonce);
484178825Sdfr	digest->request.clientNonce = NULL;
485178825Sdfr	return ENOMEM;
486178825Sdfr    }
487178825Sdfr    return 0;
488178825Sdfr}
489178825Sdfr
490233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
491178825Sdfrkrb5_digest_set_digest(krb5_context context,
492178825Sdfr		       krb5_digest digest,
493178825Sdfr		       const char *dgst)
494178825Sdfr{
495178825Sdfr    if (digest->request.digest) {
496233294Sstas	krb5_set_error_message(context, EINVAL,
497233294Sstas			       N_("digest already set", ""));
498178825Sdfr	return EINVAL;
499178825Sdfr    }
500178825Sdfr    digest->request.digest = strdup(dgst);
501178825Sdfr    if (digest->request.digest == NULL) {
502233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
503178825Sdfr	return ENOMEM;
504178825Sdfr    }
505178825Sdfr    return 0;
506178825Sdfr}
507178825Sdfr
508233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
509178825Sdfrkrb5_digest_set_username(krb5_context context,
510178825Sdfr			 krb5_digest digest,
511178825Sdfr			 const char *username)
512178825Sdfr{
513178825Sdfr    if (digest->request.username) {
514233294Sstas	krb5_set_error_message(context, EINVAL, "username already set");
515178825Sdfr	return EINVAL;
516178825Sdfr    }
517178825Sdfr    digest->request.username = strdup(username);
518178825Sdfr    if (digest->request.username == NULL) {
519233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
520178825Sdfr	return ENOMEM;
521178825Sdfr    }
522178825Sdfr    return 0;
523178825Sdfr}
524178825Sdfr
525233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
526178825Sdfrkrb5_digest_set_authid(krb5_context context,
527178825Sdfr		       krb5_digest digest,
528178825Sdfr		       const char *authid)
529178825Sdfr{
530178825Sdfr    if (digest->request.authid) {
531233294Sstas	krb5_set_error_message(context, EINVAL, "authid already set");
532178825Sdfr	return EINVAL;
533178825Sdfr    }
534178825Sdfr    digest->request.authid = malloc(sizeof(*digest->request.authid));
535178825Sdfr    if (digest->request.authid == NULL) {
536233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
537178825Sdfr	return ENOMEM;
538178825Sdfr    }
539178825Sdfr    *digest->request.authid = strdup(authid);
540178825Sdfr    if (*digest->request.authid == NULL) {
541233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
542178825Sdfr	free(digest->request.authid);
543178825Sdfr	digest->request.authid = NULL;
544178825Sdfr	return ENOMEM;
545178825Sdfr    }
546178825Sdfr    return 0;
547178825Sdfr}
548178825Sdfr
549233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
550178825Sdfrkrb5_digest_set_authentication_user(krb5_context context,
551178825Sdfr				    krb5_digest digest,
552178825Sdfr				    krb5_principal authentication_user)
553178825Sdfr{
554178825Sdfr    krb5_error_code ret;
555178825Sdfr
556178825Sdfr    if (digest->request.authentication_user) {
557233294Sstas	krb5_set_error_message(context, EINVAL,
558233294Sstas			       N_("authentication_user already set", ""));
559178825Sdfr	return EINVAL;
560178825Sdfr    }
561178825Sdfr    ret = krb5_copy_principal(context,
562178825Sdfr			      authentication_user,
563178825Sdfr			      &digest->request.authentication_user);
564233294Sstas    if (ret)
565233294Sstas	return ret;
566178825Sdfr    return 0;
567178825Sdfr}
568178825Sdfr
569233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
570178825Sdfrkrb5_digest_set_realm(krb5_context context,
571178825Sdfr		      krb5_digest digest,
572178825Sdfr		      const char *realm)
573178825Sdfr{
574178825Sdfr    if (digest->request.realm) {
575233294Sstas	krb5_set_error_message(context, EINVAL, "realm already set");
576178825Sdfr	return EINVAL;
577178825Sdfr    }
578178825Sdfr    digest->request.realm = malloc(sizeof(*digest->request.realm));
579178825Sdfr    if (digest->request.realm == NULL) {
580233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
581178825Sdfr	return ENOMEM;
582178825Sdfr    }
583178825Sdfr    *digest->request.realm = strdup(realm);
584178825Sdfr    if (*digest->request.realm == NULL) {
585233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
586178825Sdfr	free(digest->request.realm);
587178825Sdfr	digest->request.realm = NULL;
588178825Sdfr	return ENOMEM;
589178825Sdfr    }
590178825Sdfr    return 0;
591178825Sdfr}
592178825Sdfr
593233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
594178825Sdfrkrb5_digest_set_method(krb5_context context,
595178825Sdfr		       krb5_digest digest,
596178825Sdfr		       const char *method)
597178825Sdfr{
598178825Sdfr    if (digest->request.method) {
599233294Sstas	krb5_set_error_message(context, EINVAL,
600233294Sstas			       N_("method already set", ""));
601178825Sdfr	return EINVAL;
602178825Sdfr    }
603178825Sdfr    digest->request.method = malloc(sizeof(*digest->request.method));
604178825Sdfr    if (digest->request.method == NULL) {
605233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
606178825Sdfr	return ENOMEM;
607178825Sdfr    }
608178825Sdfr    *digest->request.method = strdup(method);
609178825Sdfr    if (*digest->request.method == NULL) {
610233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
611178825Sdfr	free(digest->request.method);
612178825Sdfr	digest->request.method = NULL;
613178825Sdfr	return ENOMEM;
614178825Sdfr    }
615178825Sdfr    return 0;
616178825Sdfr}
617178825Sdfr
618233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
619178825Sdfrkrb5_digest_set_uri(krb5_context context,
620178825Sdfr		    krb5_digest digest,
621178825Sdfr		    const char *uri)
622178825Sdfr{
623178825Sdfr    if (digest->request.uri) {
624233294Sstas	krb5_set_error_message(context, EINVAL, N_("uri already set", ""));
625178825Sdfr	return EINVAL;
626178825Sdfr    }
627178825Sdfr    digest->request.uri = malloc(sizeof(*digest->request.uri));
628178825Sdfr    if (digest->request.uri == NULL) {
629233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
630178825Sdfr	return ENOMEM;
631178825Sdfr    }
632178825Sdfr    *digest->request.uri = strdup(uri);
633178825Sdfr    if (*digest->request.uri == NULL) {
634233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
635178825Sdfr	free(digest->request.uri);
636178825Sdfr	digest->request.uri = NULL;
637178825Sdfr	return ENOMEM;
638178825Sdfr    }
639178825Sdfr    return 0;
640178825Sdfr}
641178825Sdfr
642233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
643178825Sdfrkrb5_digest_set_nonceCount(krb5_context context,
644178825Sdfr			   krb5_digest digest,
645178825Sdfr			   const char *nonce_count)
646178825Sdfr{
647178825Sdfr    if (digest->request.nonceCount) {
648233294Sstas	krb5_set_error_message(context, EINVAL,
649233294Sstas			       N_("nonceCount already set", ""));
650178825Sdfr	return EINVAL;
651178825Sdfr    }
652233294Sstas    digest->request.nonceCount =
653178825Sdfr	malloc(sizeof(*digest->request.nonceCount));
654178825Sdfr    if (digest->request.nonceCount == NULL) {
655233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
656178825Sdfr	return ENOMEM;
657178825Sdfr    }
658178825Sdfr    *digest->request.nonceCount = strdup(nonce_count);
659178825Sdfr    if (*digest->request.nonceCount == NULL) {
660233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
661178825Sdfr	free(digest->request.nonceCount);
662178825Sdfr	digest->request.nonceCount = NULL;
663178825Sdfr	return ENOMEM;
664178825Sdfr    }
665178825Sdfr    return 0;
666178825Sdfr}
667178825Sdfr
668233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
669178825Sdfrkrb5_digest_set_qop(krb5_context context,
670178825Sdfr		    krb5_digest digest,
671178825Sdfr		    const char *qop)
672178825Sdfr{
673178825Sdfr    if (digest->request.qop) {
674233294Sstas	krb5_set_error_message(context, EINVAL, "qop already set");
675178825Sdfr	return EINVAL;
676178825Sdfr    }
677178825Sdfr    digest->request.qop = malloc(sizeof(*digest->request.qop));
678178825Sdfr    if (digest->request.qop == NULL) {
679233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
680178825Sdfr	return ENOMEM;
681178825Sdfr    }
682178825Sdfr    *digest->request.qop = strdup(qop);
683178825Sdfr    if (*digest->request.qop == NULL) {
684233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
685178825Sdfr	free(digest->request.qop);
686178825Sdfr	digest->request.qop = NULL;
687178825Sdfr	return ENOMEM;
688178825Sdfr    }
689178825Sdfr    return 0;
690178825Sdfr}
691178825Sdfr
692233294SstasKRB5_LIB_FUNCTION int KRB5_LIB_CALL
693178825Sdfrkrb5_digest_set_responseData(krb5_context context,
694178825Sdfr			     krb5_digest digest,
695178825Sdfr			     const char *response)
696178825Sdfr{
697178825Sdfr    digest->request.responseData = strdup(response);
698178825Sdfr    if (digest->request.responseData == NULL) {
699233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
700178825Sdfr	return ENOMEM;
701178825Sdfr    }
702178825Sdfr    return 0;
703178825Sdfr}
704178825Sdfr
705233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
706178825Sdfrkrb5_digest_request(krb5_context context,
707178825Sdfr		    krb5_digest digest,
708178825Sdfr		    krb5_realm realm,
709178825Sdfr		    krb5_ccache ccache)
710178825Sdfr{
711178825Sdfr    DigestReqInner ireq;
712178825Sdfr    DigestRepInner irep;
713178825Sdfr    krb5_error_code ret;
714178825Sdfr
715178825Sdfr    memset(&ireq, 0, sizeof(ireq));
716178825Sdfr    memset(&irep, 0, sizeof(irep));
717178825Sdfr
718178825Sdfr    ireq.element = choice_DigestReqInner_digestRequest;
719178825Sdfr    ireq.u.digestRequest = digest->request;
720178825Sdfr
721178825Sdfr    if (digest->request.type == NULL) {
722178825Sdfr	if (digest->init.type == NULL) {
723233294Sstas	    krb5_set_error_message(context, EINVAL,
724233294Sstas				   N_("Type missing from req", ""));
725178825Sdfr	    return EINVAL;
726178825Sdfr	}
727178825Sdfr	ireq.u.digestRequest.type = digest->init.type;
728178825Sdfr    }
729178825Sdfr
730233294Sstas    if (ireq.u.digestRequest.digest == NULL) {
731233294Sstas	static char md5[] = "md5";
732233294Sstas	ireq.u.digestRequest.digest = md5;
733233294Sstas    }
734178825Sdfr
735178825Sdfr    ret = digest_request(context, realm, ccache,
736178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
737178825Sdfr    if (ret)
738178825Sdfr	return ret;
739178825Sdfr
740178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
741178825Sdfr	ret = irep.u.error.code;
742233294Sstas	krb5_set_error_message(context, ret,
743233294Sstas			       N_("Digest response error: %s", ""),
744233294Sstas			       irep.u.error.reason);
745178825Sdfr	goto out;
746178825Sdfr    }
747178825Sdfr
748178825Sdfr    if (irep.element != choice_DigestRepInner_response) {
749233294Sstas	krb5_set_error_message(context, EINVAL,
750233294Sstas			       N_("digest reply not an DigestResponse", ""));
751178825Sdfr	ret = EINVAL;
752178825Sdfr	goto out;
753178825Sdfr    }
754178825Sdfr
755178825Sdfr    ret = copy_DigestResponse(&irep.u.response, &digest->response);
756178825Sdfr    if (ret) {
757233294Sstas	krb5_set_error_message(context, ret,
758233294Sstas			       N_("Failed to copy initReply,", ""));
759178825Sdfr	goto out;
760178825Sdfr    }
761178825Sdfr
762233294Sstas out:
763178825Sdfr    free_DigestRepInner(&irep);
764178825Sdfr
765178825Sdfr    return ret;
766178825Sdfr}
767178825Sdfr
768233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
769233294Sstaskrb5_digest_rep_get_status(krb5_context context,
770178825Sdfr			   krb5_digest digest)
771178825Sdfr{
772178825Sdfr    return digest->response.success ? TRUE : FALSE;
773178825Sdfr}
774178825Sdfr
775233294SstasKRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
776178825Sdfrkrb5_digest_get_rsp(krb5_context context,
777178825Sdfr		    krb5_digest digest)
778178825Sdfr{
779178825Sdfr    if (digest->response.rsp == NULL)
780178825Sdfr	return NULL;
781178825Sdfr    return *digest->response.rsp;
782178825Sdfr}
783178825Sdfr
784233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
785178825Sdfrkrb5_digest_get_tickets(krb5_context context,
786178825Sdfr			krb5_digest digest,
787178825Sdfr			Ticket **tickets)
788178825Sdfr{
789178825Sdfr    *tickets = NULL;
790178825Sdfr    return 0;
791178825Sdfr}
792178825Sdfr
793178825Sdfr
794233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
795178825Sdfrkrb5_digest_get_client_binding(krb5_context context,
796178825Sdfr			       krb5_digest digest,
797178825Sdfr			       char **type,
798178825Sdfr			       char **binding)
799178825Sdfr{
800178825Sdfr    if (digest->response.channel) {
801178825Sdfr	*type = strdup(digest->response.channel->cb_type);
802178825Sdfr	*binding = strdup(digest->response.channel->cb_binding);
803178825Sdfr	if (*type == NULL || *binding == NULL) {
804178825Sdfr	    free(*type);
805178825Sdfr	    free(*binding);
806233294Sstas	    krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
807178825Sdfr	    return ENOMEM;
808178825Sdfr	}
809178825Sdfr    } else {
810178825Sdfr	*type = NULL;
811178825Sdfr	*binding = NULL;
812178825Sdfr    }
813178825Sdfr    return 0;
814178825Sdfr}
815178825Sdfr
816233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
817178825Sdfrkrb5_digest_get_session_key(krb5_context context,
818178825Sdfr			    krb5_digest digest,
819178825Sdfr			    krb5_data *data)
820178825Sdfr{
821178825Sdfr    krb5_error_code ret;
822178825Sdfr
823178825Sdfr    krb5_data_zero(data);
824178825Sdfr    if (digest->response.session_key == NULL)
825178825Sdfr	return 0;
826178825Sdfr    ret = der_copy_octet_string(digest->response.session_key, data);
827178825Sdfr    if (ret)
828233294Sstas	krb5_clear_error_message(context);
829178825Sdfr
830178825Sdfr    return ret;
831178825Sdfr}
832178825Sdfr
833178825Sdfrstruct krb5_ntlm_data {
834178825Sdfr    NTLMInit init;
835178825Sdfr    NTLMInitReply initReply;
836178825Sdfr    NTLMRequest request;
837178825Sdfr    NTLMResponse response;
838178825Sdfr};
839178825Sdfr
840233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
841233294Sstaskrb5_ntlm_alloc(krb5_context context,
842178825Sdfr		krb5_ntlm *ntlm)
843178825Sdfr{
844178825Sdfr    *ntlm = calloc(1, sizeof(**ntlm));
845178825Sdfr    if (*ntlm == NULL) {
846233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
847178825Sdfr	return ENOMEM;
848178825Sdfr    }
849178825Sdfr    return 0;
850178825Sdfr}
851178825Sdfr
852233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
853178825Sdfrkrb5_ntlm_free(krb5_context context, krb5_ntlm ntlm)
854178825Sdfr{
855178825Sdfr    free_NTLMInit(&ntlm->init);
856178825Sdfr    free_NTLMInitReply(&ntlm->initReply);
857178825Sdfr    free_NTLMRequest(&ntlm->request);
858178825Sdfr    free_NTLMResponse(&ntlm->response);
859178825Sdfr    memset(ntlm, 0, sizeof(*ntlm));
860178825Sdfr    free(ntlm);
861178825Sdfr    return 0;
862178825Sdfr}
863178825Sdfr
864178825Sdfr
865233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
866233294Sstaskrb5_ntlm_init_request(krb5_context context,
867178825Sdfr		       krb5_ntlm ntlm,
868178825Sdfr		       krb5_realm realm,
869178825Sdfr		       krb5_ccache ccache,
870178825Sdfr		       uint32_t flags,
871178825Sdfr		       const char *hostname,
872178825Sdfr		       const char *domainname)
873178825Sdfr{
874178825Sdfr    DigestReqInner ireq;
875178825Sdfr    DigestRepInner irep;
876178825Sdfr    krb5_error_code ret;
877178825Sdfr
878178825Sdfr    memset(&ireq, 0, sizeof(ireq));
879178825Sdfr    memset(&irep, 0, sizeof(irep));
880178825Sdfr
881178825Sdfr    ntlm->init.flags = flags;
882178825Sdfr    if (hostname) {
883178825Sdfr	ALLOC(ntlm->init.hostname, 1);
884178825Sdfr	*ntlm->init.hostname = strdup(hostname);
885178825Sdfr    }
886178825Sdfr    if (domainname) {
887178825Sdfr	ALLOC(ntlm->init.domain, 1);
888178825Sdfr	*ntlm->init.domain = strdup(domainname);
889178825Sdfr    }
890178825Sdfr
891178825Sdfr    ireq.element = choice_DigestReqInner_ntlmInit;
892178825Sdfr    ireq.u.ntlmInit = ntlm->init;
893178825Sdfr
894178825Sdfr    ret = digest_request(context, realm, ccache,
895178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
896178825Sdfr    if (ret)
897178825Sdfr	goto out;
898178825Sdfr
899178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
900178825Sdfr	ret = irep.u.error.code;
901233294Sstas	krb5_set_error_message(context, ret, N_("Digest init error: %s", ""),
902233294Sstas			       irep.u.error.reason);
903178825Sdfr	goto out;
904178825Sdfr    }
905178825Sdfr
906178825Sdfr    if (irep.element != choice_DigestRepInner_ntlmInitReply) {
907178825Sdfr	ret = EINVAL;
908233294Sstas	krb5_set_error_message(context, ret,
909233294Sstas			       N_("ntlm reply not an initReply", ""));
910178825Sdfr	goto out;
911178825Sdfr    }
912178825Sdfr
913178825Sdfr    ret = copy_NTLMInitReply(&irep.u.ntlmInitReply, &ntlm->initReply);
914178825Sdfr    if (ret) {
915233294Sstas	krb5_set_error_message(context, ret,
916233294Sstas			       N_("Failed to copy initReply", ""));
917178825Sdfr	goto out;
918178825Sdfr    }
919178825Sdfr
920233294Sstas out:
921178825Sdfr    free_DigestRepInner(&irep);
922178825Sdfr
923178825Sdfr    return ret;
924178825Sdfr}
925178825Sdfr
926233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
927178825Sdfrkrb5_ntlm_init_get_flags(krb5_context context,
928178825Sdfr			 krb5_ntlm ntlm,
929178825Sdfr			 uint32_t *flags)
930178825Sdfr{
931178825Sdfr    *flags = ntlm->initReply.flags;
932178825Sdfr    return 0;
933178825Sdfr}
934178825Sdfr
935233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
936178825Sdfrkrb5_ntlm_init_get_challange(krb5_context context,
937178825Sdfr			     krb5_ntlm ntlm,
938178825Sdfr			     krb5_data *challange)
939178825Sdfr{
940178825Sdfr    krb5_error_code ret;
941178825Sdfr
942178825Sdfr    ret = der_copy_octet_string(&ntlm->initReply.challange, challange);
943178825Sdfr    if (ret)
944233294Sstas	krb5_clear_error_message(context);
945178825Sdfr
946178825Sdfr    return ret;
947178825Sdfr}
948178825Sdfr
949233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
950178825Sdfrkrb5_ntlm_init_get_opaque(krb5_context context,
951178825Sdfr			  krb5_ntlm ntlm,
952178825Sdfr			  krb5_data *opaque)
953178825Sdfr{
954178825Sdfr    krb5_error_code ret;
955178825Sdfr
956178825Sdfr    ret = der_copy_octet_string(&ntlm->initReply.opaque, opaque);
957178825Sdfr    if (ret)
958233294Sstas	krb5_clear_error_message(context);
959178825Sdfr
960178825Sdfr    return ret;
961178825Sdfr}
962178825Sdfr
963233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
964178825Sdfrkrb5_ntlm_init_get_targetname(krb5_context context,
965178825Sdfr			      krb5_ntlm ntlm,
966178825Sdfr			      char **name)
967178825Sdfr{
968178825Sdfr    *name = strdup(ntlm->initReply.targetname);
969178825Sdfr    if (*name == NULL) {
970233294Sstas	krb5_clear_error_message(context);
971178825Sdfr	return ENOMEM;
972178825Sdfr    }
973178825Sdfr    return 0;
974178825Sdfr}
975178825Sdfr
976233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
977178825Sdfrkrb5_ntlm_init_get_targetinfo(krb5_context context,
978178825Sdfr			      krb5_ntlm ntlm,
979178825Sdfr			      krb5_data *data)
980178825Sdfr{
981178825Sdfr    krb5_error_code ret;
982178825Sdfr
983178825Sdfr    if (ntlm->initReply.targetinfo == NULL) {
984178825Sdfr	krb5_data_zero(data);
985178825Sdfr	return 0;
986178825Sdfr    }
987178825Sdfr
988178825Sdfr    ret = krb5_data_copy(data,
989178825Sdfr			 ntlm->initReply.targetinfo->data,
990178825Sdfr			 ntlm->initReply.targetinfo->length);
991178825Sdfr    if (ret) {
992233294Sstas	krb5_clear_error_message(context);
993178825Sdfr	return ret;
994178825Sdfr    }
995178825Sdfr    return 0;
996178825Sdfr}
997178825Sdfr
998178825Sdfr
999233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1000178825Sdfrkrb5_ntlm_request(krb5_context context,
1001178825Sdfr		  krb5_ntlm ntlm,
1002178825Sdfr		  krb5_realm realm,
1003178825Sdfr		  krb5_ccache ccache)
1004178825Sdfr{
1005178825Sdfr    DigestReqInner ireq;
1006178825Sdfr    DigestRepInner irep;
1007178825Sdfr    krb5_error_code ret;
1008178825Sdfr
1009178825Sdfr    memset(&ireq, 0, sizeof(ireq));
1010178825Sdfr    memset(&irep, 0, sizeof(irep));
1011178825Sdfr
1012178825Sdfr    ireq.element = choice_DigestReqInner_ntlmRequest;
1013178825Sdfr    ireq.u.ntlmRequest = ntlm->request;
1014178825Sdfr
1015178825Sdfr    ret = digest_request(context, realm, ccache,
1016178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
1017178825Sdfr    if (ret)
1018178825Sdfr	return ret;
1019178825Sdfr
1020178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
1021178825Sdfr	ret = irep.u.error.code;
1022233294Sstas	krb5_set_error_message(context, ret,
1023233294Sstas			       N_("NTLM response error: %s", ""),
1024233294Sstas			       irep.u.error.reason);
1025178825Sdfr	goto out;
1026178825Sdfr    }
1027178825Sdfr
1028178825Sdfr    if (irep.element != choice_DigestRepInner_ntlmResponse) {
1029178825Sdfr	ret = EINVAL;
1030233294Sstas	krb5_set_error_message(context, ret,
1031233294Sstas			       N_("NTLM reply not an NTLMResponse", ""));
1032178825Sdfr	goto out;
1033178825Sdfr    }
1034178825Sdfr
1035178825Sdfr    ret = copy_NTLMResponse(&irep.u.ntlmResponse, &ntlm->response);
1036178825Sdfr    if (ret) {
1037233294Sstas	krb5_set_error_message(context, ret,
1038233294Sstas			       N_("Failed to copy NTLMResponse", ""));
1039178825Sdfr	goto out;
1040178825Sdfr    }
1041178825Sdfr
1042233294Sstas out:
1043178825Sdfr    free_DigestRepInner(&irep);
1044178825Sdfr
1045178825Sdfr    return ret;
1046178825Sdfr}
1047178825Sdfr
1048233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1049233294Sstaskrb5_ntlm_req_set_flags(krb5_context context,
1050178825Sdfr			krb5_ntlm ntlm,
1051178825Sdfr			uint32_t flags)
1052178825Sdfr{
1053178825Sdfr    ntlm->request.flags = flags;
1054178825Sdfr    return 0;
1055178825Sdfr}
1056178825Sdfr
1057233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1058233294Sstaskrb5_ntlm_req_set_username(krb5_context context,
1059178825Sdfr			   krb5_ntlm ntlm,
1060178825Sdfr			   const char *username)
1061178825Sdfr{
1062178825Sdfr    ntlm->request.username = strdup(username);
1063178825Sdfr    if (ntlm->request.username == NULL) {
1064233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1065178825Sdfr	return ENOMEM;
1066178825Sdfr    }
1067178825Sdfr    return 0;
1068178825Sdfr}
1069178825Sdfr
1070233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1071233294Sstaskrb5_ntlm_req_set_targetname(krb5_context context,
1072178825Sdfr			     krb5_ntlm ntlm,
1073178825Sdfr			     const char *targetname)
1074178825Sdfr{
1075178825Sdfr    ntlm->request.targetname = strdup(targetname);
1076178825Sdfr    if (ntlm->request.targetname == NULL) {
1077233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1078178825Sdfr	return ENOMEM;
1079178825Sdfr    }
1080178825Sdfr    return 0;
1081178825Sdfr}
1082178825Sdfr
1083233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1084233294Sstaskrb5_ntlm_req_set_lm(krb5_context context,
1085178825Sdfr		     krb5_ntlm ntlm,
1086178825Sdfr		     void *hash, size_t len)
1087178825Sdfr{
1088178825Sdfr    ntlm->request.lm.data = malloc(len);
1089233294Sstas    if (ntlm->request.lm.data == NULL && len != 0) {
1090233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1091178825Sdfr	return ENOMEM;
1092178825Sdfr    }
1093178825Sdfr    ntlm->request.lm.length = len;
1094178825Sdfr    memcpy(ntlm->request.lm.data, hash, len);
1095178825Sdfr    return 0;
1096178825Sdfr}
1097178825Sdfr
1098233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1099233294Sstaskrb5_ntlm_req_set_ntlm(krb5_context context,
1100178825Sdfr		       krb5_ntlm ntlm,
1101178825Sdfr		       void *hash, size_t len)
1102178825Sdfr{
1103178825Sdfr    ntlm->request.ntlm.data = malloc(len);
1104233294Sstas    if (ntlm->request.ntlm.data == NULL && len != 0) {
1105233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1106178825Sdfr	return ENOMEM;
1107178825Sdfr    }
1108178825Sdfr    ntlm->request.ntlm.length = len;
1109178825Sdfr    memcpy(ntlm->request.ntlm.data, hash, len);
1110178825Sdfr    return 0;
1111178825Sdfr}
1112178825Sdfr
1113233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1114233294Sstaskrb5_ntlm_req_set_opaque(krb5_context context,
1115178825Sdfr			 krb5_ntlm ntlm,
1116178825Sdfr			 krb5_data *opaque)
1117178825Sdfr{
1118178825Sdfr    ntlm->request.opaque.data = malloc(opaque->length);
1119233294Sstas    if (ntlm->request.opaque.data == NULL && opaque->length != 0) {
1120233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1121178825Sdfr	return ENOMEM;
1122178825Sdfr    }
1123178825Sdfr    ntlm->request.opaque.length = opaque->length;
1124178825Sdfr    memcpy(ntlm->request.opaque.data, opaque->data, opaque->length);
1125178825Sdfr    return 0;
1126178825Sdfr}
1127178825Sdfr
1128233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1129233294Sstaskrb5_ntlm_req_set_session(krb5_context context,
1130178825Sdfr			  krb5_ntlm ntlm,
1131178825Sdfr			  void *sessionkey, size_t length)
1132178825Sdfr{
1133178825Sdfr    ntlm->request.sessionkey = calloc(1, sizeof(*ntlm->request.sessionkey));
1134178825Sdfr    if (ntlm->request.sessionkey == NULL) {
1135233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1136178825Sdfr	return ENOMEM;
1137178825Sdfr    }
1138178825Sdfr    ntlm->request.sessionkey->data = malloc(length);
1139233294Sstas    if (ntlm->request.sessionkey->data == NULL && length != 0) {
1140233294Sstas	krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
1141178825Sdfr	return ENOMEM;
1142178825Sdfr    }
1143178825Sdfr    memcpy(ntlm->request.sessionkey->data, sessionkey, length);
1144178825Sdfr    ntlm->request.sessionkey->length = length;
1145178825Sdfr    return 0;
1146178825Sdfr}
1147178825Sdfr
1148233294SstasKRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1149233294Sstaskrb5_ntlm_rep_get_status(krb5_context context,
1150178825Sdfr			 krb5_ntlm ntlm)
1151178825Sdfr{
1152178825Sdfr    return ntlm->response.success ? TRUE : FALSE;
1153178825Sdfr}
1154178825Sdfr
1155233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1156233294Sstaskrb5_ntlm_rep_get_sessionkey(krb5_context context,
1157178825Sdfr			     krb5_ntlm ntlm,
1158178825Sdfr			     krb5_data *data)
1159178825Sdfr{
1160178825Sdfr    if (ntlm->response.sessionkey == NULL) {
1161233294Sstas	krb5_set_error_message(context, EINVAL,
1162233294Sstas			       N_("no ntlm session key", ""));
1163178825Sdfr	return EINVAL;
1164178825Sdfr    }
1165233294Sstas    krb5_clear_error_message(context);
1166178825Sdfr    return krb5_data_copy(data,
1167178825Sdfr			  ntlm->response.sessionkey->data,
1168178825Sdfr			  ntlm->response.sessionkey->length);
1169178825Sdfr}
1170178825Sdfr
1171178825Sdfr/**
1172178825Sdfr * Get the supported/allowed mechanism for this principal.
1173178825Sdfr *
1174178825Sdfr * @param context A Keberos context.
1175178825Sdfr * @param realm The realm of the KDC.
1176178825Sdfr * @param ccache The credential cache to use when talking to the KDC.
1177178825Sdfr * @param flags The supported mechanism.
1178178825Sdfr *
1179178825Sdfr * @return Return an error code or 0.
1180178825Sdfr *
1181178825Sdfr * @ingroup krb5_digest
1182178825Sdfr */
1183178825Sdfr
1184233294SstasKRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1185178825Sdfrkrb5_digest_probe(krb5_context context,
1186178825Sdfr		  krb5_realm realm,
1187178825Sdfr		  krb5_ccache ccache,
1188178825Sdfr		  unsigned *flags)
1189178825Sdfr{
1190178825Sdfr    DigestReqInner ireq;
1191178825Sdfr    DigestRepInner irep;
1192178825Sdfr    krb5_error_code ret;
1193178825Sdfr
1194178825Sdfr    memset(&ireq, 0, sizeof(ireq));
1195178825Sdfr    memset(&irep, 0, sizeof(irep));
1196178825Sdfr
1197178825Sdfr    ireq.element = choice_DigestReqInner_supportedMechs;
1198178825Sdfr
1199178825Sdfr    ret = digest_request(context, realm, ccache,
1200178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
1201178825Sdfr    if (ret)
1202178825Sdfr	goto out;
1203178825Sdfr
1204178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
1205178825Sdfr	ret = irep.u.error.code;
1206233294Sstas	krb5_set_error_message(context, ret, "Digest probe error: %s",
1207233294Sstas			       irep.u.error.reason);
1208178825Sdfr	goto out;
1209178825Sdfr    }
1210178825Sdfr
1211178825Sdfr    if (irep.element != choice_DigestRepInner_supportedMechs) {
1212178825Sdfr	ret = EINVAL;
1213233294Sstas	krb5_set_error_message(context, ret, "Digest reply not an probe");
1214178825Sdfr	goto out;
1215178825Sdfr    }
1216178825Sdfr
1217178825Sdfr    *flags = DigestTypes2int(irep.u.supportedMechs);
1218178825Sdfr
1219233294Sstas out:
1220178825Sdfr    free_DigestRepInner(&irep);
1221178825Sdfr
1222178825Sdfr    return ret;
1223178825Sdfr}
1224233294Sstas
1225233294Sstas#endif /* HEIMDAL_SMALLER */
1226