1178825Sdfr/*
2178825Sdfr * Copyright (c) 2006 Kungliga Tekniska H�gskolan
3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden).
4178825Sdfr * All rights reserved.
5178825Sdfr *
6178825Sdfr * Redistribution and use in source and binary forms, with or without
7178825Sdfr * modification, are permitted provided that the following conditions
8178825Sdfr * are met:
9178825Sdfr *
10178825Sdfr * 1. Redistributions of source code must retain the above copyright
11178825Sdfr *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright
14178825Sdfr *    notice, this list of conditions and the following disclaimer in the
15178825Sdfr *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors
18178825Sdfr *    may be used to endorse or promote products derived from this software
19178825Sdfr *    without specific prior written permission.
20178825Sdfr *
21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24178825Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31178825Sdfr * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34178825Sdfr#include "krb5_locl.h"
35178825SdfrRCSID("$Id: digest.c 22156 2007-12-04 20:02:49Z lha $");
36178825Sdfr#include "digest_asn1.h"
37178825Sdfr
38178825Sdfrstruct krb5_digest_data {
39178825Sdfr    char *cbtype;
40178825Sdfr    char *cbbinding;
41178825Sdfr
42178825Sdfr    DigestInit init;
43178825Sdfr    DigestInitReply initReply;
44178825Sdfr    DigestRequest request;
45178825Sdfr    DigestResponse response;
46178825Sdfr};
47178825Sdfr
48178825Sdfrkrb5_error_code
49178825Sdfrkrb5_digest_alloc(krb5_context context, krb5_digest *digest)
50178825Sdfr{
51178825Sdfr    krb5_digest d;
52178825Sdfr
53178825Sdfr    d = calloc(1, sizeof(*d));
54178825Sdfr    if (d == NULL) {
55178825Sdfr	*digest = NULL;
56178825Sdfr	krb5_set_error_string(context, "out of memory");
57178825Sdfr	return ENOMEM;
58178825Sdfr    }
59178825Sdfr    *digest = d;
60178825Sdfr
61178825Sdfr    return 0;
62178825Sdfr}
63178825Sdfr
64178825Sdfrvoid
65178825Sdfrkrb5_digest_free(krb5_digest digest)
66178825Sdfr{
67178825Sdfr    if (digest == NULL)
68178825Sdfr	return;
69178825Sdfr    free_DigestInit(&digest->init);
70178825Sdfr    free_DigestInitReply(&digest->initReply);
71178825Sdfr    free_DigestRequest(&digest->request);
72178825Sdfr    free_DigestResponse(&digest->response);
73178825Sdfr    memset(digest, 0, sizeof(*digest));
74178825Sdfr    free(digest);
75178825Sdfr    return;
76178825Sdfr}
77178825Sdfr
78178825Sdfrkrb5_error_code
79178825Sdfrkrb5_digest_set_server_cb(krb5_context context,
80178825Sdfr			  krb5_digest digest,
81178825Sdfr			  const char *type,
82178825Sdfr			  const char *binding)
83178825Sdfr{
84178825Sdfr    if (digest->init.channel) {
85178825Sdfr	krb5_set_error_string(context, "server channel binding already set");
86178825Sdfr	return EINVAL;
87178825Sdfr    }
88178825Sdfr    digest->init.channel = calloc(1, sizeof(*digest->init.channel));
89178825Sdfr    if (digest->init.channel == NULL)
90178825Sdfr	goto error;
91178825Sdfr
92178825Sdfr    digest->init.channel->cb_type = strdup(type);
93178825Sdfr    if (digest->init.channel->cb_type == NULL)
94178825Sdfr	goto error;
95178825Sdfr
96178825Sdfr    digest->init.channel->cb_binding = strdup(binding);
97178825Sdfr    if (digest->init.channel->cb_binding == NULL)
98178825Sdfr	goto error;
99178825Sdfr    return 0;
100178825Sdfrerror:
101178825Sdfr    if (digest->init.channel) {
102178825Sdfr	free(digest->init.channel->cb_type);
103178825Sdfr	free(digest->init.channel->cb_binding);
104178825Sdfr	free(digest->init.channel);
105178825Sdfr	digest->init.channel = NULL;
106178825Sdfr    }
107178825Sdfr    krb5_set_error_string(context, "out of memory");
108178825Sdfr    return ENOMEM;
109178825Sdfr}
110178825Sdfr
111178825Sdfrkrb5_error_code
112178825Sdfrkrb5_digest_set_type(krb5_context context,
113178825Sdfr		     krb5_digest digest,
114178825Sdfr		     const char *type)
115178825Sdfr{
116178825Sdfr    if (digest->init.type) {
117178825Sdfr	krb5_set_error_string(context, "client type already set");
118178825Sdfr	return EINVAL;
119178825Sdfr    }
120178825Sdfr    digest->init.type = strdup(type);
121178825Sdfr    if (digest->init.type == NULL) {
122178825Sdfr	krb5_set_error_string(context, "out of memory");
123178825Sdfr	return ENOMEM;
124178825Sdfr    }
125178825Sdfr    return 0;
126178825Sdfr}
127178825Sdfr
128178825Sdfrkrb5_error_code
129178825Sdfrkrb5_digest_set_hostname(krb5_context context,
130178825Sdfr			 krb5_digest digest,
131178825Sdfr			 const char *hostname)
132178825Sdfr{
133178825Sdfr    if (digest->init.hostname) {
134178825Sdfr	krb5_set_error_string(context, "server hostname already set");
135178825Sdfr	return EINVAL;
136178825Sdfr    }
137178825Sdfr    digest->init.hostname = malloc(sizeof(*digest->init.hostname));
138178825Sdfr    if (digest->init.hostname == NULL) {
139178825Sdfr	krb5_set_error_string(context, "out of memory");
140178825Sdfr	return ENOMEM;
141178825Sdfr    }
142178825Sdfr    *digest->init.hostname = strdup(hostname);
143178825Sdfr    if (*digest->init.hostname == NULL) {
144178825Sdfr	krb5_set_error_string(context, "out of memory");
145178825Sdfr	free(digest->init.hostname);
146178825Sdfr	digest->init.hostname = NULL;
147178825Sdfr	return ENOMEM;
148178825Sdfr    }
149178825Sdfr    return 0;
150178825Sdfr}
151178825Sdfr
152178825Sdfrconst char *
153178825Sdfrkrb5_digest_get_server_nonce(krb5_context context,
154178825Sdfr			     krb5_digest digest)
155178825Sdfr{
156178825Sdfr    return digest->initReply.nonce;
157178825Sdfr}
158178825Sdfr
159178825Sdfrkrb5_error_code
160178825Sdfrkrb5_digest_set_server_nonce(krb5_context context,
161178825Sdfr			     krb5_digest digest,
162178825Sdfr			     const char *nonce)
163178825Sdfr{
164178825Sdfr    if (digest->request.serverNonce) {
165178825Sdfr	krb5_set_error_string(context, "nonce already set");
166178825Sdfr	return EINVAL;
167178825Sdfr    }
168178825Sdfr    digest->request.serverNonce = strdup(nonce);
169178825Sdfr    if (digest->request.serverNonce == NULL) {
170178825Sdfr	krb5_set_error_string(context, "out of memory");
171178825Sdfr	return ENOMEM;
172178825Sdfr    }
173178825Sdfr    return 0;
174178825Sdfr}
175178825Sdfr
176178825Sdfrconst char *
177178825Sdfrkrb5_digest_get_opaque(krb5_context context,
178178825Sdfr		       krb5_digest digest)
179178825Sdfr{
180178825Sdfr    return digest->initReply.opaque;
181178825Sdfr}
182178825Sdfr
183178825Sdfrkrb5_error_code
184178825Sdfrkrb5_digest_set_opaque(krb5_context context,
185178825Sdfr		       krb5_digest digest,
186178825Sdfr		       const char *opaque)
187178825Sdfr{
188178825Sdfr    if (digest->request.opaque) {
189178825Sdfr	krb5_set_error_string(context, "opaque already set");
190178825Sdfr	return EINVAL;
191178825Sdfr    }
192178825Sdfr    digest->request.opaque = strdup(opaque);
193178825Sdfr    if (digest->request.opaque == NULL) {
194178825Sdfr	krb5_set_error_string(context, "out of memory");
195178825Sdfr	return ENOMEM;
196178825Sdfr    }
197178825Sdfr    return 0;
198178825Sdfr}
199178825Sdfr
200178825Sdfrconst char *
201178825Sdfrkrb5_digest_get_identifier(krb5_context context,
202178825Sdfr			   krb5_digest digest)
203178825Sdfr{
204178825Sdfr    if (digest->initReply.identifier == NULL)
205178825Sdfr	return NULL;
206178825Sdfr    return *digest->initReply.identifier;
207178825Sdfr}
208178825Sdfr
209178825Sdfrkrb5_error_code
210178825Sdfrkrb5_digest_set_identifier(krb5_context context,
211178825Sdfr			   krb5_digest digest,
212178825Sdfr			   const char *id)
213178825Sdfr{
214178825Sdfr    if (digest->request.identifier) {
215178825Sdfr	krb5_set_error_string(context, "identifier already set");
216178825Sdfr	return EINVAL;
217178825Sdfr    }
218178825Sdfr    digest->request.identifier = calloc(1, sizeof(*digest->request.identifier));
219178825Sdfr    if (digest->request.identifier == NULL) {
220178825Sdfr	krb5_set_error_string(context, "out of memory");
221178825Sdfr	return ENOMEM;
222178825Sdfr    }
223178825Sdfr    *digest->request.identifier = strdup(id);
224178825Sdfr    if (*digest->request.identifier == NULL) {
225178825Sdfr	krb5_set_error_string(context, "out of memory");
226178825Sdfr	free(digest->request.identifier);
227178825Sdfr	digest->request.identifier = NULL;
228178825Sdfr	return ENOMEM;
229178825Sdfr    }
230178825Sdfr    return 0;
231178825Sdfr}
232178825Sdfr
233178825Sdfrstatic krb5_error_code
234178825Sdfrdigest_request(krb5_context context,
235178825Sdfr	       krb5_realm realm,
236178825Sdfr	       krb5_ccache ccache,
237178825Sdfr	       krb5_key_usage usage,
238178825Sdfr	       const DigestReqInner *ireq,
239178825Sdfr	       DigestRepInner *irep)
240178825Sdfr{
241178825Sdfr    DigestREQ req;
242178825Sdfr    DigestREP rep;
243178825Sdfr    krb5_error_code ret;
244178825Sdfr    krb5_data data, data2;
245178825Sdfr    size_t size;
246178825Sdfr    krb5_crypto crypto = NULL;
247178825Sdfr    krb5_auth_context ac = NULL;
248178825Sdfr    krb5_principal principal = NULL;
249178825Sdfr    krb5_ccache id = NULL;
250178825Sdfr    krb5_realm r = NULL;
251178825Sdfr
252178825Sdfr    krb5_data_zero(&data);
253178825Sdfr    krb5_data_zero(&data2);
254178825Sdfr    memset(&req, 0, sizeof(req));
255178825Sdfr    memset(&rep, 0, sizeof(rep));
256178825Sdfr
257178825Sdfr    if (ccache == NULL) {
258178825Sdfr	ret = krb5_cc_default(context, &id);
259178825Sdfr	if (ret)
260178825Sdfr	    goto out;
261178825Sdfr    } else
262178825Sdfr	id = ccache;
263178825Sdfr
264178825Sdfr    if (realm == NULL) {
265178825Sdfr	ret = krb5_get_default_realm(context, &r);
266178825Sdfr	if (ret)
267178825Sdfr	    goto out;
268178825Sdfr    } else
269178825Sdfr	r = realm;
270178825Sdfr
271178825Sdfr    /*
272178825Sdfr     *
273178825Sdfr     */
274178825Sdfr
275178825Sdfr    ret = krb5_make_principal(context, &principal,
276178825Sdfr			      r, KRB5_DIGEST_NAME, r, NULL);
277178825Sdfr    if (ret)
278178825Sdfr	goto out;
279178825Sdfr
280178825Sdfr    ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length,
281178825Sdfr		       ireq, &size, ret);
282178825Sdfr    if (ret) {
283178825Sdfr	krb5_set_error_string(context,
284178825Sdfr			      "Failed to encode digest inner request");
285178825Sdfr	goto out;
286178825Sdfr    }
287178825Sdfr    if (size != data.length)
288178825Sdfr	krb5_abortx(context, "ASN.1 internal encoder error");
289178825Sdfr
290178825Sdfr    ret = krb5_mk_req_exact(context, &ac,
291178825Sdfr			    AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED,
292178825Sdfr			    principal, NULL, id, &req.apReq);
293178825Sdfr    if (ret)
294178825Sdfr	goto out;
295178825Sdfr
296178825Sdfr    {
297178825Sdfr	krb5_keyblock *key;
298178825Sdfr
299178825Sdfr	ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
300178825Sdfr	if (ret)
301178825Sdfr	    goto out;
302178825Sdfr	if (key == NULL) {
303178825Sdfr	    krb5_set_error_string(context, "Digest failed to get local subkey");
304178825Sdfr	    ret = EINVAL;
305178825Sdfr	    goto out;
306178825Sdfr	}
307178825Sdfr
308178825Sdfr	ret = krb5_crypto_init(context, key, 0, &crypto);
309178825Sdfr	krb5_free_keyblock (context, key);
310178825Sdfr	if (ret)
311178825Sdfr	    goto out;
312178825Sdfr    }
313178825Sdfr
314178825Sdfr    ret = krb5_encrypt_EncryptedData(context, crypto, usage,
315178825Sdfr				     data.data, data.length, 0,
316178825Sdfr				     &req.innerReq);
317178825Sdfr    if (ret)
318178825Sdfr	goto out;
319178825Sdfr
320178825Sdfr    krb5_data_free(&data);
321178825Sdfr
322178825Sdfr    ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length,
323178825Sdfr		       &req, &size, ret);
324178825Sdfr    if (ret) {
325178825Sdfr	krb5_set_error_string(context, "Failed to encode DigestREQest");
326178825Sdfr	goto out;
327178825Sdfr    }
328178825Sdfr    if (size != data.length)
329178825Sdfr	krb5_abortx(context, "ASN.1 internal encoder error");
330178825Sdfr
331178825Sdfr    ret = krb5_sendto_kdc(context, &data, &r, &data2);
332178825Sdfr    if (ret)
333178825Sdfr	goto out;
334178825Sdfr
335178825Sdfr    ret = decode_DigestREP(data2.data, data2.length, &rep, NULL);
336178825Sdfr    if (ret) {
337178825Sdfr	krb5_set_error_string(context, "Failed to parse digest response");
338178825Sdfr	goto out;
339178825Sdfr    }
340178825Sdfr
341178825Sdfr    {
342178825Sdfr	krb5_ap_rep_enc_part *repl;
343178825Sdfr
344178825Sdfr	ret = krb5_rd_rep(context, ac, &rep.apRep, &repl);
345178825Sdfr	if (ret)
346178825Sdfr	    goto out;
347178825Sdfr
348178825Sdfr	krb5_free_ap_rep_enc_part(context, repl);
349178825Sdfr    }
350178825Sdfr    {
351178825Sdfr	krb5_keyblock *key;
352178825Sdfr
353178825Sdfr	ret = krb5_auth_con_getremotesubkey(context, ac, &key);
354178825Sdfr	if (ret)
355178825Sdfr	    goto out;
356178825Sdfr	if (key == NULL) {
357178825Sdfr	    ret = EINVAL;
358178825Sdfr	    krb5_set_error_string(context,
359178825Sdfr				  "Digest reply have no remote subkey");
360178825Sdfr	    goto out;
361178825Sdfr	}
362178825Sdfr
363178825Sdfr	krb5_crypto_destroy(context, crypto);
364178825Sdfr	ret = krb5_crypto_init(context, key, 0, &crypto);
365178825Sdfr	krb5_free_keyblock (context, key);
366178825Sdfr	if (ret)
367178825Sdfr	    goto out;
368178825Sdfr    }
369178825Sdfr
370178825Sdfr    krb5_data_free(&data);
371178825Sdfr    ret = krb5_decrypt_EncryptedData(context, crypto, usage,
372178825Sdfr				     &rep.innerRep, &data);
373178825Sdfr    if (ret)
374178825Sdfr	goto out;
375178825Sdfr
376178825Sdfr    ret = decode_DigestRepInner(data.data, data.length, irep, NULL);
377178825Sdfr    if (ret) {
378178825Sdfr	krb5_set_error_string(context, "Failed to decode digest inner reply");
379178825Sdfr	goto out;
380178825Sdfr    }
381178825Sdfr
382178825Sdfrout:
383178825Sdfr    if (ccache == NULL && id)
384178825Sdfr	krb5_cc_close(context, id);
385178825Sdfr    if (realm == NULL && r)
386178825Sdfr	free(r);
387178825Sdfr    if (crypto)
388178825Sdfr	krb5_crypto_destroy(context, crypto);
389178825Sdfr    if (ac)
390178825Sdfr	krb5_auth_con_free(context, ac);
391178825Sdfr    if (principal)
392178825Sdfr	krb5_free_principal(context, principal);
393178825Sdfr
394178825Sdfr    krb5_data_free(&data);
395178825Sdfr    krb5_data_free(&data2);
396178825Sdfr
397178825Sdfr    free_DigestREQ(&req);
398178825Sdfr    free_DigestREP(&rep);
399178825Sdfr
400178825Sdfr    return ret;
401178825Sdfr}
402178825Sdfr
403178825Sdfrkrb5_error_code
404178825Sdfrkrb5_digest_init_request(krb5_context context,
405178825Sdfr			 krb5_digest digest,
406178825Sdfr			 krb5_realm realm,
407178825Sdfr			 krb5_ccache ccache)
408178825Sdfr{
409178825Sdfr    DigestReqInner ireq;
410178825Sdfr    DigestRepInner irep;
411178825Sdfr    krb5_error_code ret;
412178825Sdfr
413178825Sdfr    memset(&ireq, 0, sizeof(ireq));
414178825Sdfr    memset(&irep, 0, sizeof(irep));
415178825Sdfr
416178825Sdfr    if (digest->init.type == NULL) {
417178825Sdfr	krb5_set_error_string(context, "Type missing from init req");
418178825Sdfr	return EINVAL;
419178825Sdfr    }
420178825Sdfr
421178825Sdfr    ireq.element = choice_DigestReqInner_init;
422178825Sdfr    ireq.u.init = digest->init;
423178825Sdfr
424178825Sdfr    ret = digest_request(context, realm, ccache,
425178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
426178825Sdfr    if (ret)
427178825Sdfr	goto out;
428178825Sdfr
429178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
430178825Sdfr	krb5_set_error_string(context, "Digest init error: %s",
431178825Sdfr			      irep.u.error.reason);
432178825Sdfr	ret = irep.u.error.code;
433178825Sdfr	goto out;
434178825Sdfr    }
435178825Sdfr
436178825Sdfr    if (irep.element != choice_DigestRepInner_initReply) {
437178825Sdfr	krb5_set_error_string(context, "digest reply not an initReply");
438178825Sdfr	ret = EINVAL;
439178825Sdfr	goto out;
440178825Sdfr    }
441178825Sdfr
442178825Sdfr    ret = copy_DigestInitReply(&irep.u.initReply, &digest->initReply);
443178825Sdfr    if (ret) {
444178825Sdfr	krb5_set_error_string(context, "Failed to copy initReply");
445178825Sdfr	goto out;
446178825Sdfr    }
447178825Sdfr
448178825Sdfrout:
449178825Sdfr    free_DigestRepInner(&irep);
450178825Sdfr
451178825Sdfr    return ret;
452178825Sdfr}
453178825Sdfr
454178825Sdfr
455178825Sdfrkrb5_error_code
456178825Sdfrkrb5_digest_set_client_nonce(krb5_context context,
457178825Sdfr			     krb5_digest digest,
458178825Sdfr			     const char *nonce)
459178825Sdfr{
460178825Sdfr    if (digest->request.clientNonce) {
461178825Sdfr	krb5_set_error_string(context, "clientNonce already set");
462178825Sdfr	return EINVAL;
463178825Sdfr    }
464178825Sdfr    digest->request.clientNonce =
465178825Sdfr	calloc(1, sizeof(*digest->request.clientNonce));
466178825Sdfr    if (digest->request.clientNonce == NULL) {
467178825Sdfr	krb5_set_error_string(context, "out of memory");
468178825Sdfr	return ENOMEM;
469178825Sdfr    }
470178825Sdfr    *digest->request.clientNonce = strdup(nonce);
471178825Sdfr    if (*digest->request.clientNonce == NULL) {
472178825Sdfr	krb5_set_error_string(context, "out of memory");
473178825Sdfr	free(digest->request.clientNonce);
474178825Sdfr	digest->request.clientNonce = NULL;
475178825Sdfr	return ENOMEM;
476178825Sdfr    }
477178825Sdfr    return 0;
478178825Sdfr}
479178825Sdfr
480178825Sdfrkrb5_error_code
481178825Sdfrkrb5_digest_set_digest(krb5_context context,
482178825Sdfr		       krb5_digest digest,
483178825Sdfr		       const char *dgst)
484178825Sdfr{
485178825Sdfr    if (digest->request.digest) {
486178825Sdfr	krb5_set_error_string(context, "digest already set");
487178825Sdfr	return EINVAL;
488178825Sdfr    }
489178825Sdfr    digest->request.digest = strdup(dgst);
490178825Sdfr    if (digest->request.digest == NULL) {
491178825Sdfr	krb5_set_error_string(context, "out of memory");
492178825Sdfr	return ENOMEM;
493178825Sdfr    }
494178825Sdfr    return 0;
495178825Sdfr}
496178825Sdfr
497178825Sdfrkrb5_error_code
498178825Sdfrkrb5_digest_set_username(krb5_context context,
499178825Sdfr			 krb5_digest digest,
500178825Sdfr			 const char *username)
501178825Sdfr{
502178825Sdfr    if (digest->request.username) {
503178825Sdfr	krb5_set_error_string(context, "username already set");
504178825Sdfr	return EINVAL;
505178825Sdfr    }
506178825Sdfr    digest->request.username = strdup(username);
507178825Sdfr    if (digest->request.username == NULL) {
508178825Sdfr	krb5_set_error_string(context, "out of memory");
509178825Sdfr	return ENOMEM;
510178825Sdfr    }
511178825Sdfr    return 0;
512178825Sdfr}
513178825Sdfr
514178825Sdfrkrb5_error_code
515178825Sdfrkrb5_digest_set_authid(krb5_context context,
516178825Sdfr		       krb5_digest digest,
517178825Sdfr		       const char *authid)
518178825Sdfr{
519178825Sdfr    if (digest->request.authid) {
520178825Sdfr	krb5_set_error_string(context, "authid already set");
521178825Sdfr	return EINVAL;
522178825Sdfr    }
523178825Sdfr    digest->request.authid = malloc(sizeof(*digest->request.authid));
524178825Sdfr    if (digest->request.authid == NULL) {
525178825Sdfr	krb5_set_error_string(context, "out of memory");
526178825Sdfr	return ENOMEM;
527178825Sdfr    }
528178825Sdfr    *digest->request.authid = strdup(authid);
529178825Sdfr    if (*digest->request.authid == NULL) {
530178825Sdfr	krb5_set_error_string(context, "out of memory");
531178825Sdfr	free(digest->request.authid);
532178825Sdfr	digest->request.authid = NULL;
533178825Sdfr	return ENOMEM;
534178825Sdfr    }
535178825Sdfr    return 0;
536178825Sdfr}
537178825Sdfr
538178825Sdfrkrb5_error_code
539178825Sdfrkrb5_digest_set_authentication_user(krb5_context context,
540178825Sdfr				    krb5_digest digest,
541178825Sdfr				    krb5_principal authentication_user)
542178825Sdfr{
543178825Sdfr    krb5_error_code ret;
544178825Sdfr
545178825Sdfr    if (digest->request.authentication_user) {
546178825Sdfr	krb5_set_error_string(context, "authentication_user already set");
547178825Sdfr	return EINVAL;
548178825Sdfr    }
549178825Sdfr    ret = krb5_copy_principal(context,
550178825Sdfr			      authentication_user,
551178825Sdfr			      &digest->request.authentication_user);
552178825Sdfr    if (digest->request.authentication_user == NULL) {
553178825Sdfr	krb5_set_error_string(context, "out of memory");
554178825Sdfr	return ENOMEM;
555178825Sdfr    }
556178825Sdfr    return 0;
557178825Sdfr}
558178825Sdfr
559178825Sdfrkrb5_error_code
560178825Sdfrkrb5_digest_set_realm(krb5_context context,
561178825Sdfr		      krb5_digest digest,
562178825Sdfr		      const char *realm)
563178825Sdfr{
564178825Sdfr    if (digest->request.realm) {
565178825Sdfr	krb5_set_error_string(context, "realm already set");
566178825Sdfr	return EINVAL;
567178825Sdfr    }
568178825Sdfr    digest->request.realm = malloc(sizeof(*digest->request.realm));
569178825Sdfr    if (digest->request.realm == NULL) {
570178825Sdfr	krb5_set_error_string(context, "out of memory");
571178825Sdfr	return ENOMEM;
572178825Sdfr    }
573178825Sdfr    *digest->request.realm = strdup(realm);
574178825Sdfr    if (*digest->request.realm == NULL) {
575178825Sdfr	krb5_set_error_string(context, "out of memory");
576178825Sdfr	free(digest->request.realm);
577178825Sdfr	digest->request.realm = NULL;
578178825Sdfr	return ENOMEM;
579178825Sdfr    }
580178825Sdfr    return 0;
581178825Sdfr}
582178825Sdfr
583178825Sdfrkrb5_error_code
584178825Sdfrkrb5_digest_set_method(krb5_context context,
585178825Sdfr		       krb5_digest digest,
586178825Sdfr		       const char *method)
587178825Sdfr{
588178825Sdfr    if (digest->request.method) {
589178825Sdfr	krb5_set_error_string(context, "method already set");
590178825Sdfr	return EINVAL;
591178825Sdfr    }
592178825Sdfr    digest->request.method = malloc(sizeof(*digest->request.method));
593178825Sdfr    if (digest->request.method == NULL) {
594178825Sdfr	krb5_set_error_string(context, "out of memory");
595178825Sdfr	return ENOMEM;
596178825Sdfr    }
597178825Sdfr    *digest->request.method = strdup(method);
598178825Sdfr    if (*digest->request.method == NULL) {
599178825Sdfr	krb5_set_error_string(context, "out of memory");
600178825Sdfr	free(digest->request.method);
601178825Sdfr	digest->request.method = NULL;
602178825Sdfr	return ENOMEM;
603178825Sdfr    }
604178825Sdfr    return 0;
605178825Sdfr}
606178825Sdfr
607178825Sdfrkrb5_error_code
608178825Sdfrkrb5_digest_set_uri(krb5_context context,
609178825Sdfr		    krb5_digest digest,
610178825Sdfr		    const char *uri)
611178825Sdfr{
612178825Sdfr    if (digest->request.uri) {
613178825Sdfr	krb5_set_error_string(context, "uri already set");
614178825Sdfr	return EINVAL;
615178825Sdfr    }
616178825Sdfr    digest->request.uri = malloc(sizeof(*digest->request.uri));
617178825Sdfr    if (digest->request.uri == NULL) {
618178825Sdfr	krb5_set_error_string(context, "out of memory");
619178825Sdfr	return ENOMEM;
620178825Sdfr    }
621178825Sdfr    *digest->request.uri = strdup(uri);
622178825Sdfr    if (*digest->request.uri == NULL) {
623178825Sdfr	krb5_set_error_string(context, "out of memory");
624178825Sdfr	free(digest->request.uri);
625178825Sdfr	digest->request.uri = NULL;
626178825Sdfr	return ENOMEM;
627178825Sdfr    }
628178825Sdfr    return 0;
629178825Sdfr}
630178825Sdfr
631178825Sdfrkrb5_error_code
632178825Sdfrkrb5_digest_set_nonceCount(krb5_context context,
633178825Sdfr			   krb5_digest digest,
634178825Sdfr			   const char *nonce_count)
635178825Sdfr{
636178825Sdfr    if (digest->request.nonceCount) {
637178825Sdfr	krb5_set_error_string(context, "nonceCount already set");
638178825Sdfr	return EINVAL;
639178825Sdfr    }
640178825Sdfr    digest->request.nonceCount =
641178825Sdfr	malloc(sizeof(*digest->request.nonceCount));
642178825Sdfr    if (digest->request.nonceCount == NULL) {
643178825Sdfr	krb5_set_error_string(context, "out of memory");
644178825Sdfr	return ENOMEM;
645178825Sdfr    }
646178825Sdfr    *digest->request.nonceCount = strdup(nonce_count);
647178825Sdfr    if (*digest->request.nonceCount == NULL) {
648178825Sdfr	krb5_set_error_string(context, "out of memory");
649178825Sdfr	free(digest->request.nonceCount);
650178825Sdfr	digest->request.nonceCount = NULL;
651178825Sdfr	return ENOMEM;
652178825Sdfr    }
653178825Sdfr    return 0;
654178825Sdfr}
655178825Sdfr
656178825Sdfrkrb5_error_code
657178825Sdfrkrb5_digest_set_qop(krb5_context context,
658178825Sdfr		    krb5_digest digest,
659178825Sdfr		    const char *qop)
660178825Sdfr{
661178825Sdfr    if (digest->request.qop) {
662178825Sdfr	krb5_set_error_string(context, "qop already set");
663178825Sdfr	return EINVAL;
664178825Sdfr    }
665178825Sdfr    digest->request.qop = malloc(sizeof(*digest->request.qop));
666178825Sdfr    if (digest->request.qop == NULL) {
667178825Sdfr	krb5_set_error_string(context, "out of memory");
668178825Sdfr	return ENOMEM;
669178825Sdfr    }
670178825Sdfr    *digest->request.qop = strdup(qop);
671178825Sdfr    if (*digest->request.qop == NULL) {
672178825Sdfr	krb5_set_error_string(context, "out of memory");
673178825Sdfr	free(digest->request.qop);
674178825Sdfr	digest->request.qop = NULL;
675178825Sdfr	return ENOMEM;
676178825Sdfr    }
677178825Sdfr    return 0;
678178825Sdfr}
679178825Sdfr
680178825Sdfrint
681178825Sdfrkrb5_digest_set_responseData(krb5_context context,
682178825Sdfr			     krb5_digest digest,
683178825Sdfr			     const char *response)
684178825Sdfr{
685178825Sdfr    digest->request.responseData = strdup(response);
686178825Sdfr    if (digest->request.responseData == NULL) {
687178825Sdfr	krb5_set_error_string(context, "out of memory");
688178825Sdfr	return ENOMEM;
689178825Sdfr    }
690178825Sdfr    return 0;
691178825Sdfr}
692178825Sdfr
693178825Sdfrkrb5_error_code
694178825Sdfrkrb5_digest_request(krb5_context context,
695178825Sdfr		    krb5_digest digest,
696178825Sdfr		    krb5_realm realm,
697178825Sdfr		    krb5_ccache ccache)
698178825Sdfr{
699178825Sdfr    DigestReqInner ireq;
700178825Sdfr    DigestRepInner irep;
701178825Sdfr    krb5_error_code ret;
702178825Sdfr
703178825Sdfr    memset(&ireq, 0, sizeof(ireq));
704178825Sdfr    memset(&irep, 0, sizeof(irep));
705178825Sdfr
706178825Sdfr    ireq.element = choice_DigestReqInner_digestRequest;
707178825Sdfr    ireq.u.digestRequest = digest->request;
708178825Sdfr
709178825Sdfr    if (digest->request.type == NULL) {
710178825Sdfr	if (digest->init.type == NULL) {
711178825Sdfr	    krb5_set_error_string(context, "Type missing from req");
712178825Sdfr	    return EINVAL;
713178825Sdfr	}
714178825Sdfr	ireq.u.digestRequest.type = digest->init.type;
715178825Sdfr    }
716178825Sdfr
717178825Sdfr    if (ireq.u.digestRequest.digest == NULL)
718178825Sdfr	ireq.u.digestRequest.digest = "md5";
719178825Sdfr
720178825Sdfr    ret = digest_request(context, realm, ccache,
721178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
722178825Sdfr    if (ret)
723178825Sdfr	return ret;
724178825Sdfr
725178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
726178825Sdfr	krb5_set_error_string(context, "Digest response error: %s",
727178825Sdfr			      irep.u.error.reason);
728178825Sdfr	ret = irep.u.error.code;
729178825Sdfr	goto out;
730178825Sdfr    }
731178825Sdfr
732178825Sdfr    if (irep.element != choice_DigestRepInner_response) {
733178825Sdfr	krb5_set_error_string(context, "digest reply not an DigestResponse");
734178825Sdfr	ret = EINVAL;
735178825Sdfr	goto out;
736178825Sdfr    }
737178825Sdfr
738178825Sdfr    ret = copy_DigestResponse(&irep.u.response, &digest->response);
739178825Sdfr    if (ret) {
740178825Sdfr	krb5_set_error_string(context, "Failed to copy initReply");
741178825Sdfr	goto out;
742178825Sdfr    }
743178825Sdfr
744178825Sdfrout:
745178825Sdfr    free_DigestRepInner(&irep);
746178825Sdfr
747178825Sdfr    return ret;
748178825Sdfr}
749178825Sdfr
750178825Sdfrkrb5_boolean
751178825Sdfrkrb5_digest_rep_get_status(krb5_context context,
752178825Sdfr			   krb5_digest digest)
753178825Sdfr{
754178825Sdfr    return digest->response.success ? TRUE : FALSE;
755178825Sdfr}
756178825Sdfr
757178825Sdfrconst char *
758178825Sdfrkrb5_digest_get_rsp(krb5_context context,
759178825Sdfr		    krb5_digest digest)
760178825Sdfr{
761178825Sdfr    if (digest->response.rsp == NULL)
762178825Sdfr	return NULL;
763178825Sdfr    return *digest->response.rsp;
764178825Sdfr}
765178825Sdfr
766178825Sdfrkrb5_error_code
767178825Sdfrkrb5_digest_get_tickets(krb5_context context,
768178825Sdfr			krb5_digest digest,
769178825Sdfr			Ticket **tickets)
770178825Sdfr{
771178825Sdfr    *tickets = NULL;
772178825Sdfr    return 0;
773178825Sdfr}
774178825Sdfr
775178825Sdfr
776178825Sdfrkrb5_error_code
777178825Sdfrkrb5_digest_get_client_binding(krb5_context context,
778178825Sdfr			       krb5_digest digest,
779178825Sdfr			       char **type,
780178825Sdfr			       char **binding)
781178825Sdfr{
782178825Sdfr    if (digest->response.channel) {
783178825Sdfr	*type = strdup(digest->response.channel->cb_type);
784178825Sdfr	*binding = strdup(digest->response.channel->cb_binding);
785178825Sdfr	if (*type == NULL || *binding == NULL) {
786178825Sdfr	    free(*type);
787178825Sdfr	    free(*binding);
788178825Sdfr	    krb5_set_error_string(context, "out of memory");
789178825Sdfr	    return ENOMEM;
790178825Sdfr	}
791178825Sdfr    } else {
792178825Sdfr	*type = NULL;
793178825Sdfr	*binding = NULL;
794178825Sdfr    }
795178825Sdfr    return 0;
796178825Sdfr}
797178825Sdfr
798178825Sdfrkrb5_error_code
799178825Sdfrkrb5_digest_get_session_key(krb5_context context,
800178825Sdfr			    krb5_digest digest,
801178825Sdfr			    krb5_data *data)
802178825Sdfr{
803178825Sdfr    krb5_error_code ret;
804178825Sdfr
805178825Sdfr    krb5_data_zero(data);
806178825Sdfr    if (digest->response.session_key == NULL)
807178825Sdfr	return 0;
808178825Sdfr    ret = der_copy_octet_string(digest->response.session_key, data);
809178825Sdfr    if (ret)
810178825Sdfr	krb5_clear_error_string(context);
811178825Sdfr
812178825Sdfr    return ret;
813178825Sdfr}
814178825Sdfr
815178825Sdfrstruct krb5_ntlm_data {
816178825Sdfr    NTLMInit init;
817178825Sdfr    NTLMInitReply initReply;
818178825Sdfr    NTLMRequest request;
819178825Sdfr    NTLMResponse response;
820178825Sdfr};
821178825Sdfr
822178825Sdfrkrb5_error_code
823178825Sdfrkrb5_ntlm_alloc(krb5_context context,
824178825Sdfr		krb5_ntlm *ntlm)
825178825Sdfr{
826178825Sdfr    *ntlm = calloc(1, sizeof(**ntlm));
827178825Sdfr    if (*ntlm == NULL) {
828178825Sdfr	krb5_set_error_string(context, "out of memory");
829178825Sdfr	return ENOMEM;
830178825Sdfr    }
831178825Sdfr    return 0;
832178825Sdfr}
833178825Sdfr
834178825Sdfrkrb5_error_code
835178825Sdfrkrb5_ntlm_free(krb5_context context, krb5_ntlm ntlm)
836178825Sdfr{
837178825Sdfr    free_NTLMInit(&ntlm->init);
838178825Sdfr    free_NTLMInitReply(&ntlm->initReply);
839178825Sdfr    free_NTLMRequest(&ntlm->request);
840178825Sdfr    free_NTLMResponse(&ntlm->response);
841178825Sdfr    memset(ntlm, 0, sizeof(*ntlm));
842178825Sdfr    free(ntlm);
843178825Sdfr    return 0;
844178825Sdfr}
845178825Sdfr
846178825Sdfr
847178825Sdfrkrb5_error_code
848178825Sdfrkrb5_ntlm_init_request(krb5_context context,
849178825Sdfr		       krb5_ntlm ntlm,
850178825Sdfr		       krb5_realm realm,
851178825Sdfr		       krb5_ccache ccache,
852178825Sdfr		       uint32_t flags,
853178825Sdfr		       const char *hostname,
854178825Sdfr		       const char *domainname)
855178825Sdfr{
856178825Sdfr    DigestReqInner ireq;
857178825Sdfr    DigestRepInner irep;
858178825Sdfr    krb5_error_code ret;
859178825Sdfr
860178825Sdfr    memset(&ireq, 0, sizeof(ireq));
861178825Sdfr    memset(&irep, 0, sizeof(irep));
862178825Sdfr
863178825Sdfr    ntlm->init.flags = flags;
864178825Sdfr    if (hostname) {
865178825Sdfr	ALLOC(ntlm->init.hostname, 1);
866178825Sdfr	*ntlm->init.hostname = strdup(hostname);
867178825Sdfr    }
868178825Sdfr    if (domainname) {
869178825Sdfr	ALLOC(ntlm->init.domain, 1);
870178825Sdfr	*ntlm->init.domain = strdup(domainname);
871178825Sdfr    }
872178825Sdfr
873178825Sdfr    ireq.element = choice_DigestReqInner_ntlmInit;
874178825Sdfr    ireq.u.ntlmInit = ntlm->init;
875178825Sdfr
876178825Sdfr    ret = digest_request(context, realm, ccache,
877178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
878178825Sdfr    if (ret)
879178825Sdfr	goto out;
880178825Sdfr
881178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
882178825Sdfr	krb5_set_error_string(context, "Digest init error: %s",
883178825Sdfr			      irep.u.error.reason);
884178825Sdfr	ret = irep.u.error.code;
885178825Sdfr	goto out;
886178825Sdfr    }
887178825Sdfr
888178825Sdfr    if (irep.element != choice_DigestRepInner_ntlmInitReply) {
889178825Sdfr	krb5_set_error_string(context, "ntlm reply not an initReply");
890178825Sdfr	ret = EINVAL;
891178825Sdfr	goto out;
892178825Sdfr    }
893178825Sdfr
894178825Sdfr    ret = copy_NTLMInitReply(&irep.u.ntlmInitReply, &ntlm->initReply);
895178825Sdfr    if (ret) {
896178825Sdfr	krb5_set_error_string(context, "Failed to copy initReply");
897178825Sdfr	goto out;
898178825Sdfr    }
899178825Sdfr
900178825Sdfrout:
901178825Sdfr    free_DigestRepInner(&irep);
902178825Sdfr
903178825Sdfr    return ret;
904178825Sdfr}
905178825Sdfr
906178825Sdfrkrb5_error_code
907178825Sdfrkrb5_ntlm_init_get_flags(krb5_context context,
908178825Sdfr			 krb5_ntlm ntlm,
909178825Sdfr			 uint32_t *flags)
910178825Sdfr{
911178825Sdfr    *flags = ntlm->initReply.flags;
912178825Sdfr    return 0;
913178825Sdfr}
914178825Sdfr
915178825Sdfrkrb5_error_code
916178825Sdfrkrb5_ntlm_init_get_challange(krb5_context context,
917178825Sdfr			     krb5_ntlm ntlm,
918178825Sdfr			     krb5_data *challange)
919178825Sdfr{
920178825Sdfr    krb5_error_code ret;
921178825Sdfr
922178825Sdfr    ret = der_copy_octet_string(&ntlm->initReply.challange, challange);
923178825Sdfr    if (ret)
924178825Sdfr	krb5_clear_error_string(context);
925178825Sdfr
926178825Sdfr    return ret;
927178825Sdfr}
928178825Sdfr
929178825Sdfrkrb5_error_code
930178825Sdfrkrb5_ntlm_init_get_opaque(krb5_context context,
931178825Sdfr			  krb5_ntlm ntlm,
932178825Sdfr			  krb5_data *opaque)
933178825Sdfr{
934178825Sdfr    krb5_error_code ret;
935178825Sdfr
936178825Sdfr    ret = der_copy_octet_string(&ntlm->initReply.opaque, opaque);
937178825Sdfr    if (ret)
938178825Sdfr	krb5_clear_error_string(context);
939178825Sdfr
940178825Sdfr    return ret;
941178825Sdfr}
942178825Sdfr
943178825Sdfrkrb5_error_code
944178825Sdfrkrb5_ntlm_init_get_targetname(krb5_context context,
945178825Sdfr			      krb5_ntlm ntlm,
946178825Sdfr			      char **name)
947178825Sdfr{
948178825Sdfr    *name = strdup(ntlm->initReply.targetname);
949178825Sdfr    if (*name == NULL) {
950178825Sdfr	krb5_clear_error_string(context);
951178825Sdfr	return ENOMEM;
952178825Sdfr    }
953178825Sdfr    return 0;
954178825Sdfr}
955178825Sdfr
956178825Sdfrkrb5_error_code
957178825Sdfrkrb5_ntlm_init_get_targetinfo(krb5_context context,
958178825Sdfr			      krb5_ntlm ntlm,
959178825Sdfr			      krb5_data *data)
960178825Sdfr{
961178825Sdfr    krb5_error_code ret;
962178825Sdfr
963178825Sdfr    if (ntlm->initReply.targetinfo == NULL) {
964178825Sdfr	krb5_data_zero(data);
965178825Sdfr	return 0;
966178825Sdfr    }
967178825Sdfr
968178825Sdfr    ret = krb5_data_copy(data,
969178825Sdfr			 ntlm->initReply.targetinfo->data,
970178825Sdfr			 ntlm->initReply.targetinfo->length);
971178825Sdfr    if (ret) {
972178825Sdfr	krb5_clear_error_string(context);
973178825Sdfr	return ret;
974178825Sdfr    }
975178825Sdfr    return 0;
976178825Sdfr}
977178825Sdfr
978178825Sdfr
979178825Sdfrkrb5_error_code
980178825Sdfrkrb5_ntlm_request(krb5_context context,
981178825Sdfr		  krb5_ntlm ntlm,
982178825Sdfr		  krb5_realm realm,
983178825Sdfr		  krb5_ccache ccache)
984178825Sdfr{
985178825Sdfr    DigestReqInner ireq;
986178825Sdfr    DigestRepInner irep;
987178825Sdfr    krb5_error_code ret;
988178825Sdfr
989178825Sdfr    memset(&ireq, 0, sizeof(ireq));
990178825Sdfr    memset(&irep, 0, sizeof(irep));
991178825Sdfr
992178825Sdfr    ireq.element = choice_DigestReqInner_ntlmRequest;
993178825Sdfr    ireq.u.ntlmRequest = ntlm->request;
994178825Sdfr
995178825Sdfr    ret = digest_request(context, realm, ccache,
996178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
997178825Sdfr    if (ret)
998178825Sdfr	return ret;
999178825Sdfr
1000178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
1001178825Sdfr	krb5_set_error_string(context, "NTLM response error: %s",
1002178825Sdfr			      irep.u.error.reason);
1003178825Sdfr	ret = irep.u.error.code;
1004178825Sdfr	goto out;
1005178825Sdfr    }
1006178825Sdfr
1007178825Sdfr    if (irep.element != choice_DigestRepInner_ntlmResponse) {
1008178825Sdfr	krb5_set_error_string(context, "NTLM reply not an NTLMResponse");
1009178825Sdfr	ret = EINVAL;
1010178825Sdfr	goto out;
1011178825Sdfr    }
1012178825Sdfr
1013178825Sdfr    ret = copy_NTLMResponse(&irep.u.ntlmResponse, &ntlm->response);
1014178825Sdfr    if (ret) {
1015178825Sdfr	krb5_set_error_string(context, "Failed to copy NTLMResponse");
1016178825Sdfr	goto out;
1017178825Sdfr    }
1018178825Sdfr
1019178825Sdfrout:
1020178825Sdfr    free_DigestRepInner(&irep);
1021178825Sdfr
1022178825Sdfr    return ret;
1023178825Sdfr}
1024178825Sdfr
1025178825Sdfrkrb5_error_code
1026178825Sdfrkrb5_ntlm_req_set_flags(krb5_context context,
1027178825Sdfr			krb5_ntlm ntlm,
1028178825Sdfr			uint32_t flags)
1029178825Sdfr{
1030178825Sdfr    ntlm->request.flags = flags;
1031178825Sdfr    return 0;
1032178825Sdfr}
1033178825Sdfr
1034178825Sdfrkrb5_error_code
1035178825Sdfrkrb5_ntlm_req_set_username(krb5_context context,
1036178825Sdfr			   krb5_ntlm ntlm,
1037178825Sdfr			   const char *username)
1038178825Sdfr{
1039178825Sdfr    ntlm->request.username = strdup(username);
1040178825Sdfr    if (ntlm->request.username == NULL) {
1041178825Sdfr	krb5_set_error_string(context, "out of memory");
1042178825Sdfr	return ENOMEM;
1043178825Sdfr    }
1044178825Sdfr    return 0;
1045178825Sdfr}
1046178825Sdfr
1047178825Sdfrkrb5_error_code
1048178825Sdfrkrb5_ntlm_req_set_targetname(krb5_context context,
1049178825Sdfr			     krb5_ntlm ntlm,
1050178825Sdfr			     const char *targetname)
1051178825Sdfr{
1052178825Sdfr    ntlm->request.targetname = strdup(targetname);
1053178825Sdfr    if (ntlm->request.targetname == NULL) {
1054178825Sdfr	krb5_set_error_string(context, "out of memory");
1055178825Sdfr	return ENOMEM;
1056178825Sdfr    }
1057178825Sdfr    return 0;
1058178825Sdfr}
1059178825Sdfr
1060178825Sdfrkrb5_error_code
1061178825Sdfrkrb5_ntlm_req_set_lm(krb5_context context,
1062178825Sdfr		     krb5_ntlm ntlm,
1063178825Sdfr		     void *hash, size_t len)
1064178825Sdfr{
1065178825Sdfr    ntlm->request.lm.data = malloc(len);
1066178825Sdfr    if (ntlm->request.lm.data == NULL) {
1067178825Sdfr	krb5_set_error_string(context, "out of memory");
1068178825Sdfr	return ENOMEM;
1069178825Sdfr    }
1070178825Sdfr    ntlm->request.lm.length = len;
1071178825Sdfr    memcpy(ntlm->request.lm.data, hash, len);
1072178825Sdfr    return 0;
1073178825Sdfr}
1074178825Sdfr
1075178825Sdfrkrb5_error_code
1076178825Sdfrkrb5_ntlm_req_set_ntlm(krb5_context context,
1077178825Sdfr		       krb5_ntlm ntlm,
1078178825Sdfr		       void *hash, size_t len)
1079178825Sdfr{
1080178825Sdfr    ntlm->request.ntlm.data = malloc(len);
1081178825Sdfr    if (ntlm->request.ntlm.data == NULL) {
1082178825Sdfr	krb5_set_error_string(context, "out of memory");
1083178825Sdfr	return ENOMEM;
1084178825Sdfr    }
1085178825Sdfr    ntlm->request.ntlm.length = len;
1086178825Sdfr    memcpy(ntlm->request.ntlm.data, hash, len);
1087178825Sdfr    return 0;
1088178825Sdfr}
1089178825Sdfr
1090178825Sdfrkrb5_error_code
1091178825Sdfrkrb5_ntlm_req_set_opaque(krb5_context context,
1092178825Sdfr			 krb5_ntlm ntlm,
1093178825Sdfr			 krb5_data *opaque)
1094178825Sdfr{
1095178825Sdfr    ntlm->request.opaque.data = malloc(opaque->length);
1096178825Sdfr    if (ntlm->request.opaque.data == NULL) {
1097178825Sdfr	krb5_set_error_string(context, "out of memory");
1098178825Sdfr	return ENOMEM;
1099178825Sdfr    }
1100178825Sdfr    ntlm->request.opaque.length = opaque->length;
1101178825Sdfr    memcpy(ntlm->request.opaque.data, opaque->data, opaque->length);
1102178825Sdfr    return 0;
1103178825Sdfr}
1104178825Sdfr
1105178825Sdfrkrb5_error_code
1106178825Sdfrkrb5_ntlm_req_set_session(krb5_context context,
1107178825Sdfr			  krb5_ntlm ntlm,
1108178825Sdfr			  void *sessionkey, size_t length)
1109178825Sdfr{
1110178825Sdfr    ntlm->request.sessionkey = calloc(1, sizeof(*ntlm->request.sessionkey));
1111178825Sdfr    if (ntlm->request.sessionkey == NULL) {
1112178825Sdfr	krb5_set_error_string(context, "out of memory");
1113178825Sdfr	return ENOMEM;
1114178825Sdfr    }
1115178825Sdfr    ntlm->request.sessionkey->data = malloc(length);
1116178825Sdfr    if (ntlm->request.sessionkey->data == NULL) {
1117178825Sdfr	krb5_set_error_string(context, "out of memory");
1118178825Sdfr	return ENOMEM;
1119178825Sdfr    }
1120178825Sdfr    memcpy(ntlm->request.sessionkey->data, sessionkey, length);
1121178825Sdfr    ntlm->request.sessionkey->length = length;
1122178825Sdfr    return 0;
1123178825Sdfr}
1124178825Sdfr
1125178825Sdfrkrb5_boolean
1126178825Sdfrkrb5_ntlm_rep_get_status(krb5_context context,
1127178825Sdfr			 krb5_ntlm ntlm)
1128178825Sdfr{
1129178825Sdfr    return ntlm->response.success ? TRUE : FALSE;
1130178825Sdfr}
1131178825Sdfr
1132178825Sdfrkrb5_error_code
1133178825Sdfrkrb5_ntlm_rep_get_sessionkey(krb5_context context,
1134178825Sdfr			     krb5_ntlm ntlm,
1135178825Sdfr			     krb5_data *data)
1136178825Sdfr{
1137178825Sdfr    if (ntlm->response.sessionkey == NULL) {
1138178825Sdfr	krb5_set_error_string(context, "no ntlm session key");
1139178825Sdfr	return EINVAL;
1140178825Sdfr    }
1141178825Sdfr    krb5_clear_error_string(context);
1142178825Sdfr    return krb5_data_copy(data,
1143178825Sdfr			  ntlm->response.sessionkey->data,
1144178825Sdfr			  ntlm->response.sessionkey->length);
1145178825Sdfr}
1146178825Sdfr
1147178825Sdfr/**
1148178825Sdfr * Get the supported/allowed mechanism for this principal.
1149178825Sdfr *
1150178825Sdfr * @param context A Keberos context.
1151178825Sdfr * @param realm The realm of the KDC.
1152178825Sdfr * @param ccache The credential cache to use when talking to the KDC.
1153178825Sdfr * @param flags The supported mechanism.
1154178825Sdfr *
1155178825Sdfr * @return Return an error code or 0.
1156178825Sdfr *
1157178825Sdfr * @ingroup krb5_digest
1158178825Sdfr */
1159178825Sdfr
1160178825Sdfrkrb5_error_code
1161178825Sdfrkrb5_digest_probe(krb5_context context,
1162178825Sdfr		  krb5_realm realm,
1163178825Sdfr		  krb5_ccache ccache,
1164178825Sdfr		  unsigned *flags)
1165178825Sdfr{
1166178825Sdfr    DigestReqInner ireq;
1167178825Sdfr    DigestRepInner irep;
1168178825Sdfr    krb5_error_code ret;
1169178825Sdfr
1170178825Sdfr    memset(&ireq, 0, sizeof(ireq));
1171178825Sdfr    memset(&irep, 0, sizeof(irep));
1172178825Sdfr
1173178825Sdfr    ireq.element = choice_DigestReqInner_supportedMechs;
1174178825Sdfr
1175178825Sdfr    ret = digest_request(context, realm, ccache,
1176178825Sdfr			 KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep);
1177178825Sdfr    if (ret)
1178178825Sdfr	goto out;
1179178825Sdfr
1180178825Sdfr    if (irep.element == choice_DigestRepInner_error) {
1181178825Sdfr	krb5_set_error_string(context, "Digest probe error: %s",
1182178825Sdfr			      irep.u.error.reason);
1183178825Sdfr	ret = irep.u.error.code;
1184178825Sdfr	goto out;
1185178825Sdfr    }
1186178825Sdfr
1187178825Sdfr    if (irep.element != choice_DigestRepInner_supportedMechs) {
1188178825Sdfr	krb5_set_error_string(context, "Digest reply not an probe");
1189178825Sdfr	ret = EINVAL;
1190178825Sdfr	goto out;
1191178825Sdfr    }
1192178825Sdfr
1193178825Sdfr    *flags = DigestTypes2int(irep.u.supportedMechs);
1194178825Sdfr
1195178825Sdfrout:
1196178825Sdfr    free_DigestRepInner(&irep);
1197178825Sdfr
1198178825Sdfr    return ret;
1199178825Sdfr}
1200