]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/gssapi/ntlm/digest.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / lib / gssapi / ntlm / digest.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/ntlm.h"
35
36 RCSID("$Id: digest.c 22169 2007-12-04 22:19:16Z lha $");
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, NULL, 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 (*destroy)
137         krb5_cc_destroy(context, *id);
138     else
139         krb5_cc_close(context, *id);
140
141     *id = NULL;
142
143     if (kt)
144         krb5_kt_close(context, kt);
145
146     if (principal)
147         krb5_free_principal(context, principal);
148     return ret;
149 }
150
151 /*
152  *
153  */
154
155 static OM_uint32
156 kdc_alloc(OM_uint32 *minor, void **ctx)
157 {
158     krb5_error_code ret;
159     struct ntlmkrb5 *c;
160     OM_uint32 junk;
161
162     c = calloc(1, sizeof(*c));
163     if (c == NULL) {
164         *minor = ENOMEM;
165         return GSS_S_FAILURE;
166     }
167
168     ret = krb5_init_context(&c->context);
169     if (ret) {
170         kdc_destroy(&junk, c);
171         *minor = ret;
172         return GSS_S_FAILURE;
173     }
174
175     ret = get_ccache(c->context, &c->destroy, &c->id);
176     if (ret) {
177         kdc_destroy(&junk, c);
178         *minor = ret;
179         return GSS_S_FAILURE;
180     }
181
182     ret = krb5_ntlm_alloc(c->context, &c->ntlm);
183     if (ret) {
184         kdc_destroy(&junk, c);
185         *minor = ret;
186         return GSS_S_FAILURE;
187     }
188
189     *ctx = c;
190
191     return GSS_S_COMPLETE;
192 }
193
194 static int
195 kdc_probe(OM_uint32 *minor, void *ctx, const char *realm)
196 {
197     struct ntlmkrb5 *c = ctx;
198     krb5_error_code ret;
199     unsigned flags;
200
201     ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags);
202     if (ret)
203         return ret;
204     
205     if ((flags & (1|2|4)) == 0)
206         return EINVAL;
207
208     return 0;
209 }
210
211 /*
212  *
213  */
214
215 static OM_uint32
216 kdc_destroy(OM_uint32 *minor, void *ctx)
217 {
218     struct ntlmkrb5 *c = ctx;
219     krb5_data_free(&c->opaque);
220     krb5_data_free(&c->sessionkey);
221     if (c->ntlm)
222         krb5_ntlm_free(c->context, c->ntlm);
223     if (c->id) {
224         if (c->destroy)
225             krb5_cc_destroy(c->context, c->id);
226         else
227             krb5_cc_close(c->context, c->id);
228     }
229     if (c->context)
230         krb5_free_context(c->context);
231     memset(c, 0, sizeof(*c));
232     free(c);
233
234     return GSS_S_COMPLETE;
235 }
236
237 /*
238  *
239  */
240
241 static OM_uint32
242 kdc_type2(OM_uint32 *minor_status,
243           void *ctx,
244           uint32_t flags,
245           const char *hostname,
246           const char *domain,
247           uint32_t *ret_flags,
248           struct ntlm_buf *out)
249 {
250     struct ntlmkrb5 *c = ctx;
251     krb5_error_code ret;
252     struct ntlm_type2 type2;
253     krb5_data challange;
254     struct ntlm_buf data;
255     krb5_data ti;
256     
257     memset(&type2, 0, sizeof(type2));
258     
259     /*
260      * Request data for type 2 packet from the KDC.
261      */
262     ret = krb5_ntlm_init_request(c->context, 
263                                  c->ntlm,
264                                  NULL,
265                                  c->id,
266                                  flags,
267                                  hostname,
268                                  domain);
269     if (ret) {
270         *minor_status = ret;
271         return GSS_S_FAILURE;
272     }
273
274     /*
275      *
276      */
277
278     ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque);
279     if (ret) {
280         *minor_status = ret;
281         return GSS_S_FAILURE;
282     }
283
284     /*
285      *
286      */
287
288     ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags);
289     if (ret) {
290         *minor_status = ret;
291         return GSS_S_FAILURE;
292     }
293     *ret_flags = type2.flags;
294
295     ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange);
296     if (ret) {
297         *minor_status = ret;
298         return GSS_S_FAILURE;
299     }
300
301     if (challange.length != sizeof(type2.challange)) {
302         *minor_status = EINVAL;
303         return GSS_S_FAILURE;
304     }
305     memcpy(type2.challange, challange.data, sizeof(type2.challange));
306     krb5_data_free(&challange);
307
308     ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm,
309                                         &type2.targetname);
310     if (ret) {
311         *minor_status = ret;
312         return GSS_S_FAILURE;
313     }
314
315     ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti);
316     if (ret) {
317         free(type2.targetname);
318         *minor_status = ret;
319         return GSS_S_FAILURE;
320     }
321
322     type2.targetinfo.data = ti.data;
323     type2.targetinfo.length = ti.length;
324         
325     ret = heim_ntlm_encode_type2(&type2, &data);
326     free(type2.targetname);
327     krb5_data_free(&ti);
328     if (ret) {
329         *minor_status = ret;
330         return GSS_S_FAILURE;
331     }
332         
333     out->data = data.data;
334     out->length = data.length;
335
336     return GSS_S_COMPLETE;
337 }
338
339 /*
340  *
341  */
342
343 static OM_uint32
344 kdc_type3(OM_uint32 *minor_status,
345           void *ctx,
346           const struct ntlm_type3 *type3,
347           struct ntlm_buf *sessionkey)
348 {
349     struct ntlmkrb5 *c = ctx;
350     krb5_error_code ret;
351
352     sessionkey->data = NULL;
353     sessionkey->length = 0;
354
355     ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags);
356     if (ret) goto out;
357     ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username);
358     if (ret) goto out;
359     ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm, 
360                                        type3->targetname);
361     if (ret) goto out;
362     ret = krb5_ntlm_req_set_lm(c->context, c->ntlm, 
363                                type3->lm.data, type3->lm.length);
364     if (ret) goto out;
365     ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm, 
366                                  type3->ntlm.data, type3->ntlm.length);
367     if (ret) goto out;
368     ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque);
369     if (ret) goto out;
370
371     if (type3->sessionkey.length) {
372         ret = krb5_ntlm_req_set_session(c->context, c->ntlm,
373                                         type3->sessionkey.data,
374                                         type3->sessionkey.length);
375         if (ret) goto out;
376     }
377
378     /*
379      * Verify with the KDC the type3 packet is ok
380      */
381     ret = krb5_ntlm_request(c->context, 
382                             c->ntlm,
383                             NULL,
384                             c->id);
385     if (ret)
386         goto out;
387
388     if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) {
389         ret = EINVAL;
390         goto out;
391     }
392
393     if (type3->sessionkey.length) {
394         ret = krb5_ntlm_rep_get_sessionkey(c->context, 
395                                            c->ntlm,
396                                            &c->sessionkey);
397         if (ret)
398             goto out;
399
400         sessionkey->data = c->sessionkey.data;
401         sessionkey->length = c->sessionkey.length;
402     }
403
404     return 0;
405
406  out:
407     *minor_status = ret;
408     return GSS_S_FAILURE;
409 }
410
411 /*
412  *
413  */
414
415 static void
416 kdc_free_buffer(struct ntlm_buf *sessionkey)
417 {
418     if (sessionkey->data)
419         free(sessionkey->data);
420     sessionkey->data = NULL;
421     sessionkey->length = 0;
422 }
423
424 /*
425  *
426  */
427
428 struct ntlm_server_interface ntlmsspi_kdc_digest = {
429     kdc_alloc,
430     kdc_destroy,
431     kdc_probe,
432     kdc_type2,
433     kdc_type3,
434     kdc_free_buffer
435 };