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
42 struct 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
54 static 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
61 static krb5_error_code
get_ccache(krb5_context context,int * destroy,krb5_ccache * id)62 get_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
135 out:
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
156 static OM_uint32
kdc_alloc(OM_uint32 * minor,void ** ctx)157 kdc_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
195 static int
kdc_probe(OM_uint32 * minor,void * ctx,const char * realm)196 kdc_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
216 static OM_uint32
kdc_destroy(OM_uint32 * minor,void * ctx)217 kdc_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
242 static OM_uint32
kdc_type2(OM_uint32 * minor_status,void * ctx,uint32_t flags,const char * hostname,const char * domain,uint32_t * ret_flags,struct ntlm_buf * out)243 kdc_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
344 static OM_uint32
kdc_type3(OM_uint32 * minor_status,void * ctx,const struct ntlm_type3 * type3,struct ntlm_buf * sessionkey)345 kdc_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
416 static void
kdc_free_buffer(struct ntlm_buf * sessionkey)417 kdc_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
429 struct 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