allocate enough memory
[heimdal.git] / lib / gssapi / ntlm / init_sec_context.c
blobbae04e174060e6af36fee6759bc6ed0d3397d63b
1 /*
2 * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "ntlm.h"
36 static int
37 from_file(const char *fn, const char *target_domain,
38 char **username, struct ntlm_buf *key)
40 char *str, buf[1024];
41 FILE *f;
43 f = fopen(fn, "r");
44 if (f == NULL)
45 return ENOENT;
46 rk_cloexec_file(f);
48 while (fgets(buf, sizeof(buf), f) != NULL) {
49 char *d, *u, *p;
50 buf[strcspn(buf, "\r\n")] = '\0';
51 if (buf[0] == '#')
52 continue;
53 str = NULL;
54 d = strtok_r(buf, ":", &str);
55 if (d && strcasecmp(target_domain, d) != 0)
56 continue;
57 u = strtok_r(NULL, ":", &str);
58 p = strtok_r(NULL, ":", &str);
59 if (u == NULL || p == NULL)
60 continue;
62 *username = strdup(u);
64 heim_ntlm_nt_key(p, key);
66 memset(buf, 0, sizeof(buf));
67 fclose(f);
68 return 0;
70 memset(buf, 0, sizeof(buf));
71 fclose(f);
72 return ENOENT;
75 static int
76 get_user_file(const ntlm_name target_name,
77 char **username, struct ntlm_buf *key)
79 const char *fn;
81 if (issuid())
82 return ENOENT;
84 fn = getenv("NTLM_USER_FILE");
85 if (fn == NULL)
86 return ENOENT;
87 if (from_file(fn, target_name->domain, username, key) == 0)
88 return 0;
90 return ENOENT;
94 * Pick up the ntlm cred from the default krb5 credential cache.
97 static int
98 get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
100 krb5_context context = NULL;
101 krb5_principal client;
102 krb5_ccache id = NULL;
103 krb5_error_code ret;
104 char *confname;
105 krb5_data data;
107 *username = NULL;
108 krb5_data_zero(&data);
109 key->length = 0;
110 key->data = NULL;
112 ret = krb5_init_context(&context);
113 if (ret)
114 return ret;
116 ret = krb5_cc_default(context, &id);
117 if (ret)
118 goto out;
120 ret = krb5_cc_get_principal(context, id, &client);
121 if (ret)
122 goto out;
124 ret = krb5_unparse_name_flags(context, client,
125 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
126 username);
127 krb5_free_principal(context, client);
128 if (ret)
129 goto out;
131 asprintf(&confname, "ntlm-key-%s", name->domain);
132 if (confname == NULL) {
133 krb5_clear_error_message(context);
134 ret = ENOMEM;
135 goto out;
138 ret = krb5_cc_get_config(context, id, NULL,
139 confname, &data);
140 if (ret)
141 goto out;
143 key->data = malloc(data.length);
144 if (key->data == NULL) {
145 ret = ENOMEM;
146 goto out;
148 key->length = data.length;
149 memcpy(key->data, data.data, data.length);
151 out:
152 krb5_data_free(&data);
153 if (id)
154 krb5_cc_close(context, id);
156 krb5_free_context(context);
158 return ret;
162 _gss_ntlm_get_user_cred(const ntlm_name target_name,
163 ntlm_cred *rcred)
165 ntlm_cred cred;
166 int ret;
168 cred = calloc(1, sizeof(*cred));
169 if (cred == NULL)
170 return ENOMEM;
172 ret = get_user_file(target_name, &cred->username, &cred->key);
173 if (ret)
174 ret = get_user_ccache(target_name, &cred->username, &cred->key);
175 if (ret) {
176 free(cred);
177 return ret;
180 cred->domain = strdup(target_name->domain);
181 *rcred = cred;
183 return ret;
186 static int
187 _gss_copy_cred(ntlm_cred from, ntlm_cred *to)
189 *to = calloc(1, sizeof(**to));
190 if (*to == NULL)
191 return ENOMEM;
192 (*to)->username = strdup(from->username);
193 if ((*to)->username == NULL) {
194 free(*to);
195 return ENOMEM;
197 (*to)->domain = strdup(from->domain);
198 if ((*to)->domain == NULL) {
199 free((*to)->username);
200 free(*to);
201 return ENOMEM;
203 (*to)->key.data = malloc(from->key.length);
204 if ((*to)->key.data == NULL) {
205 free((*to)->domain);
206 free((*to)->username);
207 free(*to);
208 return ENOMEM;
210 memcpy((*to)->key.data, from->key.data, from->key.length);
211 (*to)->key.length = from->key.length;
213 return 0;
216 OM_uint32 GSSAPI_CALLCONV
217 _gss_ntlm_init_sec_context
218 (OM_uint32 * minor_status,
219 const gss_cred_id_t initiator_cred_handle,
220 gss_ctx_id_t * context_handle,
221 const gss_name_t target_name,
222 const gss_OID mech_type,
223 OM_uint32 req_flags,
224 OM_uint32 time_req,
225 const gss_channel_bindings_t input_chan_bindings,
226 const gss_buffer_t input_token,
227 gss_OID * actual_mech_type,
228 gss_buffer_t output_token,
229 OM_uint32 * ret_flags,
230 OM_uint32 * time_rec
233 ntlm_ctx ctx;
234 ntlm_name name = (ntlm_name)target_name;
236 *minor_status = 0;
238 if (ret_flags)
239 *ret_flags = 0;
240 if (time_rec)
241 *time_rec = 0;
242 if (actual_mech_type)
243 *actual_mech_type = GSS_C_NO_OID;
245 if (*context_handle == GSS_C_NO_CONTEXT) {
246 struct ntlm_type1 type1;
247 struct ntlm_buf data;
248 uint32_t flags = 0;
249 int ret;
251 ctx = calloc(1, sizeof(*ctx));
252 if (ctx == NULL) {
253 *minor_status = EINVAL;
254 return GSS_S_FAILURE;
256 *context_handle = (gss_ctx_id_t)ctx;
258 if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
259 ntlm_cred cred = (ntlm_cred)initiator_cred_handle;
260 ret = _gss_copy_cred(cred, &ctx->client);
261 } else
262 ret = _gss_ntlm_get_user_cred(name, &ctx->client);
264 if (ret) {
265 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
266 *minor_status = ret;
267 return GSS_S_FAILURE;
270 if (req_flags & GSS_C_CONF_FLAG)
271 flags |= NTLM_NEG_SEAL;
272 if (req_flags & GSS_C_INTEG_FLAG)
273 flags |= NTLM_NEG_SIGN;
274 else
275 flags |= NTLM_NEG_ALWAYS_SIGN;
277 flags |= NTLM_NEG_UNICODE;
278 flags |= NTLM_NEG_NTLM;
279 flags |= NTLM_NEG_NTLM2_SESSION;
280 flags |= NTLM_NEG_KEYEX;
282 memset(&type1, 0, sizeof(type1));
284 type1.flags = flags;
285 type1.domain = name->domain;
286 type1.hostname = NULL;
287 type1.os[0] = 0;
288 type1.os[1] = 0;
290 ret = heim_ntlm_encode_type1(&type1, &data);
291 if (ret) {
292 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
293 *minor_status = ret;
294 return GSS_S_FAILURE;
297 output_token->value = data.data;
298 output_token->length = data.length;
300 return GSS_S_CONTINUE_NEEDED;
301 } else {
302 krb5_error_code ret;
303 struct ntlm_type2 type2;
304 struct ntlm_type3 type3;
305 struct ntlm_buf data;
307 ctx = (ntlm_ctx)*context_handle;
309 data.data = input_token->value;
310 data.length = input_token->length;
312 ret = heim_ntlm_decode_type2(&data, &type2);
313 if (ret) {
314 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
315 *minor_status = ret;
316 return GSS_S_FAILURE;
319 ctx->flags = type2.flags;
321 /* XXX check that type2.targetinfo matches `target_name´ */
322 /* XXX check verify targetinfo buffer */
324 memset(&type3, 0, sizeof(type3));
326 type3.username = ctx->client->username;
327 type3.flags = type2.flags;
328 type3.targetname = type2.targetname;
329 type3.ws = rk_UNCONST("workstation");
332 * NTLM Version 1 if no targetinfo buffer.
335 if (1 || type2.targetinfo.length == 0) {
336 struct ntlm_buf sessionkey;
338 if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
339 unsigned char nonce[8];
341 if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
342 _gss_ntlm_delete_sec_context(minor_status,
343 context_handle, NULL);
344 *minor_status = EINVAL;
345 return GSS_S_FAILURE;
348 ret = heim_ntlm_calculate_ntlm2_sess(nonce,
349 type2.challenge,
350 ctx->client->key.data,
351 &type3.lm,
352 &type3.ntlm);
353 } else {
354 ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data,
355 ctx->client->key.length,
356 type2.challenge,
357 &type3.ntlm);
360 if (ret) {
361 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
362 *minor_status = ret;
363 return GSS_S_FAILURE;
366 ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data,
367 ctx->client->key.length,
368 &sessionkey,
369 &type3.sessionkey);
370 if (ret) {
371 if (type3.lm.data)
372 free(type3.lm.data);
373 if (type3.ntlm.data)
374 free(type3.ntlm.data);
375 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
376 *minor_status = ret;
377 return GSS_S_FAILURE;
380 ret = krb5_data_copy(&ctx->sessionkey,
381 sessionkey.data, sessionkey.length);
382 free(sessionkey.data);
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;
392 ctx->status |= STATUS_SESSIONKEY;
394 } else {
395 struct ntlm_buf sessionkey;
396 unsigned char ntlmv2[16];
397 struct ntlm_targetinfo ti;
399 /* verify infotarget */
401 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
402 if(ret) {
403 _gss_ntlm_delete_sec_context(minor_status,
404 context_handle, NULL);
405 *minor_status = ret;
406 return GSS_S_FAILURE;
409 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
410 _gss_ntlm_delete_sec_context(minor_status,
411 context_handle, NULL);
412 *minor_status = EINVAL;
413 return GSS_S_FAILURE;
416 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
417 ctx->client->key.length,
418 ctx->client->username,
419 name->domain,
420 type2.challenge,
421 &type2.targetinfo,
422 ntlmv2,
423 &type3.ntlm);
424 if (ret) {
425 _gss_ntlm_delete_sec_context(minor_status,
426 context_handle, NULL);
427 *minor_status = ret;
428 return GSS_S_FAILURE;
431 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
432 &sessionkey,
433 &type3.sessionkey);
434 memset(ntlmv2, 0, sizeof(ntlmv2));
435 if (ret) {
436 _gss_ntlm_delete_sec_context(minor_status,
437 context_handle, NULL);
438 *minor_status = ret;
439 return GSS_S_FAILURE;
442 ctx->flags |= NTLM_NEG_NTLM2_SESSION;
444 ret = krb5_data_copy(&ctx->sessionkey,
445 sessionkey.data, sessionkey.length);
446 free(sessionkey.data);
447 if (ret) {
448 _gss_ntlm_delete_sec_context(minor_status,
449 context_handle, NULL);
450 *minor_status = ret;
451 return GSS_S_FAILURE;
455 if (ctx->flags & NTLM_NEG_NTLM2_SESSION) {
456 ctx->status |= STATUS_SESSIONKEY;
457 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
458 ctx->sessionkey.data,
459 ctx->sessionkey.length);
460 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
461 ctx->sessionkey.data,
462 ctx->sessionkey.length);
463 } else {
464 ctx->status |= STATUS_SESSIONKEY;
465 RC4_set_key(&ctx->u.v1.crypto_recv.key,
466 ctx->sessionkey.length,
467 ctx->sessionkey.data);
468 RC4_set_key(&ctx->u.v1.crypto_send.key,
469 ctx->sessionkey.length,
470 ctx->sessionkey.data);
475 ret = heim_ntlm_encode_type3(&type3, &data);
476 free(type3.sessionkey.data);
477 if (type3.lm.data)
478 free(type3.lm.data);
479 if (type3.ntlm.data)
480 free(type3.ntlm.data);
481 if (ret) {
482 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
483 *minor_status = ret;
484 return GSS_S_FAILURE;
487 output_token->length = data.length;
488 output_token->value = data.data;
490 if (actual_mech_type)
491 *actual_mech_type = GSS_NTLM_MECHANISM;
492 if (ret_flags)
493 *ret_flags = 0;
494 if (time_rec)
495 *time_rec = GSS_C_INDEFINITE;
497 ctx->status |= STATUS_OPEN;
499 return GSS_S_COMPLETE;