]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/lib/gssapi/ntlm/init_sec_context.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 / init_sec_context.c
1 /*
2  * Copyright (c) 2006 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: init_sec_context.c 22382 2007-12-30 12:13:17Z lha $");
37
38 static int
39 from_file(const char *fn, const char *target_domain, 
40           char **username, struct ntlm_buf *key)
41 {         
42     char *str, buf[1024];
43     FILE *f;
44
45     f = fopen(fn, "r");
46     if (f == NULL)
47         return ENOENT;
48
49     while (fgets(buf, sizeof(buf), f) != NULL) {
50         char *d, *u, *p;
51         buf[strcspn(buf, "\r\n")] = '\0';
52         if (buf[0] == '#')
53             continue;
54         str = NULL;
55         d = strtok_r(buf, ":", &str);
56         if (d && strcasecmp(target_domain, d) != 0)
57             continue;
58         u = strtok_r(NULL, ":", &str);
59         p = strtok_r(NULL, ":", &str);
60         if (u == NULL || p == NULL)
61             continue;
62
63         *username = strdup(u);
64
65         heim_ntlm_nt_key(p, key);
66
67         memset(buf, 0, sizeof(buf));
68         fclose(f);
69         return 0;
70     }
71     memset(buf, 0, sizeof(buf));
72     fclose(f);
73     return ENOENT;
74 }
75
76 static int
77 get_user_file(const ntlm_name target_name, 
78               char **username, struct ntlm_buf *key)
79 {
80     const char *fn;
81
82     if (issuid())
83         return ENOENT;
84
85     fn = getenv("NTLM_USER_FILE");
86     if (fn == NULL)
87         return ENOENT;
88     if (from_file(fn, target_name->domain, username, key) == 0)
89         return 0;
90
91     return ENOENT;
92 }
93
94 /*
95  * Pick up the ntlm cred from the default krb5 credential cache.
96  */
97
98 static int
99 get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
100 {
101     krb5_principal client;
102     krb5_context context = NULL;
103     krb5_error_code ret;
104     krb5_ccache id = NULL;
105     krb5_creds mcreds, creds;
106
107     *username = NULL;
108     key->length = 0;
109     key->data = NULL;
110
111     memset(&creds, 0, sizeof(creds));
112     memset(&mcreds, 0, sizeof(mcreds));
113
114     ret = krb5_init_context(&context);
115     if (ret)
116         return ret;
117
118     ret = krb5_cc_default(context, &id);
119     if (ret)
120         goto out;
121
122     ret = krb5_cc_get_principal(context, id, &client);
123     if (ret)
124         goto out;
125
126     ret = krb5_unparse_name_flags(context, client,
127                                   KRB5_PRINCIPAL_UNPARSE_NO_REALM,
128                                   username);
129     if (ret)
130         goto out;
131
132     ret = krb5_make_principal(context, &mcreds.server,
133                               krb5_principal_get_realm(context, client),
134                               "@ntlm-key", name->domain, NULL);
135     krb5_free_principal(context, client);
136     if (ret)
137         goto out;
138
139     mcreds.session.keytype = ENCTYPE_ARCFOUR_HMAC_MD5;
140     ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_MATCH_KEYTYPE, 
141                                 &mcreds, &creds);
142     if (ret) {
143         char *s = krb5_get_error_message(context, ret);
144         krb5_free_error_string(context, s);
145         goto out;
146     }
147
148     key->data = malloc(creds.session.keyvalue.length);
149     if (key->data == NULL)
150         goto out;
151     key->length = creds.session.keyvalue.length;
152     memcpy(key->data, creds.session.keyvalue.data, key->length);
153
154     krb5_free_cred_contents(context, &creds);
155
156     return 0;
157
158 out:
159     if (*username) {
160         free(*username);
161         *username = NULL;
162     }
163     krb5_free_cred_contents(context, &creds);
164     if (mcreds.server)
165         krb5_free_principal(context, mcreds.server);
166     if (id)
167         krb5_cc_close(context, id);
168     if (context)
169         krb5_free_context(context);
170
171     return ret;
172 }
173
174 int
175 _gss_ntlm_get_user_cred(const ntlm_name target_name,
176                         ntlm_cred *rcred)
177 {
178     ntlm_cred cred;
179     int ret;
180  
181     cred = calloc(1, sizeof(*cred));
182     if (cred == NULL)
183         return ENOMEM;
184     
185     ret = get_user_file(target_name, &cred->username, &cred->key);
186     if (ret)
187         ret = get_user_ccache(target_name, &cred->username, &cred->key);
188     if (ret) {
189         free(cred);
190         return ret;
191     }
192     
193     cred->domain = strdup(target_name->domain);
194     *rcred = cred;
195
196     return ret;
197 }
198
199 static int
200 _gss_copy_cred(ntlm_cred from, ntlm_cred *to)
201 {
202     *to = calloc(1, sizeof(*to));
203     if (*to == NULL)
204         return ENOMEM;
205     (*to)->username = strdup(from->username);
206     if ((*to)->username == NULL) {
207         free(*to);
208         return ENOMEM;
209     }
210     (*to)->domain = strdup(from->domain);
211     if ((*to)->domain == NULL) {
212         free((*to)->username);
213         free(*to);
214         return ENOMEM;
215     }
216     (*to)->key.data = malloc(from->key.length);
217     if ((*to)->key.data == NULL) {
218         free((*to)->domain);
219         free((*to)->username);
220         free(*to);
221         return ENOMEM;
222     }
223     memcpy((*to)->key.data, from->key.data, from->key.length);
224     (*to)->key.length = from->key.length;
225
226     return 0;
227 }
228
229 OM_uint32
230 _gss_ntlm_init_sec_context
231            (OM_uint32 * minor_status,
232             const gss_cred_id_t initiator_cred_handle,
233             gss_ctx_id_t * context_handle,
234             const gss_name_t target_name,
235             const gss_OID mech_type,
236             OM_uint32 req_flags,
237             OM_uint32 time_req,
238             const gss_channel_bindings_t input_chan_bindings,
239             const gss_buffer_t input_token,
240             gss_OID * actual_mech_type,
241             gss_buffer_t output_token,
242             OM_uint32 * ret_flags,
243             OM_uint32 * time_rec
244            )
245 {
246     ntlm_ctx ctx;
247     ntlm_name name = (ntlm_name)target_name;
248
249     *minor_status = 0;
250
251     if (ret_flags)
252         *ret_flags = 0;
253     if (time_rec)
254         *time_rec = 0;
255     if (actual_mech_type)
256         *actual_mech_type = GSS_C_NO_OID;
257
258     if (*context_handle == GSS_C_NO_CONTEXT) {
259         struct ntlm_type1 type1;
260         struct ntlm_buf data;
261         uint32_t flags = 0;
262         int ret;
263         
264         ctx = calloc(1, sizeof(*ctx));
265         if (ctx == NULL) {
266             *minor_status = EINVAL;
267             return GSS_S_FAILURE;
268         }
269         *context_handle = (gss_ctx_id_t)ctx;
270
271         if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
272             ntlm_cred cred = (ntlm_cred)initiator_cred_handle;
273             ret = _gss_copy_cred(cred, &ctx->client);
274         } else
275             ret = _gss_ntlm_get_user_cred(name, &ctx->client);
276
277         if (ret) {
278             _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
279             *minor_status = ret;
280             return GSS_S_FAILURE;
281         }
282
283         if (req_flags & GSS_C_CONF_FLAG)
284             flags |= NTLM_NEG_SEAL;
285         if (req_flags & GSS_C_INTEG_FLAG)
286             flags |= NTLM_NEG_SIGN;
287         else
288             flags |= NTLM_NEG_ALWAYS_SIGN;
289
290         flags |= NTLM_NEG_UNICODE;
291         flags |= NTLM_NEG_NTLM;
292         flags |= NTLM_NEG_NTLM2_SESSION;
293         flags |= NTLM_NEG_KEYEX;
294
295         memset(&type1, 0, sizeof(type1));
296         
297         type1.flags = flags;
298         type1.domain = name->domain;
299         type1.hostname = NULL;
300         type1.os[0] = 0;
301         type1.os[1] = 0;
302         
303         ret = heim_ntlm_encode_type1(&type1, &data);
304         if (ret) {
305             _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
306             *minor_status = ret;
307             return GSS_S_FAILURE;
308         }
309         
310         output_token->value = data.data;
311         output_token->length = data.length;
312         
313         return GSS_S_CONTINUE_NEEDED;
314     } else {
315         krb5_error_code ret;
316         struct ntlm_type2 type2;
317         struct ntlm_type3 type3;
318         struct ntlm_buf data;
319
320         ctx = (ntlm_ctx)*context_handle;
321
322         data.data = input_token->value;
323         data.length = input_token->length;
324
325         ret = heim_ntlm_decode_type2(&data, &type2);
326         if (ret) {
327             _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
328             *minor_status = ret;
329             return GSS_S_FAILURE;
330         }
331
332         ctx->flags = type2.flags;
333
334         /* XXX check that type2.targetinfo matches `target_name´ */
335         /* XXX check verify targetinfo buffer */
336
337         memset(&type3, 0, sizeof(type3));
338
339         type3.username = ctx->client->username;
340         type3.flags = type2.flags;
341         type3.targetname = type2.targetname;
342         type3.ws = rk_UNCONST("workstation");
343
344         /*
345          * NTLM Version 1 if no targetinfo buffer.
346          */
347
348         if (1 || type2.targetinfo.length == 0) {
349             struct ntlm_buf sessionkey;
350
351             if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
352                 unsigned char nonce[8];
353
354                 if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
355                     _gss_ntlm_delete_sec_context(minor_status, 
356                                                  context_handle, NULL);
357                     *minor_status = EINVAL;
358                     return GSS_S_FAILURE;
359                 }
360
361                 ret = heim_ntlm_calculate_ntlm2_sess(nonce,
362                                                      type2.challange,
363                                                      ctx->client->key.data,
364                                                      &type3.lm,
365                                                      &type3.ntlm);
366             } else {
367                 ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data, 
368                                                 ctx->client->key.length,
369                                                 type2.challange,
370                                                 &type3.ntlm);
371
372             }
373             if (ret) {
374                 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
375                 *minor_status = ret;
376                 return GSS_S_FAILURE;
377             }
378
379             ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data, 
380                                                ctx->client->key.length,
381                                                &sessionkey,
382                                                &type3.sessionkey);
383             if (ret) {
384                 if (type3.lm.data)
385                     free(type3.lm.data);
386                 if (type3.ntlm.data)
387                     free(type3.ntlm.data);
388                 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
389                 *minor_status = ret;
390                 return GSS_S_FAILURE;
391             }
392
393             ret = krb5_data_copy(&ctx->sessionkey, 
394                                  sessionkey.data, sessionkey.length);
395             free(sessionkey.data);
396             if (ret) {
397                 if (type3.lm.data)
398                     free(type3.lm.data);
399                 if (type3.ntlm.data)
400                     free(type3.ntlm.data);
401                 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
402                 *minor_status = ret;
403                 return GSS_S_FAILURE;
404             }
405             ctx->status |= STATUS_SESSIONKEY; 
406
407         } else {
408             struct ntlm_buf sessionkey;
409             unsigned char ntlmv2[16];
410             struct ntlm_targetinfo ti;
411
412             /* verify infotarget */
413             
414             ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
415             if(ret) {
416                 _gss_ntlm_delete_sec_context(minor_status, 
417                                              context_handle, NULL);
418                 *minor_status = ret;
419                 return GSS_S_FAILURE;
420             }
421
422             if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
423                 _gss_ntlm_delete_sec_context(minor_status, 
424                                              context_handle, NULL);
425                 *minor_status = EINVAL;
426                 return GSS_S_FAILURE;
427             }
428
429             ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
430                                             ctx->client->key.length,
431                                             ctx->client->username,
432                                             name->domain,
433                                             type2.challange,
434                                             &type2.targetinfo,
435                                             ntlmv2,
436                                             &type3.ntlm);
437             if (ret) {
438                 _gss_ntlm_delete_sec_context(minor_status, 
439                                              context_handle, NULL);
440                 *minor_status = ret;
441                 return GSS_S_FAILURE;
442             }
443
444             ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
445                                                &sessionkey,
446                                                &type3.sessionkey);
447             memset(ntlmv2, 0, sizeof(ntlmv2));
448             if (ret) {
449                 _gss_ntlm_delete_sec_context(minor_status, 
450                                              context_handle, NULL);
451                 *minor_status = ret;
452                 return GSS_S_FAILURE;
453             }
454             
455             ctx->flags |= NTLM_NEG_NTLM2_SESSION;
456
457             ret = krb5_data_copy(&ctx->sessionkey, 
458                                  sessionkey.data, sessionkey.length);
459             free(sessionkey.data);
460         }
461
462         if (ctx->flags & NTLM_NEG_NTLM2_SESSION) {
463             ctx->status |= STATUS_SESSIONKEY; 
464             _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
465                               ctx->sessionkey.data,
466                               ctx->sessionkey.length);
467             _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
468                               ctx->sessionkey.data,
469                               ctx->sessionkey.length);
470         } else {
471             ctx->status |= STATUS_SESSIONKEY; 
472             RC4_set_key(&ctx->u.v1.crypto_recv.key, 
473                         ctx->sessionkey.length,
474                         ctx->sessionkey.data);
475             RC4_set_key(&ctx->u.v1.crypto_send.key, 
476                         ctx->sessionkey.length,
477                         ctx->sessionkey.data);
478         }
479         
480
481
482         ret = heim_ntlm_encode_type3(&type3, &data);
483         free(type3.sessionkey.data);
484         if (type3.lm.data)
485             free(type3.lm.data);
486         if (type3.ntlm.data)
487             free(type3.ntlm.data);
488         if (ret) {
489             _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
490             *minor_status = ret;
491             return GSS_S_FAILURE;
492         }
493
494         output_token->length = data.length;
495         output_token->value = data.data;
496
497         if (actual_mech_type)
498             *actual_mech_type = GSS_NTLM_MECHANISM;
499         if (ret_flags)
500             *ret_flags = 0;
501         if (time_rec)
502             *time_rec = GSS_C_INDEFINITE;
503
504         ctx->status |= STATUS_OPEN;
505
506         return GSS_S_COMPLETE;
507     }
508 }