1226031Sstas/*
2226031Sstas * Copyright (c) 2006 - 2007 Kungliga Tekniska H��gskolan
3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4226031Sstas * All rights reserved.
5226031Sstas *
6226031Sstas * Redistribution and use in source and binary forms, with or without
7226031Sstas * modification, are permitted provided that the following conditions
8226031Sstas * are met:
9226031Sstas *
10226031Sstas * 1. Redistributions of source code must retain the above copyright
11226031Sstas *    notice, this list of conditions and the following disclaimer.
12226031Sstas *
13226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
14226031Sstas *    notice, this list of conditions and the following disclaimer in the
15226031Sstas *    documentation and/or other materials provided with the distribution.
16226031Sstas *
17226031Sstas * 3. Neither the name of the Institute nor the names of its contributors
18226031Sstas *    may be used to endorse or promote products derived from this software
19226031Sstas *    without specific prior written permission.
20226031Sstas *
21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24226031Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31226031Sstas * SUCH DAMAGE.
32226031Sstas */
33226031Sstas
34226031Sstas#include "ntlm.h"
35226031Sstas
36226031Sstas#ifdef DIGEST
37226031Sstas
38226031Sstas/*
39226031Sstas *
40226031Sstas */
41226031Sstas
42226031Sstasstruct ntlmkrb5 {
43226031Sstas    krb5_context context;
44226031Sstas    krb5_ntlm ntlm;
45226031Sstas    krb5_realm kerberos_realm;
46226031Sstas    krb5_ccache id;
47226031Sstas    krb5_data opaque;
48226031Sstas    int destroy;
49226031Sstas    OM_uint32 flags;
50226031Sstas    struct ntlm_buf key;
51226031Sstas    krb5_data sessionkey;
52226031Sstas};
53226031Sstas
54226031Sstasstatic OM_uint32 kdc_destroy(OM_uint32 *, void *);
55226031Sstas
56226031Sstas/*
57226031Sstas * Get credential cache that the ntlm code can use to talk to the KDC
58226031Sstas * using the digest API.
59226031Sstas */
60226031Sstas
61226031Sstasstatic krb5_error_code
62226031Sstasget_ccache(krb5_context context, int *destroy, krb5_ccache *id)
63226031Sstas{
64226031Sstas    krb5_principal principal = NULL;
65226031Sstas    krb5_error_code ret;
66226031Sstas    krb5_keytab kt = NULL;
67226031Sstas
68226031Sstas    *id = NULL;
69226031Sstas
70226031Sstas    if (!issuid()) {
71226031Sstas	const char *cache;
72226031Sstas
73226031Sstas	cache = getenv("NTLM_ACCEPTOR_CCACHE");
74226031Sstas	if (cache) {
75226031Sstas	    ret = krb5_cc_resolve(context, cache, id);
76226031Sstas	    if (ret)
77226031Sstas		goto out;
78226031Sstas	    return 0;
79226031Sstas	}
80226031Sstas    }
81226031Sstas
82226031Sstas    ret = krb5_sname_to_principal(context, NULL, "host",
83226031Sstas				  KRB5_NT_SRV_HST, &principal);
84226031Sstas    if (ret)
85226031Sstas	goto out;
86226031Sstas
87226031Sstas    ret = krb5_cc_cache_match(context, principal, id);
88226031Sstas    if (ret == 0)
89226031Sstas	return 0;
90226031Sstas
91226031Sstas    /* did not find in default credcache, lets try default keytab */
92226031Sstas    ret = krb5_kt_default(context, &kt);
93226031Sstas    if (ret)
94226031Sstas	goto out;
95226031Sstas
96226031Sstas    /* XXX check in keytab */
97226031Sstas    {
98226031Sstas	krb5_get_init_creds_opt *opt;
99226031Sstas	krb5_creds cred;
100226031Sstas
101226031Sstas	memset(&cred, 0, sizeof(cred));
102226031Sstas
103226031Sstas	ret = krb5_cc_new_unique(context, "MEMORY", NULL, id);
104226031Sstas	if (ret)
105226031Sstas	    goto out;
106226031Sstas	*destroy = 1;
107226031Sstas	ret = krb5_get_init_creds_opt_alloc(context, &opt);
108226031Sstas	if (ret)
109226031Sstas	    goto out;
110226031Sstas	ret = krb5_get_init_creds_keytab (context,
111226031Sstas					  &cred,
112226031Sstas					  principal,
113226031Sstas					  kt,
114226031Sstas					  0,
115226031Sstas					  NULL,
116226031Sstas					  opt);
117226031Sstas	krb5_get_init_creds_opt_free(context, opt);
118226031Sstas	if (ret)
119226031Sstas	    goto out;
120226031Sstas	ret = krb5_cc_initialize (context, *id, cred.client);
121226031Sstas	if (ret) {
122226031Sstas	    krb5_free_cred_contents (context, &cred);
123226031Sstas	    goto out;
124226031Sstas	}
125226031Sstas	ret = krb5_cc_store_cred (context, *id, &cred);
126226031Sstas	krb5_free_cred_contents (context, &cred);
127226031Sstas	if (ret)
128226031Sstas	    goto out;
129226031Sstas    }
130226031Sstas
131226031Sstas    krb5_kt_close(context, kt);
132226031Sstas
133226031Sstas    return 0;
134226031Sstas
135226031Sstasout:
136226031Sstas    if (*id) {
137226031Sstas	if (*destroy)
138226031Sstas	    krb5_cc_destroy(context, *id);
139226031Sstas	else
140226031Sstas	    krb5_cc_close(context, *id);
141226031Sstas	*id = NULL;
142226031Sstas    }
143226031Sstas
144226031Sstas    if (kt)
145226031Sstas	krb5_kt_close(context, kt);
146226031Sstas
147226031Sstas    if (principal)
148226031Sstas	krb5_free_principal(context, principal);
149226031Sstas    return ret;
150226031Sstas}
151226031Sstas
152226031Sstas/*
153226031Sstas *
154226031Sstas */
155226031Sstas
156226031Sstasstatic OM_uint32
157226031Sstaskdc_alloc(OM_uint32 *minor, void **ctx)
158226031Sstas{
159226031Sstas    krb5_error_code ret;
160226031Sstas    struct ntlmkrb5 *c;
161226031Sstas    OM_uint32 junk;
162226031Sstas
163226031Sstas    c = calloc(1, sizeof(*c));
164226031Sstas    if (c == NULL) {
165226031Sstas	*minor = ENOMEM;
166226031Sstas	return GSS_S_FAILURE;
167226031Sstas    }
168226031Sstas
169226031Sstas    ret = krb5_init_context(&c->context);
170226031Sstas    if (ret) {
171226031Sstas	kdc_destroy(&junk, c);
172226031Sstas	*minor = ret;
173226031Sstas	return GSS_S_FAILURE;
174226031Sstas    }
175226031Sstas
176226031Sstas    ret = get_ccache(c->context, &c->destroy, &c->id);
177226031Sstas    if (ret) {
178226031Sstas	kdc_destroy(&junk, c);
179226031Sstas	*minor = ret;
180226031Sstas	return GSS_S_FAILURE;
181226031Sstas    }
182226031Sstas
183226031Sstas    ret = krb5_ntlm_alloc(c->context, &c->ntlm);
184226031Sstas    if (ret) {
185226031Sstas	kdc_destroy(&junk, c);
186226031Sstas	*minor = ret;
187226031Sstas	return GSS_S_FAILURE;
188226031Sstas    }
189226031Sstas
190226031Sstas    *ctx = c;
191226031Sstas
192226031Sstas    return GSS_S_COMPLETE;
193226031Sstas}
194226031Sstas
195226031Sstasstatic int
196226031Sstaskdc_probe(OM_uint32 *minor, void *ctx, const char *realm)
197226031Sstas{
198226031Sstas    struct ntlmkrb5 *c = ctx;
199226031Sstas    krb5_error_code ret;
200226031Sstas    unsigned flags;
201226031Sstas
202226031Sstas    ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags);
203226031Sstas    if (ret)
204226031Sstas	return ret;
205226031Sstas
206226031Sstas    if ((flags & (1|2|4)) == 0)
207226031Sstas	return EINVAL;
208226031Sstas
209226031Sstas    return 0;
210226031Sstas}
211226031Sstas
212226031Sstas/*
213226031Sstas *
214226031Sstas */
215226031Sstas
216226031Sstasstatic OM_uint32
217226031Sstaskdc_destroy(OM_uint32 *minor, void *ctx)
218226031Sstas{
219226031Sstas    struct ntlmkrb5 *c = ctx;
220226031Sstas    krb5_data_free(&c->opaque);
221226031Sstas    krb5_data_free(&c->sessionkey);
222226031Sstas    if (c->ntlm)
223226031Sstas	krb5_ntlm_free(c->context, c->ntlm);
224226031Sstas    if (c->id) {
225226031Sstas	if (c->destroy)
226226031Sstas	    krb5_cc_destroy(c->context, c->id);
227226031Sstas	else
228226031Sstas	    krb5_cc_close(c->context, c->id);
229226031Sstas    }
230226031Sstas    if (c->context)
231226031Sstas	krb5_free_context(c->context);
232226031Sstas    memset(c, 0, sizeof(*c));
233226031Sstas    free(c);
234226031Sstas
235226031Sstas    return GSS_S_COMPLETE;
236226031Sstas}
237226031Sstas
238226031Sstas/*
239226031Sstas *
240226031Sstas */
241226031Sstas
242226031Sstasstatic OM_uint32
243226031Sstaskdc_type2(OM_uint32 *minor_status,
244226031Sstas	  void *ctx,
245226031Sstas	  uint32_t flags,
246226031Sstas	  const char *hostname,
247226031Sstas	  const char *domain,
248226031Sstas	  uint32_t *ret_flags,
249226031Sstas	  struct ntlm_buf *out)
250226031Sstas{
251226031Sstas    struct ntlmkrb5 *c = ctx;
252226031Sstas    krb5_error_code ret;
253226031Sstas    struct ntlm_type2 type2;
254226031Sstas    krb5_data challange;
255226031Sstas    struct ntlm_buf data;
256226031Sstas    krb5_data ti;
257226031Sstas
258226031Sstas    memset(&type2, 0, sizeof(type2));
259226031Sstas
260226031Sstas    /*
261226031Sstas     * Request data for type 2 packet from the KDC.
262226031Sstas     */
263226031Sstas    ret = krb5_ntlm_init_request(c->context,
264226031Sstas				 c->ntlm,
265226031Sstas				 NULL,
266226031Sstas				 c->id,
267226031Sstas				 flags,
268226031Sstas				 hostname,
269226031Sstas				 domain);
270226031Sstas    if (ret) {
271226031Sstas	*minor_status = ret;
272226031Sstas	return GSS_S_FAILURE;
273226031Sstas    }
274226031Sstas
275226031Sstas    /*
276226031Sstas     *
277226031Sstas     */
278226031Sstas
279226031Sstas    ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque);
280226031Sstas    if (ret) {
281226031Sstas	*minor_status = ret;
282226031Sstas	return GSS_S_FAILURE;
283226031Sstas    }
284226031Sstas
285226031Sstas    /*
286226031Sstas     *
287226031Sstas     */
288226031Sstas
289226031Sstas    ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags);
290226031Sstas    if (ret) {
291226031Sstas	*minor_status = ret;
292226031Sstas	return GSS_S_FAILURE;
293226031Sstas    }
294226031Sstas    *ret_flags = type2.flags;
295226031Sstas
296226031Sstas    ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange);
297226031Sstas    if (ret) {
298226031Sstas	*minor_status = ret;
299226031Sstas	return GSS_S_FAILURE;
300226031Sstas    }
301226031Sstas
302226031Sstas    if (challange.length != sizeof(type2.challenge)) {
303226031Sstas	*minor_status = EINVAL;
304226031Sstas	return GSS_S_FAILURE;
305226031Sstas    }
306226031Sstas    memcpy(type2.challenge, challange.data, sizeof(type2.challenge));
307226031Sstas    krb5_data_free(&challange);
308226031Sstas
309226031Sstas    ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm,
310226031Sstas					&type2.targetname);
311226031Sstas    if (ret) {
312226031Sstas	*minor_status = ret;
313226031Sstas	return GSS_S_FAILURE;
314226031Sstas    }
315226031Sstas
316226031Sstas    ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti);
317226031Sstas    if (ret) {
318226031Sstas	free(type2.targetname);
319226031Sstas	*minor_status = ret;
320226031Sstas	return GSS_S_FAILURE;
321226031Sstas    }
322226031Sstas
323226031Sstas    type2.targetinfo.data = ti.data;
324226031Sstas    type2.targetinfo.length = ti.length;
325226031Sstas
326226031Sstas    ret = heim_ntlm_encode_type2(&type2, &data);
327226031Sstas    free(type2.targetname);
328226031Sstas    krb5_data_free(&ti);
329226031Sstas    if (ret) {
330226031Sstas	*minor_status = ret;
331226031Sstas	return GSS_S_FAILURE;
332226031Sstas    }
333226031Sstas
334226031Sstas    out->data = data.data;
335226031Sstas    out->length = data.length;
336226031Sstas
337226031Sstas    return GSS_S_COMPLETE;
338226031Sstas}
339226031Sstas
340226031Sstas/*
341226031Sstas *
342226031Sstas */
343226031Sstas
344226031Sstasstatic OM_uint32
345226031Sstaskdc_type3(OM_uint32 *minor_status,
346226031Sstas	  void *ctx,
347226031Sstas	  const struct ntlm_type3 *type3,
348226031Sstas	  struct ntlm_buf *sessionkey)
349226031Sstas{
350226031Sstas    struct ntlmkrb5 *c = ctx;
351226031Sstas    krb5_error_code ret;
352226031Sstas
353226031Sstas    sessionkey->data = NULL;
354226031Sstas    sessionkey->length = 0;
355226031Sstas
356226031Sstas    ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags);
357226031Sstas    if (ret) goto out;
358226031Sstas    ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username);
359226031Sstas    if (ret) goto out;
360226031Sstas    ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm,
361226031Sstas				       type3->targetname);
362226031Sstas    if (ret) goto out;
363226031Sstas    ret = krb5_ntlm_req_set_lm(c->context, c->ntlm,
364226031Sstas			       type3->lm.data, type3->lm.length);
365226031Sstas    if (ret) goto out;
366226031Sstas    ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm,
367226031Sstas				 type3->ntlm.data, type3->ntlm.length);
368226031Sstas    if (ret) goto out;
369226031Sstas    ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque);
370226031Sstas    if (ret) goto out;
371226031Sstas
372226031Sstas    if (type3->sessionkey.length) {
373226031Sstas	ret = krb5_ntlm_req_set_session(c->context, c->ntlm,
374226031Sstas					type3->sessionkey.data,
375226031Sstas					type3->sessionkey.length);
376226031Sstas	if (ret) goto out;
377226031Sstas    }
378226031Sstas
379226031Sstas    /*
380226031Sstas     * Verify with the KDC the type3 packet is ok
381226031Sstas     */
382226031Sstas    ret = krb5_ntlm_request(c->context,
383226031Sstas			    c->ntlm,
384226031Sstas			    NULL,
385226031Sstas			    c->id);
386226031Sstas    if (ret)
387226031Sstas	goto out;
388226031Sstas
389226031Sstas    if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) {
390226031Sstas	ret = EINVAL;
391226031Sstas	goto out;
392226031Sstas    }
393226031Sstas
394226031Sstas    if (type3->sessionkey.length) {
395226031Sstas	ret = krb5_ntlm_rep_get_sessionkey(c->context,
396226031Sstas					   c->ntlm,
397226031Sstas					   &c->sessionkey);
398226031Sstas	if (ret)
399226031Sstas	    goto out;
400226031Sstas
401226031Sstas	sessionkey->data = c->sessionkey.data;
402226031Sstas	sessionkey->length = c->sessionkey.length;
403226031Sstas    }
404226031Sstas
405226031Sstas    return 0;
406226031Sstas
407226031Sstas out:
408226031Sstas    *minor_status = ret;
409226031Sstas    return GSS_S_FAILURE;
410226031Sstas}
411226031Sstas
412226031Sstas/*
413226031Sstas *
414226031Sstas */
415226031Sstas
416226031Sstasstatic void
417226031Sstaskdc_free_buffer(struct ntlm_buf *sessionkey)
418226031Sstas{
419226031Sstas    if (sessionkey->data)
420226031Sstas	free(sessionkey->data);
421226031Sstas    sessionkey->data = NULL;
422226031Sstas    sessionkey->length = 0;
423226031Sstas}
424226031Sstas
425226031Sstas/*
426226031Sstas *
427226031Sstas */
428226031Sstas
429226031Sstasstruct ntlm_server_interface ntlmsspi_kdc_digest = {
430226031Sstas    kdc_alloc,
431226031Sstas    kdc_destroy,
432226031Sstas    kdc_probe,
433226031Sstas    kdc_type2,
434226031Sstas    kdc_type3,
435226031Sstas    kdc_free_buffer
436226031Sstas};
437226031Sstas
438226031Sstas#endif /* DIGEST */
439