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/ntlm.h"
35
36RCSID("$Id: digest.c 22169 2007-12-04 22:19:16Z lha $");
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, NULL, 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 (*destroy)
137	krb5_cc_destroy(context, *id);
138    else
139	krb5_cc_close(context, *id);
140
141    *id = NULL;
142
143    if (kt)
144	krb5_kt_close(context, kt);
145
146    if (principal)
147	krb5_free_principal(context, principal);
148    return ret;
149}
150
151/*
152 *
153 */
154
155static OM_uint32
156kdc_alloc(OM_uint32 *minor, void **ctx)
157{
158    krb5_error_code ret;
159    struct ntlmkrb5 *c;
160    OM_uint32 junk;
161
162    c = calloc(1, sizeof(*c));
163    if (c == NULL) {
164	*minor = ENOMEM;
165	return GSS_S_FAILURE;
166    }
167
168    ret = krb5_init_context(&c->context);
169    if (ret) {
170	kdc_destroy(&junk, c);
171	*minor = ret;
172	return GSS_S_FAILURE;
173    }
174
175    ret = get_ccache(c->context, &c->destroy, &c->id);
176    if (ret) {
177	kdc_destroy(&junk, c);
178	*minor = ret;
179	return GSS_S_FAILURE;
180    }
181
182    ret = krb5_ntlm_alloc(c->context, &c->ntlm);
183    if (ret) {
184	kdc_destroy(&junk, c);
185	*minor = ret;
186	return GSS_S_FAILURE;
187    }
188
189    *ctx = c;
190
191    return GSS_S_COMPLETE;
192}
193
194static int
195kdc_probe(OM_uint32 *minor, void *ctx, const char *realm)
196{
197    struct ntlmkrb5 *c = ctx;
198    krb5_error_code ret;
199    unsigned flags;
200
201    ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags);
202    if (ret)
203	return ret;
204
205    if ((flags & (1|2|4)) == 0)
206	return EINVAL;
207
208    return 0;
209}
210
211/*
212 *
213 */
214
215static OM_uint32
216kdc_destroy(OM_uint32 *minor, void *ctx)
217{
218    struct ntlmkrb5 *c = ctx;
219    krb5_data_free(&c->opaque);
220    krb5_data_free(&c->sessionkey);
221    if (c->ntlm)
222	krb5_ntlm_free(c->context, c->ntlm);
223    if (c->id) {
224	if (c->destroy)
225	    krb5_cc_destroy(c->context, c->id);
226	else
227	    krb5_cc_close(c->context, c->id);
228    }
229    if (c->context)
230	krb5_free_context(c->context);
231    memset(c, 0, sizeof(*c));
232    free(c);
233
234    return GSS_S_COMPLETE;
235}
236
237/*
238 *
239 */
240
241static OM_uint32
242kdc_type2(OM_uint32 *minor_status,
243	  void *ctx,
244	  uint32_t flags,
245	  const char *hostname,
246	  const char *domain,
247	  uint32_t *ret_flags,
248	  struct ntlm_buf *out)
249{
250    struct ntlmkrb5 *c = ctx;
251    krb5_error_code ret;
252    struct ntlm_type2 type2;
253    krb5_data challange;
254    struct ntlm_buf data;
255    krb5_data ti;
256
257    memset(&type2, 0, sizeof(type2));
258
259    /*
260     * Request data for type 2 packet from the KDC.
261     */
262    ret = krb5_ntlm_init_request(c->context,
263				 c->ntlm,
264				 NULL,
265				 c->id,
266				 flags,
267				 hostname,
268				 domain);
269    if (ret) {
270	*minor_status = ret;
271	return GSS_S_FAILURE;
272    }
273
274    /*
275     *
276     */
277
278    ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque);
279    if (ret) {
280	*minor_status = ret;
281	return GSS_S_FAILURE;
282    }
283
284    /*
285     *
286     */
287
288    ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags);
289    if (ret) {
290	*minor_status = ret;
291	return GSS_S_FAILURE;
292    }
293    *ret_flags = type2.flags;
294
295    ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange);
296    if (ret) {
297	*minor_status = ret;
298	return GSS_S_FAILURE;
299    }
300
301    if (challange.length != sizeof(type2.challange)) {
302	*minor_status = EINVAL;
303	return GSS_S_FAILURE;
304    }
305    memcpy(type2.challange, challange.data, sizeof(type2.challange));
306    krb5_data_free(&challange);
307
308    ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm,
309					&type2.targetname);
310    if (ret) {
311	*minor_status = ret;
312	return GSS_S_FAILURE;
313    }
314
315    ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti);
316    if (ret) {
317	free(type2.targetname);
318	*minor_status = ret;
319	return GSS_S_FAILURE;
320    }
321
322    type2.targetinfo.data = ti.data;
323    type2.targetinfo.length = ti.length;
324
325    ret = heim_ntlm_encode_type2(&type2, &data);
326    free(type2.targetname);
327    krb5_data_free(&ti);
328    if (ret) {
329	*minor_status = ret;
330	return GSS_S_FAILURE;
331    }
332
333    out->data = data.data;
334    out->length = data.length;
335
336    return GSS_S_COMPLETE;
337}
338
339/*
340 *
341 */
342
343static OM_uint32
344kdc_type3(OM_uint32 *minor_status,
345	  void *ctx,
346	  const struct ntlm_type3 *type3,
347	  struct ntlm_buf *sessionkey)
348{
349    struct ntlmkrb5 *c = ctx;
350    krb5_error_code ret;
351
352    sessionkey->data = NULL;
353    sessionkey->length = 0;
354
355    ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags);
356    if (ret) goto out;
357    ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username);
358    if (ret) goto out;
359    ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm,
360				       type3->targetname);
361    if (ret) goto out;
362    ret = krb5_ntlm_req_set_lm(c->context, c->ntlm,
363			       type3->lm.data, type3->lm.length);
364    if (ret) goto out;
365    ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm,
366				 type3->ntlm.data, type3->ntlm.length);
367    if (ret) goto out;
368    ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque);
369    if (ret) goto out;
370
371    if (type3->sessionkey.length) {
372	ret = krb5_ntlm_req_set_session(c->context, c->ntlm,
373					type3->sessionkey.data,
374					type3->sessionkey.length);
375	if (ret) goto out;
376    }
377
378    /*
379     * Verify with the KDC the type3 packet is ok
380     */
381    ret = krb5_ntlm_request(c->context,
382			    c->ntlm,
383			    NULL,
384			    c->id);
385    if (ret)
386	goto out;
387
388    if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) {
389	ret = EINVAL;
390	goto out;
391    }
392
393    if (type3->sessionkey.length) {
394	ret = krb5_ntlm_rep_get_sessionkey(c->context,
395					   c->ntlm,
396					   &c->sessionkey);
397	if (ret)
398	    goto out;
399
400	sessionkey->data = c->sessionkey.data;
401	sessionkey->length = c->sessionkey.length;
402    }
403
404    return 0;
405
406 out:
407    *minor_status = ret;
408    return GSS_S_FAILURE;
409}
410
411/*
412 *
413 */
414
415static void
416kdc_free_buffer(struct ntlm_buf *sessionkey)
417{
418    if (sessionkey->data)
419	free(sessionkey->data);
420    sessionkey->data = NULL;
421    sessionkey->length = 0;
422}
423
424/*
425 *
426 */
427
428struct ntlm_server_interface ntlmsspi_kdc_digest = {
429    kdc_alloc,
430    kdc_destroy,
431    kdc_probe,
432    kdc_type2,
433    kdc_type3,
434    kdc_free_buffer
435};
436