s4:torture: Adapt KDC canon test to Heimdal upstream changes
[samba.git] / third_party / 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     const char *cache = secure_getenv("NTLM_ACCEPTOR_CCACHE");
68
69     *id = NULL;
70
71     if (cache) {
72         ret = krb5_cc_resolve(context, cache, id);
73         if (ret)
74             goto out;
75         return 0;
76     }
77
78     ret = krb5_sname_to_principal(context, NULL, "host",
79                                   KRB5_NT_SRV_HST, &principal);
80     if (ret)
81         goto out;
82
83     ret = krb5_cc_cache_match(context, principal, id);
84     if (ret == 0)
85         return 0;
86
87     /* did not find in default credcache, lets try default keytab */
88     ret = krb5_kt_default(context, &kt);
89     if (ret)
90         goto out;
91
92     /* XXX check in keytab */
93     {
94         krb5_get_init_creds_opt *opt;
95         krb5_creds cred;
96
97         memset(&cred, 0, sizeof(cred));
98
99         ret = krb5_cc_new_unique(context, "MEMORY", NULL, id);
100         if (ret)
101             goto out;
102         *destroy = 1;
103         ret = krb5_get_init_creds_opt_alloc(context, &opt);
104         if (ret)
105             goto out;
106         ret = krb5_get_init_creds_keytab (context,
107                                           &cred,
108                                           principal,
109                                           kt,
110                                           0,
111                                           NULL,
112                                           opt);
113         krb5_get_init_creds_opt_free(context, opt);
114         if (ret)
115             goto out;
116         ret = krb5_cc_initialize (context, *id, cred.client);
117         if (ret) {
118             krb5_free_cred_contents (context, &cred);
119             goto out;
120         }
121         ret = krb5_cc_store_cred (context, *id, &cred);
122         krb5_free_cred_contents (context, &cred);
123         if (ret)
124             goto out;
125     }
126
127     krb5_kt_close(context, kt);
128
129     return 0;
130
131 out:
132     if (*id) {
133         if (*destroy)
134             krb5_cc_destroy(context, *id);
135         else
136             krb5_cc_close(context, *id);
137         *id = NULL;
138     }
139
140     if (kt)
141         krb5_kt_close(context, kt);
142
143     if (principal)
144         krb5_free_principal(context, principal);
145     return ret;
146 }
147
148 /*
149  *
150  */
151
152 static OM_uint32
153 kdc_alloc(OM_uint32 *minor, void **ctx)
154 {
155     krb5_error_code ret;
156     struct ntlmkrb5 *c;
157     OM_uint32 junk;
158
159     c = calloc(1, sizeof(*c));
160     if (c == NULL) {
161         *minor = ENOMEM;
162         return GSS_S_FAILURE;
163     }
164
165     ret = krb5_init_context(&c->context);
166     if (ret) {
167         kdc_destroy(&junk, c);
168         *minor = ret;
169         return GSS_S_FAILURE;
170     }
171
172     ret = get_ccache(c->context, &c->destroy, &c->id);
173     if (ret) {
174         kdc_destroy(&junk, c);
175         *minor = ret;
176         return GSS_S_FAILURE;
177     }
178
179     ret = krb5_ntlm_alloc(c->context, &c->ntlm);
180     if (ret) {
181         kdc_destroy(&junk, c);
182         *minor = ret;
183         return GSS_S_FAILURE;
184     }
185
186     *ctx = c;
187
188     return GSS_S_COMPLETE;
189 }
190
191 static int
192 kdc_probe(OM_uint32 *minor, void *ctx, const char *realm)
193 {
194     struct ntlmkrb5 *c = ctx;
195     krb5_error_code ret;
196     unsigned flags;
197
198     ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags);
199     if (ret)
200         return ret;
201
202     if ((flags & (1|2|4)) == 0)
203         return EINVAL;
204
205     return 0;
206 }
207
208 /*
209  *
210  */
211
212 static OM_uint32
213 kdc_destroy(OM_uint32 *minor, void *ctx)
214 {
215     struct ntlmkrb5 *c = ctx;
216     krb5_data_free(&c->opaque);
217     krb5_data_free(&c->sessionkey);
218     if (c->ntlm)
219         krb5_ntlm_free(c->context, c->ntlm);
220     if (c->id) {
221         if (c->destroy)
222             krb5_cc_destroy(c->context, c->id);
223         else
224             krb5_cc_close(c->context, c->id);
225     }
226     if (c->context)
227         krb5_free_context(c->context);
228     memset(c, 0, sizeof(*c));
229     free(c);
230
231     return GSS_S_COMPLETE;
232 }
233
234 /*
235  *
236  */
237
238 static OM_uint32
239 kdc_type2(OM_uint32 *minor_status,
240           void *ctx,
241           uint32_t flags,
242           const char *hostname,
243           const char *domain,
244           uint32_t *ret_flags,
245           struct ntlm_buf *out)
246 {
247     struct ntlmkrb5 *c = ctx;
248     krb5_error_code ret;
249     struct ntlm_type2 type2;
250     krb5_data challenge;
251     struct ntlm_buf data;
252     krb5_data ti;
253
254     memset(&type2, 0, sizeof(type2));
255
256     /*
257      * Request data for type 2 packet from the KDC.
258      */
259     ret = krb5_ntlm_init_request(c->context,
260                                  c->ntlm,
261                                  NULL,
262                                  c->id,
263                                  flags,
264                                  hostname,
265                                  domain);
266     if (ret) {
267         *minor_status = ret;
268         return GSS_S_FAILURE;
269     }
270
271     /*
272      *
273      */
274
275     ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque);
276     if (ret) {
277         *minor_status = ret;
278         return GSS_S_FAILURE;
279     }
280
281     /*
282      *
283      */
284
285     ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags);
286     if (ret) {
287         *minor_status = ret;
288         return GSS_S_FAILURE;
289     }
290     *ret_flags = type2.flags;
291
292     ret = krb5_ntlm_init_get_challenge(c->context, c->ntlm, &challenge);
293     if (ret) {
294         *minor_status = ret;
295         return GSS_S_FAILURE;
296     }
297
298     if (challenge.length != sizeof(type2.challenge)) {
299         *minor_status = EINVAL;
300         return GSS_S_FAILURE;
301     }
302     memcpy(type2.challenge, challenge.data, sizeof(type2.challenge));
303     krb5_data_free(&challenge);
304
305     ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm,
306                                         &type2.targetname);
307     if (ret) {
308         *minor_status = ret;
309         return GSS_S_FAILURE;
310     }
311
312     ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti);
313     if (ret) {
314         free(type2.targetname);
315         *minor_status = ret;
316         return GSS_S_FAILURE;
317     }
318
319     type2.targetinfo.data = ti.data;
320     type2.targetinfo.length = ti.length;
321
322     ret = heim_ntlm_encode_type2(&type2, &data);
323     free(type2.targetname);
324     krb5_data_free(&ti);
325     if (ret) {
326         *minor_status = ret;
327         return GSS_S_FAILURE;
328     }
329
330     out->data = data.data;
331     out->length = data.length;
332
333     return GSS_S_COMPLETE;
334 }
335
336 /*
337  *
338  */
339
340 static OM_uint32
341 kdc_type3(OM_uint32 *minor_status,
342           void *ctx,
343           const struct ntlm_type3 *type3,
344           struct ntlm_buf *sessionkey)
345 {
346     struct ntlmkrb5 *c = ctx;
347     krb5_error_code ret;
348
349     sessionkey->data = NULL;
350     sessionkey->length = 0;
351
352     ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags);
353     if (ret) goto out;
354     ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username);
355     if (ret) goto out;
356     ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm,
357                                        type3->targetname);
358     if (ret) goto out;
359     ret = krb5_ntlm_req_set_lm(c->context, c->ntlm,
360                                type3->lm.data, type3->lm.length);
361     if (ret) goto out;
362     ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm,
363                                  type3->ntlm.data, type3->ntlm.length);
364     if (ret) goto out;
365     ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque);
366     if (ret) goto out;
367
368     if (type3->sessionkey.length) {
369         ret = krb5_ntlm_req_set_session(c->context, c->ntlm,
370                                         type3->sessionkey.data,
371                                         type3->sessionkey.length);
372         if (ret) goto out;
373     }
374
375     /*
376      * Verify with the KDC the type3 packet is ok
377      */
378     ret = krb5_ntlm_request(c->context,
379                             c->ntlm,
380                             NULL,
381                             c->id);
382     if (ret)
383         goto out;
384
385     if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) {
386         ret = EINVAL;
387         goto out;
388     }
389
390     if (type3->sessionkey.length) {
391         ret = krb5_ntlm_rep_get_sessionkey(c->context,
392                                            c->ntlm,
393                                            &c->sessionkey);
394         if (ret)
395             goto out;
396
397         sessionkey->data = c->sessionkey.data;
398         sessionkey->length = c->sessionkey.length;
399     }
400
401     return 0;
402
403  out:
404     *minor_status = ret;
405     return GSS_S_FAILURE;
406 }
407
408 /*
409  *
410  */
411
412 static void
413 kdc_free_buffer(struct ntlm_buf *sessionkey)
414 {
415     if (sessionkey->data)
416         free(sessionkey->data);
417     sessionkey->data = NULL;
418     sessionkey->length = 0;
419 }
420
421 /*
422  *
423  */
424
425 struct ntlm_server_interface ntlmsspi_kdc_digest = {
426     kdc_alloc,
427     kdc_destroy,
428     kdc_probe,
429     kdc_type2,
430     kdc_type3,
431     kdc_free_buffer
432 };
433
434 #endif /* DIGEST */