kdc.c revision 233294
1256752Sbrooks/*
2270268Sbz * Copyright (c) 2006 - 2007 Kungliga Tekniska H��gskolan
3256752Sbrooks * (Royal Institute of Technology, Stockholm, Sweden).
4256752Sbrooks * All rights reserved.
5256752Sbrooks *
6256752Sbrooks * Redistribution and use in source and binary forms, with or without
7256752Sbrooks * modification, are permitted provided that the following conditions
8256752Sbrooks * are met:
9256752Sbrooks *
10256752Sbrooks * 1. Redistributions of source code must retain the above copyright
11256752Sbrooks *    notice, this list of conditions and the following disclaimer.
12256752Sbrooks *
13256752Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
14256752Sbrooks *    notice, this list of conditions and the following disclaimer in the
15256752Sbrooks *    documentation and/or other materials provided with the distribution.
16256752Sbrooks *
17256752Sbrooks * 3. Neither the name of the Institute nor the names of its contributors
18256752Sbrooks *    may be used to endorse or promote products derived from this software
19256752Sbrooks *    without specific prior written permission.
20256752Sbrooks *
21256752Sbrooks * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22256752Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23256752Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24256752Sbrooks * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25256752Sbrooks * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26256752Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27256752Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28256752Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29256752Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30256752Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31256752Sbrooks * SUCH DAMAGE.
32270268Sbz */
33256752Sbrooks
34256752Sbrooks#include "ntlm.h"
35256752Sbrooks
36256752Sbrooks#ifdef DIGEST
37256752Sbrooks
38256752Sbrooks/*
39256752Sbrooks *
40256752Sbrooks */
41256752Sbrooks
42256752Sbrooksstruct ntlmkrb5 {
43256752Sbrooks    krb5_context context;
44256752Sbrooks    krb5_ntlm ntlm;
45256752Sbrooks    krb5_realm kerberos_realm;
46256752Sbrooks    krb5_ccache id;
47256752Sbrooks    krb5_data opaque;
48256752Sbrooks    int destroy;
49256752Sbrooks    OM_uint32 flags;
50256752Sbrooks    struct ntlm_buf key;
51256752Sbrooks    krb5_data sessionkey;
52256752Sbrooks};
53256752Sbrooks
54256752Sbrooksstatic OM_uint32 kdc_destroy(OM_uint32 *, void *);
55256752Sbrooks
56256752Sbrooks/*
57256752Sbrooks * Get credential cache that the ntlm code can use to talk to the KDC
58256752Sbrooks * using the digest API.
59256752Sbrooks */
60256752Sbrooks
61256752Sbrooksstatic krb5_error_code
62256752Sbrooksget_ccache(krb5_context context, int *destroy, krb5_ccache *id)
63256752Sbrooks{
64256752Sbrooks    krb5_principal principal = NULL;
65256752Sbrooks    krb5_error_code ret;
66256752Sbrooks    krb5_keytab kt = NULL;
67257656Sjoel
68256752Sbrooks    *id = NULL;
69270268Sbz
70270268Sbz    if (!issuid()) {
71270268Sbz	const char *cache;
72270268Sbz
73270268Sbz	cache = getenv("NTLM_ACCEPTOR_CCACHE");
74270268Sbz	if (cache) {
75270268Sbz	    ret = krb5_cc_resolve(context, cache, id);
76270268Sbz	    if (ret)
77270268Sbz		goto out;
78270268Sbz	    return 0;
79270268Sbz	}
80270268Sbz    }
81270268Sbz
82270268Sbz    ret = krb5_sname_to_principal(context, NULL, "host",
83270268Sbz				  KRB5_NT_SRV_HST, &principal);
84270268Sbz    if (ret)
85270268Sbz	goto out;
86270268Sbz
87270268Sbz    ret = krb5_cc_cache_match(context, principal, id);
88256752Sbrooks    if (ret == 0)
89256752Sbrooks	return 0;
90256752Sbrooks
91256752Sbrooks    /* did not find in default credcache, lets try default keytab */
92256752Sbrooks    ret = krb5_kt_default(context, &kt);
93256752Sbrooks    if (ret)
94256752Sbrooks	goto out;
95256752Sbrooks
96256752Sbrooks    /* XXX check in keytab */
97256752Sbrooks    {
98256752Sbrooks	krb5_get_init_creds_opt *opt;
99256752Sbrooks	krb5_creds cred;
100256752Sbrooks
101256752Sbrooks	memset(&cred, 0, sizeof(cred));
102256752Sbrooks
103256752Sbrooks	ret = krb5_cc_new_unique(context, "MEMORY", NULL, id);
104256752Sbrooks	if (ret)
105256752Sbrooks	    goto out;
106256752Sbrooks	*destroy = 1;
107256752Sbrooks	ret = krb5_get_init_creds_opt_alloc(context, &opt);
108256752Sbrooks	if (ret)
109256752Sbrooks	    goto out;
110256752Sbrooks	ret = krb5_get_init_creds_keytab (context,
111256752Sbrooks					  &cred,
112256752Sbrooks					  principal,
113256752Sbrooks					  kt,
114256752Sbrooks					  0,
115256752Sbrooks					  NULL,
116256752Sbrooks					  opt);
117256752Sbrooks	krb5_get_init_creds_opt_free(context, opt);
118256752Sbrooks	if (ret)
119256752Sbrooks	    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