2 * Copyright (c) 2006 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
34 #include "ntlm/ntlm.h"
36 RCSID("$Id: init_sec_context.c 22382 2007-12-30 12:13:17Z lha $");
39 from_file(const char *fn, const char *target_domain,
40 char **username, struct ntlm_buf *key)
49 while (fgets(buf, sizeof(buf), f) != NULL) {
51 buf[strcspn(buf, "\r\n")] = '\0';
55 d = strtok_r(buf, ":", &str);
56 if (d && strcasecmp(target_domain, d) != 0)
58 u = strtok_r(NULL, ":", &str);
59 p = strtok_r(NULL, ":", &str);
60 if (u == NULL || p == NULL)
63 *username = strdup(u);
65 heim_ntlm_nt_key(p, key);
67 memset(buf, 0, sizeof(buf));
71 memset(buf, 0, sizeof(buf));
77 get_user_file(const ntlm_name target_name,
78 char **username, struct ntlm_buf *key)
85 fn = getenv("NTLM_USER_FILE");
88 if (from_file(fn, target_name->domain, username, key) == 0)
95 * Pick up the ntlm cred from the default krb5 credential cache.
99 get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
101 krb5_principal client;
102 krb5_context context = NULL;
104 krb5_ccache id = NULL;
105 krb5_creds mcreds, creds;
111 memset(&creds, 0, sizeof(creds));
112 memset(&mcreds, 0, sizeof(mcreds));
114 ret = krb5_init_context(&context);
118 ret = krb5_cc_default(context, &id);
122 ret = krb5_cc_get_principal(context, id, &client);
126 ret = krb5_unparse_name_flags(context, client,
127 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
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);
139 mcreds.session.keytype = ENCTYPE_ARCFOUR_HMAC_MD5;
140 ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_MATCH_KEYTYPE,
143 char *s = krb5_get_error_message(context, ret);
144 krb5_free_error_string(context, s);
148 key->data = malloc(creds.session.keyvalue.length);
149 if (key->data == NULL)
151 key->length = creds.session.keyvalue.length;
152 memcpy(key->data, creds.session.keyvalue.data, key->length);
154 krb5_free_cred_contents(context, &creds);
163 krb5_free_cred_contents(context, &creds);
165 krb5_free_principal(context, mcreds.server);
167 krb5_cc_close(context, id);
169 krb5_free_context(context);
175 _gss_ntlm_get_user_cred(const ntlm_name target_name,
181 cred = calloc(1, sizeof(*cred));
185 ret = get_user_file(target_name, &cred->username, &cred->key);
187 ret = get_user_ccache(target_name, &cred->username, &cred->key);
193 cred->domain = strdup(target_name->domain);
200 _gss_copy_cred(ntlm_cred from, ntlm_cred *to)
202 *to = calloc(1, sizeof(*to));
205 (*to)->username = strdup(from->username);
206 if ((*to)->username == NULL) {
210 (*to)->domain = strdup(from->domain);
211 if ((*to)->domain == NULL) {
212 free((*to)->username);
216 (*to)->key.data = malloc(from->key.length);
217 if ((*to)->key.data == NULL) {
219 free((*to)->username);
223 memcpy((*to)->key.data, from->key.data, from->key.length);
224 (*to)->key.length = from->key.length;
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,
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,
247 ntlm_name name = (ntlm_name)target_name;
255 if (actual_mech_type)
256 *actual_mech_type = GSS_C_NO_OID;
258 if (*context_handle == GSS_C_NO_CONTEXT) {
259 struct ntlm_type1 type1;
260 struct ntlm_buf data;
264 ctx = calloc(1, sizeof(*ctx));
266 *minor_status = EINVAL;
267 return GSS_S_FAILURE;
269 *context_handle = (gss_ctx_id_t)ctx;
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);
275 ret = _gss_ntlm_get_user_cred(name, &ctx->client);
278 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
280 return GSS_S_FAILURE;
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;
288 flags |= NTLM_NEG_ALWAYS_SIGN;
290 flags |= NTLM_NEG_UNICODE;
291 flags |= NTLM_NEG_NTLM;
292 flags |= NTLM_NEG_NTLM2_SESSION;
293 flags |= NTLM_NEG_KEYEX;
295 memset(&type1, 0, sizeof(type1));
298 type1.domain = name->domain;
299 type1.hostname = NULL;
303 ret = heim_ntlm_encode_type1(&type1, &data);
305 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
307 return GSS_S_FAILURE;
310 output_token->value = data.data;
311 output_token->length = data.length;
313 return GSS_S_CONTINUE_NEEDED;
316 struct ntlm_type2 type2;
317 struct ntlm_type3 type3;
318 struct ntlm_buf data;
320 ctx = (ntlm_ctx)*context_handle;
322 data.data = input_token->value;
323 data.length = input_token->length;
325 ret = heim_ntlm_decode_type2(&data, &type2);
327 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
329 return GSS_S_FAILURE;
332 ctx->flags = type2.flags;
334 /* XXX check that type2.targetinfo matches `target_name´ */
335 /* XXX check verify targetinfo buffer */
337 memset(&type3, 0, sizeof(type3));
339 type3.username = ctx->client->username;
340 type3.flags = type2.flags;
341 type3.targetname = type2.targetname;
342 type3.ws = rk_UNCONST("workstation");
345 * NTLM Version 1 if no targetinfo buffer.
348 if (1 || type2.targetinfo.length == 0) {
349 struct ntlm_buf sessionkey;
351 if (type2.flags & NTLM_NEG_NTLM2_SESSION) {
352 unsigned char nonce[8];
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;
361 ret = heim_ntlm_calculate_ntlm2_sess(nonce,
363 ctx->client->key.data,
367 ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data,
368 ctx->client->key.length,
374 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
376 return GSS_S_FAILURE;
379 ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data,
380 ctx->client->key.length,
387 free(type3.ntlm.data);
388 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
390 return GSS_S_FAILURE;
393 ret = krb5_data_copy(&ctx->sessionkey,
394 sessionkey.data, sessionkey.length);
395 free(sessionkey.data);
400 free(type3.ntlm.data);
401 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL);
403 return GSS_S_FAILURE;
405 ctx->status |= STATUS_SESSIONKEY;
408 struct ntlm_buf sessionkey;
409 unsigned char ntlmv2[16];
410 struct ntlm_targetinfo ti;
412 /* verify infotarget */
414 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti);
416 _gss_ntlm_delete_sec_context(minor_status,
417 context_handle, NULL);
419 return GSS_S_FAILURE;
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;
429 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data,
430 ctx->client->key.length,
431 ctx->client->username,
438 _gss_ntlm_delete_sec_context(minor_status,
439 context_handle, NULL);
441 return GSS_S_FAILURE;
444 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2),
447 memset(ntlmv2, 0, sizeof(ntlmv2));
449 _gss_ntlm_delete_sec_context(minor_status,
450 context_handle, NULL);
452 return GSS_S_FAILURE;
455 ctx->flags |= NTLM_NEG_NTLM2_SESSION;
457 ret = krb5_data_copy(&ctx->sessionkey,
458 sessionkey.data, sessionkey.length);
459 free(sessionkey.data);
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);
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);
482 ret = heim_ntlm_encode_type3(&type3, &data);
483 free(type3.sessionkey.data);
487 free(type3.ntlm.data);
489 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL);
491 return GSS_S_FAILURE;
494 output_token->length = data.length;
495 output_token->value = data.data;
497 if (actual_mech_type)
498 *actual_mech_type = GSS_NTLM_MECHANISM;
502 *time_rec = GSS_C_INDEFINITE;
504 ctx->status |= STATUS_OPEN;
506 return GSS_S_COMPLETE;