1178825Sdfr/*
2178825Sdfr * Copyright (c) 2006 - 2007 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 "ntlm/ntlm.h"
35178825Sdfr
36178825SdfrRCSID("$Id: digest.c 22169 2007-12-04 22:19:16Z lha $");
37178825Sdfr
38178825Sdfr/*
39178825Sdfr *
40178825Sdfr */
41178825Sdfr
42178825Sdfrstruct ntlmkrb5 {
43178825Sdfr    krb5_context context;
44178825Sdfr    krb5_ntlm ntlm;
45178825Sdfr    krb5_realm kerberos_realm;
46178825Sdfr    krb5_ccache id;
47178825Sdfr    krb5_data opaque;
48178825Sdfr    int destroy;
49178825Sdfr    OM_uint32 flags;
50178825Sdfr    struct ntlm_buf key;
51178825Sdfr    krb5_data sessionkey;
52178825Sdfr};
53178825Sdfr
54178825Sdfrstatic OM_uint32 kdc_destroy(OM_uint32 *, void *);
55178825Sdfr
56178825Sdfr/*
57178825Sdfr * Get credential cache that the ntlm code can use to talk to the KDC
58178825Sdfr * using the digest API.
59178825Sdfr */
60178825Sdfr
61178825Sdfrstatic krb5_error_code
62178825Sdfrget_ccache(krb5_context context, int *destroy, krb5_ccache *id)
63178825Sdfr{
64178825Sdfr    krb5_principal principal = NULL;
65178825Sdfr    krb5_error_code ret;
66178825Sdfr    krb5_keytab kt = NULL;
67178825Sdfr
68178825Sdfr    *id = NULL;
69178825Sdfr
70178825Sdfr    if (!issuid()) {
71178825Sdfr	const char *cache;
72178825Sdfr
73178825Sdfr	cache = getenv("NTLM_ACCEPTOR_CCACHE");
74178825Sdfr	if (cache) {
75178825Sdfr	    ret = krb5_cc_resolve(context, cache, id);
76178825Sdfr	    if (ret)
77178825Sdfr		goto out;
78178825Sdfr	    return 0;
79178825Sdfr	}
80178825Sdfr    }
81178825Sdfr
82178825Sdfr    ret = krb5_sname_to_principal(context, NULL, "host",
83178825Sdfr				  KRB5_NT_SRV_HST, &principal);
84178825Sdfr    if (ret)
85178825Sdfr	goto out;
86178825Sdfr
87178825Sdfr    ret = krb5_cc_cache_match(context, principal, NULL, id);
88178825Sdfr    if (ret == 0)
89178825Sdfr	return 0;
90178825Sdfr
91178825Sdfr    /* did not find in default credcache, lets try default keytab */
92178825Sdfr    ret = krb5_kt_default(context, &kt);
93178825Sdfr    if (ret)
94178825Sdfr	goto out;
95178825Sdfr
96178825Sdfr    /* XXX check in keytab */
97178825Sdfr    {
98178825Sdfr	krb5_get_init_creds_opt *opt;
99178825Sdfr	krb5_creds cred;
100178825Sdfr
101178825Sdfr	memset(&cred, 0, sizeof(cred));
102178825Sdfr
103178825Sdfr	ret = krb5_cc_new_unique(context, "MEMORY", NULL, id);
104178825Sdfr	if (ret)
105178825Sdfr	    goto out;
106178825Sdfr	*destroy = 1;
107178825Sdfr	ret = krb5_get_init_creds_opt_alloc(context, &opt);
108178825Sdfr	if (ret)
109178825Sdfr	    goto out;
110178825Sdfr	ret = krb5_get_init_creds_keytab (context,
111178825Sdfr					  &cred,
112178825Sdfr					  principal,
113178825Sdfr					  kt,
114178825Sdfr					  0,
115178825Sdfr					  NULL,
116178825Sdfr					  opt);
117178825Sdfr	krb5_get_init_creds_opt_free(context, opt);
118178825Sdfr	if (ret)
119178825Sdfr	    goto out;
120178825Sdfr	ret = krb5_cc_initialize (context, *id, cred.client);
121178825Sdfr	if (ret) {
122178825Sdfr	    krb5_free_cred_contents (context, &cred);
123178825Sdfr	    goto out;
124178825Sdfr	}
125178825Sdfr	ret = krb5_cc_store_cred (context, *id, &cred);
126178825Sdfr	krb5_free_cred_contents (context, &cred);
127178825Sdfr	if (ret)
128178825Sdfr	    goto out;
129178825Sdfr    }
130178825Sdfr
131178825Sdfr    krb5_kt_close(context, kt);
132178825Sdfr
133178825Sdfr    return 0;
134178825Sdfr
135178825Sdfrout:
136178825Sdfr    if (*destroy)
137178825Sdfr	krb5_cc_destroy(context, *id);
138178825Sdfr    else
139178825Sdfr	krb5_cc_close(context, *id);
140178825Sdfr
141178825Sdfr    *id = NULL;
142178825Sdfr
143178825Sdfr    if (kt)
144178825Sdfr	krb5_kt_close(context, kt);
145178825Sdfr
146178825Sdfr    if (principal)
147178825Sdfr	krb5_free_principal(context, principal);
148178825Sdfr    return ret;
149178825Sdfr}
150178825Sdfr
151178825Sdfr/*
152178825Sdfr *
153178825Sdfr */
154178825Sdfr
155178825Sdfrstatic OM_uint32
156178825Sdfrkdc_alloc(OM_uint32 *minor, void **ctx)
157178825Sdfr{
158178825Sdfr    krb5_error_code ret;
159178825Sdfr    struct ntlmkrb5 *c;
160178825Sdfr    OM_uint32 junk;
161178825Sdfr
162178825Sdfr    c = calloc(1, sizeof(*c));
163178825Sdfr    if (c == NULL) {
164178825Sdfr	*minor = ENOMEM;
165178825Sdfr	return GSS_S_FAILURE;
166178825Sdfr    }
167178825Sdfr
168178825Sdfr    ret = krb5_init_context(&c->context);
169178825Sdfr    if (ret) {
170178825Sdfr	kdc_destroy(&junk, c);
171178825Sdfr	*minor = ret;
172178825Sdfr	return GSS_S_FAILURE;
173178825Sdfr    }
174178825Sdfr
175178825Sdfr    ret = get_ccache(c->context, &c->destroy, &c->id);
176178825Sdfr    if (ret) {
177178825Sdfr	kdc_destroy(&junk, c);
178178825Sdfr	*minor = ret;
179178825Sdfr	return GSS_S_FAILURE;
180178825Sdfr    }
181178825Sdfr
182178825Sdfr    ret = krb5_ntlm_alloc(c->context, &c->ntlm);
183178825Sdfr    if (ret) {
184178825Sdfr	kdc_destroy(&junk, c);
185178825Sdfr	*minor = ret;
186178825Sdfr	return GSS_S_FAILURE;
187178825Sdfr    }
188178825Sdfr
189178825Sdfr    *ctx = c;
190178825Sdfr
191178825Sdfr    return GSS_S_COMPLETE;
192178825Sdfr}
193178825Sdfr
194178825Sdfrstatic int
195178825Sdfrkdc_probe(OM_uint32 *minor, void *ctx, const char *realm)
196178825Sdfr{
197178825Sdfr    struct ntlmkrb5 *c = ctx;
198178825Sdfr    krb5_error_code ret;
199178825Sdfr    unsigned flags;
200178825Sdfr
201178825Sdfr    ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags);
202178825Sdfr    if (ret)
203178825Sdfr	return ret;
204178825Sdfr
205178825Sdfr    if ((flags & (1|2|4)) == 0)
206178825Sdfr	return EINVAL;
207178825Sdfr
208178825Sdfr    return 0;
209178825Sdfr}
210178825Sdfr
211178825Sdfr/*
212178825Sdfr *
213178825Sdfr */
214178825Sdfr
215178825Sdfrstatic OM_uint32
216178825Sdfrkdc_destroy(OM_uint32 *minor, void *ctx)
217178825Sdfr{
218178825Sdfr    struct ntlmkrb5 *c = ctx;
219178825Sdfr    krb5_data_free(&c->opaque);
220178825Sdfr    krb5_data_free(&c->sessionkey);
221178825Sdfr    if (c->ntlm)
222178825Sdfr	krb5_ntlm_free(c->context, c->ntlm);
223178825Sdfr    if (c->id) {
224178825Sdfr	if (c->destroy)
225178825Sdfr	    krb5_cc_destroy(c->context, c->id);
226178825Sdfr	else
227178825Sdfr	    krb5_cc_close(c->context, c->id);
228178825Sdfr    }
229178825Sdfr    if (c->context)
230178825Sdfr	krb5_free_context(c->context);
231178825Sdfr    memset(c, 0, sizeof(*c));
232178825Sdfr    free(c);
233178825Sdfr
234178825Sdfr    return GSS_S_COMPLETE;
235178825Sdfr}
236178825Sdfr
237178825Sdfr/*
238178825Sdfr *
239178825Sdfr */
240178825Sdfr
241178825Sdfrstatic OM_uint32
242178825Sdfrkdc_type2(OM_uint32 *minor_status,
243178825Sdfr	  void *ctx,
244178825Sdfr	  uint32_t flags,
245178825Sdfr	  const char *hostname,
246178825Sdfr	  const char *domain,
247178825Sdfr	  uint32_t *ret_flags,
248178825Sdfr	  struct ntlm_buf *out)
249178825Sdfr{
250178825Sdfr    struct ntlmkrb5 *c = ctx;
251178825Sdfr    krb5_error_code ret;
252178825Sdfr    struct ntlm_type2 type2;
253178825Sdfr    krb5_data challange;
254178825Sdfr    struct ntlm_buf data;
255178825Sdfr    krb5_data ti;
256178825Sdfr
257178825Sdfr    memset(&type2, 0, sizeof(type2));
258178825Sdfr
259178825Sdfr    /*
260178825Sdfr     * Request data for type 2 packet from the KDC.
261178825Sdfr     */
262178825Sdfr    ret = krb5_ntlm_init_request(c->context,
263178825Sdfr				 c->ntlm,
264178825Sdfr				 NULL,
265178825Sdfr				 c->id,
266178825Sdfr				 flags,
267178825Sdfr				 hostname,
268178825Sdfr				 domain);
269178825Sdfr    if (ret) {
270178825Sdfr	*minor_status = ret;
271178825Sdfr	return GSS_S_FAILURE;
272178825Sdfr    }
273178825Sdfr
274178825Sdfr    /*
275178825Sdfr     *
276178825Sdfr     */
277178825Sdfr
278178825Sdfr    ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque);
279178825Sdfr    if (ret) {
280178825Sdfr	*minor_status = ret;
281178825Sdfr	return GSS_S_FAILURE;
282178825Sdfr    }
283178825Sdfr
284178825Sdfr    /*
285178825Sdfr     *
286178825Sdfr     */
287178825Sdfr
288178825Sdfr    ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags);
289178825Sdfr    if (ret) {
290178825Sdfr	*minor_status = ret;
291178825Sdfr	return GSS_S_FAILURE;
292178825Sdfr    }
293178825Sdfr    *ret_flags = type2.flags;
294178825Sdfr
295178825Sdfr    ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange);
296178825Sdfr    if (ret) {
297178825Sdfr	*minor_status = ret;
298178825Sdfr	return GSS_S_FAILURE;
299178825Sdfr    }
300178825Sdfr
301178825Sdfr    if (challange.length != sizeof(type2.challange)) {
302178825Sdfr	*minor_status = EINVAL;
303178825Sdfr	return GSS_S_FAILURE;
304178825Sdfr    }
305178825Sdfr    memcpy(type2.challange, challange.data, sizeof(type2.challange));
306178825Sdfr    krb5_data_free(&challange);
307178825Sdfr
308178825Sdfr    ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm,
309178825Sdfr					&type2.targetname);
310178825Sdfr    if (ret) {
311178825Sdfr	*minor_status = ret;
312178825Sdfr	return GSS_S_FAILURE;
313178825Sdfr    }
314178825Sdfr
315178825Sdfr    ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti);
316178825Sdfr    if (ret) {
317178825Sdfr	free(type2.targetname);
318178825Sdfr	*minor_status = ret;
319178825Sdfr	return GSS_S_FAILURE;
320178825Sdfr    }
321178825Sdfr
322178825Sdfr    type2.targetinfo.data = ti.data;
323178825Sdfr    type2.targetinfo.length = ti.length;
324178825Sdfr
325178825Sdfr    ret = heim_ntlm_encode_type2(&type2, &data);
326178825Sdfr    free(type2.targetname);
327178825Sdfr    krb5_data_free(&ti);
328178825Sdfr    if (ret) {
329178825Sdfr	*minor_status = ret;
330178825Sdfr	return GSS_S_FAILURE;
331178825Sdfr    }
332178825Sdfr
333178825Sdfr    out->data = data.data;
334178825Sdfr    out->length = data.length;
335178825Sdfr
336178825Sdfr    return GSS_S_COMPLETE;
337178825Sdfr}
338178825Sdfr
339178825Sdfr/*
340178825Sdfr *
341178825Sdfr */
342178825Sdfr
343178825Sdfrstatic OM_uint32
344178825Sdfrkdc_type3(OM_uint32 *minor_status,
345178825Sdfr	  void *ctx,
346178825Sdfr	  const struct ntlm_type3 *type3,
347178825Sdfr	  struct ntlm_buf *sessionkey)
348178825Sdfr{
349178825Sdfr    struct ntlmkrb5 *c = ctx;
350178825Sdfr    krb5_error_code ret;
351178825Sdfr
352178825Sdfr    sessionkey->data = NULL;
353178825Sdfr    sessionkey->length = 0;
354178825Sdfr
355178825Sdfr    ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags);
356178825Sdfr    if (ret) goto out;
357178825Sdfr    ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username);
358178825Sdfr    if (ret) goto out;
359178825Sdfr    ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm,
360178825Sdfr				       type3->targetname);
361178825Sdfr    if (ret) goto out;
362178825Sdfr    ret = krb5_ntlm_req_set_lm(c->context, c->ntlm,
363178825Sdfr			       type3->lm.data, type3->lm.length);
364178825Sdfr    if (ret) goto out;
365178825Sdfr    ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm,
366178825Sdfr				 type3->ntlm.data, type3->ntlm.length);
367178825Sdfr    if (ret) goto out;
368178825Sdfr    ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque);
369178825Sdfr    if (ret) goto out;
370178825Sdfr
371178825Sdfr    if (type3->sessionkey.length) {
372178825Sdfr	ret = krb5_ntlm_req_set_session(c->context, c->ntlm,
373178825Sdfr					type3->sessionkey.data,
374178825Sdfr					type3->sessionkey.length);
375178825Sdfr	if (ret) goto out;
376178825Sdfr    }
377178825Sdfr
378178825Sdfr    /*
379178825Sdfr     * Verify with the KDC the type3 packet is ok
380178825Sdfr     */
381178825Sdfr    ret = krb5_ntlm_request(c->context,
382178825Sdfr			    c->ntlm,
383178825Sdfr			    NULL,
384178825Sdfr			    c->id);
385178825Sdfr    if (ret)
386178825Sdfr	goto out;
387178825Sdfr
388178825Sdfr    if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) {
389178825Sdfr	ret = EINVAL;
390178825Sdfr	goto out;
391178825Sdfr    }
392178825Sdfr
393178825Sdfr    if (type3->sessionkey.length) {
394178825Sdfr	ret = krb5_ntlm_rep_get_sessionkey(c->context,
395178825Sdfr					   c->ntlm,
396178825Sdfr					   &c->sessionkey);
397178825Sdfr	if (ret)
398178825Sdfr	    goto out;
399178825Sdfr
400178825Sdfr	sessionkey->data = c->sessionkey.data;
401178825Sdfr	sessionkey->length = c->sessionkey.length;
402178825Sdfr    }
403178825Sdfr
404178825Sdfr    return 0;
405178825Sdfr
406178825Sdfr out:
407178825Sdfr    *minor_status = ret;
408178825Sdfr    return GSS_S_FAILURE;
409178825Sdfr}
410178825Sdfr
411178825Sdfr/*
412178825Sdfr *
413178825Sdfr */
414178825Sdfr
415178825Sdfrstatic void
416178825Sdfrkdc_free_buffer(struct ntlm_buf *sessionkey)
417178825Sdfr{
418178825Sdfr    if (sessionkey->data)
419178825Sdfr	free(sessionkey->data);
420178825Sdfr    sessionkey->data = NULL;
421178825Sdfr    sessionkey->length = 0;
422178825Sdfr}
423178825Sdfr
424178825Sdfr/*
425178825Sdfr *
426178825Sdfr */
427178825Sdfr
428178825Sdfrstruct ntlm_server_interface ntlmsspi_kdc_digest = {
429178825Sdfr    kdc_alloc,
430178825Sdfr    kdc_destroy,
431178825Sdfr    kdc_probe,
432178825Sdfr    kdc_type2,
433178825Sdfr    kdc_type3,
434178825Sdfr    kdc_free_buffer
435178825Sdfr};
436