xref: /freebsd/crypto/heimdal/lib/gssapi/ntlm/kdc.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
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
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
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
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
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
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
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
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