]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/lib/gssapi/ntlm/kdc.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / lib / gssapi / ntlm / kdc.c
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 */