fix warnings
[heimdal.git] / lib / gssapi / ntlm / init_sec_context.c
blob66e5d477c01e16002351bd8f60e6eb132ce4b605
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;
106 int aret;
108 *username = NULL;
109 krb5_data_zero(&data);
110 key->length = 0;
111 key->data = NULL;
113 ret = krb5_init_context(&context);
114 if (ret)
115 return ret;
117 ret = krb5_cc_default(context, &id);
118 if (ret)
119 goto out;
121 ret = krb5_cc_get_principal(context, id, &client);
122 if (ret)
123 goto out;
125 ret = krb5_unparse_name_flags(context, client,
126 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
127 username);
128 krb5_free_principal(context, client);
129 if (ret)
130 goto out;
132 aret = asprintf(&confname, "ntlm-key-%s", name->domain);
133 if (aret == -1) {
134 krb5_clear_error_message(context);
135 ret = ENOMEM;
136 goto out;
139 ret = krb5_cc_get_config(context, id, NULL,
140 confname, &data);
141 if (ret)
142 goto out;
144 key->data = malloc(data.length);
145 if (key->data == NULL) {
146 ret = ENOMEM;
147 goto out;
149 key->length = data.length;
150 memcpy(key->data, data.data, data.length);
152 out:
153 krb5_data_free(&data);
154 if (id)
155 krb5_cc_close(context, id);
157 krb5_free_context(context);
159 return ret;
163 _gss_ntlm_get_user_cred(const ntlm_name target_name,
164 ntlm_cred *rcred)
166 ntlm_cred cred;
167 int ret;
169 cred = calloc(1, sizeof(*cred));
170 if (cred == NULL)
171 return ENOMEM;
173 ret = get_user_file(target_name, &cred->username, &cred->key);
174 if (ret)
175 ret = get_user_ccache(target_name, &cred->username, &cred->key);
176 if (ret) {
177 free(cred);
178 return ret;
181 cred->domain = strdup(target_name->domain);
182 *rcred = cred;
184 return ret;
187 static int
188 _gss_copy_cred(ntlm_cred from, ntlm_cred *to)
190 *to = calloc(1, sizeof(**to));
191 if (*to == NULL)
192 return ENOMEM;
193 (*to)->username = strdup(from->username);
194 if ((*to)->username == NULL) {
195 free(*to);
196 return ENOMEM;
198 (*to)->domain = strdup(from->domain);
199 if ((*to)->domain == NULL) {
200 free((*to)->username);
201 free(*to);
202 return ENOMEM;
204 (*to)->key.data = malloc(from->key.length);
205 if ((*to)->key.data == NULL) {
206 free((*to)->domain);
207 free((*to)->username);
208 free(*to);
209 return ENOMEM;
211 memcpy((*to)->key.data, from->key.data, from->key.length);
212 (*to)->key.length = from->key.length;
214 return 0;
217 OM_uint32 GSSAPI_CALLCONV
218 _gss_ntlm_init_sec_context
219 (OM_uint32 * minor_status,
220 gss_const_cred_id_t initiator_cred_handle,
221 gss_ctx_id_t * context_handle,
222 gss_const_name_t target_name,
223 const gss_OID mech_type,
224 OM_uint32 req_flags,
225 OM_uint32 time_req,
226 const gss_channel_bindings_t input_chan_bindings,
227 const gss_buffer_t input_token,
228 gss_OID * actual_mech_type,
229 gss_buffer_t output_token,
230 OM_uint32 * ret_flags,
231 OM_uint32 * time_rec
234 ntlm_ctx ctx;
235 ntlm_name name = (ntlm_name)target_name;
237 *minor_status = 0;
239 if (ret_flags)
240 *ret_flags = 0;
241 if (time_rec)
242 *time_rec = 0;
243 if (actual_mech_type)
244 *actual_mech_type = GSS_C_NO_OID;
246 if (*context_handle == GSS_C_NO_CONTEXT) {
247 struct ntlm_type1 type1;
248 struct ntlm_buf data;
249 uint32_t flags = 0;
250 int ret;
252 ctx = calloc(1, sizeof(*ctx));
253 if (ctx == NULL) {
254 *minor_status = EINVAL;
255 return GSS_S_FAILURE;
257 *context_handle = (gss_ctx_id_t)ctx;
259 if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
260 ntlm_cred cred = (ntlm_cred)initiator_cred_handle;
261 ret = _gss_copy_cred(cred, &ctx->client);
262 } else
263 ret = _gss_ntlm_get_user_cred(name, &ctx->client);
265 if (ret) {
266 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
267 *minor_status = ret;
268 return GSS_S_FAILURE;
271 if (req_flags & GSS_C_CONF_FLAG)
272 flags |= NTLM_NEG_SEAL;
273 if (req_flags & GSS_C_INTEG_FLAG)
274 flags |= NTLM_NEG_SIGN;
275 else
276 flags |= NTLM_NEG_ALWAYS_SIGN;
278 flags |= NTLM_NEG_UNICODE;
279 flags |= NTLM_NEG_NTLM;
280 flags |= NTLM_NEG_NTLM2_SESSION;
281 flags |= NTLM_NEG_KEYEX;
283 memset(&type1, 0, sizeof(type1));
285 type1.flags = flags;
286 type1.domain = name->domain;
287 type1.hostname = NULL;
288 type1.os[0] = 0;
289 type1.os[1] = 0;
291 ret = heim_ntlm_encode_type1(&type1, &data);
292 if (ret) {
293 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
294 *minor_status = ret;
295 return GSS_S_FAILURE;
298 output_token->value = data.data;
299 output_token->length = data.length;
301 return GSS_S_CONTINUE_NEEDED;
302 } else {
303 krb5_error_code ret;
304 struct ntlm_type2 type2;
305 struct ntlm_type3 type3;
306 struct ntlm_buf data;
308 ctx = (ntlm_ctx)*context_handle;
310 data.data = input_token->value;
311 data.length = input_token->length;
313 ret = heim_ntlm_decode_type2(&data, &type2);
314 if (ret) {
315 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
316 *minor_status = ret;
317 return GSS_S_FAILURE;
320 ctx->flags = type2.flags;
322 /* XXX check that type2.targetinfo matches `target_name´ */
323 /* XXX check verify targetinfo buffer */
325 memset(&type3, 0, sizeof(type3));
327 type3.username = ctx->client->username;
328 type3.flags = type2.flags;
329 type3.targetname = type2.targetname;
330 type3.ws = rk_UNCONST("workstation");
333 * NTLM Version 1 if no targetinfo buffer.
336 if (1 || type2.targetinfo.length == 0) {
337 struct ntlm_buf sessionkey;
339 if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
340 unsigned char nonce[8];
342 if (RAND_bytes(nonce, sizeof(nonce)) != 1) {
343 _gss_ntlm_delete_sec_context(minor_status,
344 context_handle, NULL);
345 *minor_status = EINVAL;
346 return GSS_S_FAILURE;
349 ret = heim_ntlm_calculate_ntlm2_sess(nonce,
350 type2.challenge,
351 ctx->client->key.data,
352 &type3.lm,
353 &type3.ntlm);
354 } else {
355 ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data,
356 ctx->client->key.length,
357 type2.challenge,
358 &type3.ntlm);
361 if (ret) {
362 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
363 *minor_status = ret;
364 return GSS_S_FAILURE;
367 ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data,
368 ctx->client->key.length,
369 &sessionkey,
370 &type3.sessionkey);
371 if (ret) {
372 if (type3.lm.data)
373 free(type3.lm.data);
374 if (type3.ntlm.data)
375 free(type3.ntlm.data);
376 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
377 *minor_status = ret;
378 return GSS_S_FAILURE;
381 ret = krb5_data_copy(&ctx->sessionkey,
382 sessionkey.data, sessionkey.length);
383 free(sessionkey.data);
384 if (ret) {
385 if (type3.lm.data)
386 free(type3.lm.data);
387 if (type3.ntlm.data)
388 free(type3.ntlm.data);
389 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
390 *minor_status = ret;
391 return GSS_S_FAILURE;
393 ctx->status |= STATUS_SESSIONKEY;
395 } else {
396 struct ntlm_buf sessionkey;
397 unsigned char ntlmv2[16];
398 struct ntlm_targetinfo ti;
400 /* verify infotarget */
402 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
403 if(ret) {
404 _gss_ntlm_delete_sec_context(minor_status,
405 context_handle, NULL);
406 *minor_status = ret;
407 return GSS_S_FAILURE;
410 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) {
411 _gss_ntlm_delete_sec_context(minor_status,
412 context_handle, NULL);
413 *minor_status = EINVAL;
414 return GSS_S_FAILURE;
417 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
418 ctx->client->key.length,
419 ctx->client->username,
420 name->domain,
421 type2.challenge,
422 &type2.targetinfo,
423 ntlmv2,
424 &type3.ntlm);
425 if (ret) {
426 _gss_ntlm_delete_sec_context(minor_status,
427 context_handle, NULL);
428 *minor_status = ret;
429 return GSS_S_FAILURE;
432 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
433 &sessionkey,
434 &type3.sessionkey);
435 memset(ntlmv2, 0, sizeof(ntlmv2));
436 if (ret) {
437 _gss_ntlm_delete_sec_context(minor_status,
438 context_handle, NULL);
439 *minor_status = ret;
440 return GSS_S_FAILURE;
443 ctx->flags |= NTLM_NEG_NTLM2_SESSION;
445 ret = krb5_data_copy(&ctx->sessionkey,
446 sessionkey.data, sessionkey.length);
447 free(sessionkey.data);
448 if (ret) {
449 _gss_ntlm_delete_sec_context(minor_status,
450 context_handle, NULL);
451 *minor_status = ret;
452 return GSS_S_FAILURE;
456 if (ctx->flags & NTLM_NEG_NTLM2_SESSION) {
457 ctx->status |= STATUS_SESSIONKEY;
458 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX),
459 ctx->sessionkey.data,
460 ctx->sessionkey.length);
461 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX),
462 ctx->sessionkey.data,
463 ctx->sessionkey.length);
464 } else {
465 ctx->status |= STATUS_SESSIONKEY;
466 RC4_set_key(&ctx->u.v1.crypto_recv.key,
467 ctx->sessionkey.length,
468 ctx->sessionkey.data);
469 RC4_set_key(&ctx->u.v1.crypto_send.key,
470 ctx->sessionkey.length,
471 ctx->sessionkey.data);
476 ret = heim_ntlm_encode_type3(&type3, &data, NULL);
477 free(type3.sessionkey.data);
478 if (type3.lm.data)
479 free(type3.lm.data);
480 if (type3.ntlm.data)
481 free(type3.ntlm.data);
482 if (ret) {
483 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
484 *minor_status = ret;
485 return GSS_S_FAILURE;
488 output_token->length = data.length;
489 output_token->value = data.data;
491 if (actual_mech_type)
492 *actual_mech_type = GSS_NTLM_MECHANISM;
493 if (ret_flags)
494 *ret_flags = 0;
495 if (time_rec)
496 *time_rec = GSS_C_INDEFINITE;
498 ctx->status |= STATUS_OPEN;
500 return GSS_S_COMPLETE;