1/*
2 * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "ntlm.h"
35
36#ifdef DIGEST
37
38/*
39 *
40 */
41
42struct ntlmkrb5 {
43    krb5_context context;
44    krb5_ntlm ntlm;
45    krb5_realm kerberos_realm;
46    krb5_ccache id;
47    krb5_data opaque;
48    int destroy;
49    OM_uint32 flags;
50    struct ntlm_buf key;
51    krb5_data sessionkey;
52};
53
54static OM_uint32 kdc_destroy(OM_uint32 *, void *);
55
56/*
57 * Get credential cache that the ntlm code can use to talk to the KDC
58 * using the digest API.
59 */
60
61static krb5_error_code
62get_ccache(krb5_context context, int *destroy, krb5_ccache *id)
63{
64    krb5_principal principal = NULL;
65    krb5_error_code ret;
66    krb5_keytab kt = NULL;
67
68    *id = NULL;
69
70    if (!issuid()) {
71	const char *cache;
72
73	cache = getenv("NTLM_ACCEPTOR_CCACHE");
74	if (cache) {
75	    ret = krb5_cc_resolve(context, cache, id);
76	    if (ret)
77		goto out;
78	    return 0;
79	}
80    }
81
82    ret = krb5_sname_to_principal(context, NULL, "host",
83				  KRB5_NT_SRV_HST, &principal);
84    if (ret)
85	goto out;
86
87    ret = krb5_cc_cache_match(context, principal, id);
88    if (ret == 0)
89	return 0;
90
91    /* did not find in default credcache, lets try default keytab */
92    ret = krb5_kt_default(context, &kt);
93    if (ret)
94	goto out;
95
96    /* XXX check in keytab */
97    {
98	krb5_get_init_creds_opt *opt;
99	krb5_creds cred;
100
101	memset(&cred, 0, sizeof(cred));
102
103	ret = krb5_cc_new_unique(context, "MEMORY", NULL, id);
104	if (ret)
105	    goto out;
106	*destroy = 1;
107	ret = krb5_get_init_creds_opt_alloc(context, &opt);
108	if (ret)
109	    goto out;
110	ret = krb5_get_init_creds_keytab (context,
111					  &cred,
112					  principal,
113					  kt,
114					  0,
115					  NULL,
116					  opt);
117	krb5_get_init_creds_opt_free(context, opt);
118	if (ret)
119	    goto out;
120	ret = krb5_cc_initialize (context, *id, cred.client);
121	if (ret) {
122	    krb5_free_cred_contents (context, &cred);
123	    goto out;
124	}
125	ret = krb5_cc_store_cred (context, *id, &cred);
126	krb5_free_cred_contents (context, &cred);
127	if (ret)
128	    goto out;
129    }
130
131    krb5_kt_close(context, kt);
132
133    return 0;
134
135out:
136    if (*id) {
137	if (*destroy)
138	    krb5_cc_destroy(context, *id);
139	else
140	    krb5_cc_close(context, *id);
141	*id = NULL;
142    }
143
144    if (kt)
145	krb5_kt_close(context, kt);
146
147    if (principal)
148	krb5_free_principal(context, principal);
149    return ret;
150}
151
152/*
153 *
154 */
155
156static OM_uint32
157kdc_alloc(OM_uint32 *minor, void **ctx)
158{
159    krb5_error_code ret;
160    struct ntlmkrb5 *c;
161    OM_uint32 junk;
162
163    c = calloc(1, sizeof(*c));
164    if (c == NULL) {
165	*minor = ENOMEM;
166	return GSS_S_FAILURE;
167    }
168
169    ret = krb5_init_context(&c->context);
170    if (ret) {
171	kdc_destroy(&junk, c);
172	*minor = ret;
173	return GSS_S_FAILURE;
174    }
175
176    ret = get_ccache(c->context, &c->destroy, &c->id);
177    if (ret) {
178	kdc_destroy(&junk, c);
179	*minor = ret;
180	return GSS_S_FAILURE;
181    }
182
183    ret = krb5_ntlm_alloc(c->context, &c->ntlm);
184    if (ret) {
185	kdc_destroy(&junk, c);
186	*minor = ret;
187	return GSS_S_FAILURE;
188    }
189
190    *ctx = c;
191
192    return GSS_S_COMPLETE;
193}
194
195static int
196kdc_probe(OM_uint32 *minor, void *ctx, const char *realm)
197{
198    struct ntlmkrb5 *c = ctx;
199    krb5_error_code ret;
200    unsigned flags;
201
202    ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags);
203    if (ret)
204	return ret;
205
206    if ((flags & (1|2|4)) == 0)
207	return EINVAL;
208
209    return 0;
210}
211
212/*
213 *
214 */
215
216static OM_uint32
217kdc_destroy(OM_uint32 *minor, void *ctx)
218{
219    struct ntlmkrb5 *c = ctx;
220    krb5_data_free(&c->opaque);
221    krb5_data_free(&c->sessionkey);
222    if (c->ntlm)
223	krb5_ntlm_free(c->context, c->ntlm);
224    if (c->id) {
225	if (c->destroy)
226	    krb5_cc_destroy(c->context, c->id);
227	else
228	    krb5_cc_close(c->context, c->id);
229    }
230    if (c->context)
231	krb5_free_context(c->context);
232    memset(c, 0, sizeof(*c));
233    free(c);
234
235    return GSS_S_COMPLETE;
236}
237
238/*
239 *
240 */
241
242static OM_uint32
243kdc_type2(OM_uint32 *minor_status,
244	  void *ctx,
245	  uint32_t flags,
246	  const char *hostname,
247	  const char *domain,
248	  uint32_t *ret_flags,
249	  struct ntlm_buf *out)
250{
251    struct ntlmkrb5 *c = ctx;
252    krb5_error_code ret;
253    struct ntlm_type2 type2;
254    krb5_data challange;
255    struct ntlm_buf data;
256    krb5_data ti;
257
258    memset(&type2, 0, sizeof(type2));
259
260    /*
261     * Request data for type 2 packet from the KDC.
262     */
263    ret = krb5_ntlm_init_request(c->context,
264				 c->ntlm,
265				 NULL,
266				 c->id,
267				 flags,
268				 hostname,
269				 domain);
270    if (ret) {
271	*minor_status = ret;
272	return GSS_S_FAILURE;
273    }
274
275    /*
276     *
277     */
278
279    ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque);
280    if (ret) {
281	*minor_status = ret;
282	return GSS_S_FAILURE;
283    }
284
285    /*
286     *
287     */
288
289    ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags);
290    if (ret) {
291	*minor_status = ret;
292	return GSS_S_FAILURE;
293    }
294    *ret_flags = type2.flags;
295
296    ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange);
297    if (ret) {
298	*minor_status = ret;
299	return GSS_S_FAILURE;
300    }
301
302    if (challange.length != sizeof(type2.challenge)) {
303	*minor_status = EINVAL;
304	return GSS_S_FAILURE;
305    }
306    memcpy(type2.challenge, challange.data, sizeof(type2.challenge));
307    krb5_data_free(&challange);
308
309    ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm,
310					&type2.targetname);
311    if (ret) {
312	*minor_status = ret;
313	return GSS_S_FAILURE;
314    }
315
316    ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti);
317    if (ret) {
318	free(type2.targetname);
319	*minor_status = ret;
320	return GSS_S_FAILURE;
321    }
322
323    type2.targetinfo.data = ti.data;
324    type2.targetinfo.length = ti.length;
325
326    ret = heim_ntlm_encode_type2(&type2, &data);
327    free(type2.targetname);
328    krb5_data_free(&ti);
329    if (ret) {
330	*minor_status = ret;
331	return GSS_S_FAILURE;
332    }
333
334    out->data = data.data;
335    out->length = data.length;
336
337    return GSS_S_COMPLETE;
338}
339
340/*
341 *
342 */
343
344static OM_uint32
345kdc_type3(OM_uint32 *minor_status,
346	  void *ctx,
347	  const struct ntlm_type3 *type3,
348	  struct ntlm_buf *sessionkey)
349{
350    struct ntlmkrb5 *c = ctx;
351    krb5_error_code ret;
352
353    sessionkey->data = NULL;
354    sessionkey->length = 0;
355
356    ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags);
357    if (ret) goto out;
358    ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username);
359    if (ret) goto out;
360    ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm,
361				       type3->targetname);
362    if (ret) goto out;
363    ret = krb5_ntlm_req_set_lm(c->context, c->ntlm,
364			       type3->lm.data, type3->lm.length);
365    if (ret) goto out;
366    ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm,
367				 type3->ntlm.data, type3->ntlm.length);
368    if (ret) goto out;
369    ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque);
370    if (ret) goto out;
371
372    if (type3->sessionkey.length) {
373	ret = krb5_ntlm_req_set_session(c->context, c->ntlm,
374					type3->sessionkey.data,
375					type3->sessionkey.length);
376	if (ret) goto out;
377    }
378
379    /*
380     * Verify with the KDC the type3 packet is ok
381     */
382    ret = krb5_ntlm_request(c->context,
383			    c->ntlm,
384			    NULL,
385			    c->id);
386    if (ret)
387	goto out;
388
389    if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) {
390	ret = EINVAL;
391	goto out;
392    }
393
394    if (type3->sessionkey.length) {
395	ret = krb5_ntlm_rep_get_sessionkey(c->context,
396					   c->ntlm,
397					   &c->sessionkey);
398	if (ret)
399	    goto out;
400
401	sessionkey->data = c->sessionkey.data;
402	sessionkey->length = c->sessionkey.length;
403    }
404
405    return 0;
406
407 out:
408    *minor_status = ret;
409    return GSS_S_FAILURE;
410}
411
412/*
413 *
414 */
415
416static void
417kdc_free_buffer(struct ntlm_buf *sessionkey)
418{
419    if (sessionkey->data)
420	free(sessionkey->data);
421    sessionkey->data = NULL;
422    sessionkey->length = 0;
423}
424
425/*
426 *
427 */
428
429struct ntlm_server_interface ntlmsspi_kdc_digest = {
430    kdc_alloc,
431    kdc_destroy,
432    kdc_probe,
433    kdc_type2,
434    kdc_type3,
435    kdc_free_buffer
436};
437
438#endif /* DIGEST */
439