1178825Sdfr/* 2233294Sstas * Copyright (c) 2006 - 2008 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 5178825Sdfr * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 9178825Sdfr * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 20178825Sdfr * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34233294Sstas#include "ntlm.h" 35178825Sdfr 36178825Sdfrstatic int 37233294Sstasfrom_file(const char *fn, const char *target_domain, 38178825Sdfr char **username, struct ntlm_buf *key) 39233294Sstas{ 40178825Sdfr char *str, buf[1024]; 41178825Sdfr FILE *f; 42178825Sdfr 43178825Sdfr f = fopen(fn, "r"); 44178825Sdfr if (f == NULL) 45178825Sdfr return ENOENT; 46233294Sstas rk_cloexec_file(f); 47178825Sdfr 48178825Sdfr while (fgets(buf, sizeof(buf), f) != NULL) { 49178825Sdfr char *d, *u, *p; 50178825Sdfr buf[strcspn(buf, "\r\n")] = '\0'; 51178825Sdfr if (buf[0] == '#') 52178825Sdfr continue; 53178825Sdfr str = NULL; 54178825Sdfr d = strtok_r(buf, ":", &str); 55178825Sdfr if (d && strcasecmp(target_domain, d) != 0) 56178825Sdfr continue; 57178825Sdfr u = strtok_r(NULL, ":", &str); 58178825Sdfr p = strtok_r(NULL, ":", &str); 59178825Sdfr if (u == NULL || p == NULL) 60178825Sdfr continue; 61178825Sdfr 62178825Sdfr *username = strdup(u); 63178825Sdfr 64178825Sdfr heim_ntlm_nt_key(p, key); 65178825Sdfr 66178825Sdfr memset(buf, 0, sizeof(buf)); 67178825Sdfr fclose(f); 68178825Sdfr return 0; 69178825Sdfr } 70178825Sdfr memset(buf, 0, sizeof(buf)); 71178825Sdfr fclose(f); 72178825Sdfr return ENOENT; 73178825Sdfr} 74178825Sdfr 75178825Sdfrstatic int 76233294Sstasget_user_file(const ntlm_name target_name, 77178825Sdfr char **username, struct ntlm_buf *key) 78178825Sdfr{ 79178825Sdfr const char *fn; 80178825Sdfr 81178825Sdfr if (issuid()) 82178825Sdfr return ENOENT; 83178825Sdfr 84178825Sdfr fn = getenv("NTLM_USER_FILE"); 85178825Sdfr if (fn == NULL) 86178825Sdfr return ENOENT; 87178825Sdfr if (from_file(fn, target_name->domain, username, key) == 0) 88178825Sdfr return 0; 89178825Sdfr 90178825Sdfr return ENOENT; 91178825Sdfr} 92178825Sdfr 93178825Sdfr/* 94178825Sdfr * Pick up the ntlm cred from the default krb5 credential cache. 95178825Sdfr */ 96178825Sdfr 97178825Sdfrstatic int 98178825Sdfrget_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key) 99178825Sdfr{ 100233294Sstas krb5_context context = NULL; 101178825Sdfr krb5_principal client; 102233294Sstas krb5_ccache id = NULL; 103178825Sdfr krb5_error_code ret; 104233294Sstas char *confname; 105233294Sstas krb5_data data; 106178825Sdfr 107178825Sdfr *username = NULL; 108233294Sstas krb5_data_zero(&data); 109178825Sdfr key->length = 0; 110178825Sdfr key->data = NULL; 111178825Sdfr 112178825Sdfr ret = krb5_init_context(&context); 113178825Sdfr if (ret) 114178825Sdfr return ret; 115178825Sdfr 116178825Sdfr ret = krb5_cc_default(context, &id); 117178825Sdfr if (ret) 118178825Sdfr goto out; 119178825Sdfr 120178825Sdfr ret = krb5_cc_get_principal(context, id, &client); 121178825Sdfr if (ret) 122178825Sdfr goto out; 123178825Sdfr 124178825Sdfr ret = krb5_unparse_name_flags(context, client, 125178825Sdfr KRB5_PRINCIPAL_UNPARSE_NO_REALM, 126178825Sdfr username); 127178825Sdfr krb5_free_principal(context, client); 128178825Sdfr if (ret) 129178825Sdfr goto out; 130178825Sdfr 131233294Sstas asprintf(&confname, "ntlm-key-%s", name->domain); 132233294Sstas if (confname == NULL) { 133233294Sstas krb5_clear_error_message(context); 134233294Sstas ret = ENOMEM; 135178825Sdfr goto out; 136178825Sdfr } 137178825Sdfr 138233294Sstas ret = krb5_cc_get_config(context, id, NULL, 139233294Sstas confname, &data); 140233294Sstas if (ret) 141178825Sdfr goto out; 142178825Sdfr 143233294Sstas key->data = malloc(data.length); 144233294Sstas if (key->data == NULL) { 145233294Sstas ret = ENOMEM; 146233294Sstas goto out; 147233294Sstas } 148233294Sstas key->length = data.length; 149233294Sstas memcpy(key->data, data.data, data.length); 150178825Sdfr 151233294Sstas out: 152233294Sstas krb5_data_free(&data); 153178825Sdfr if (id) 154178825Sdfr krb5_cc_close(context, id); 155178825Sdfr 156233294Sstas krb5_free_context(context); 157233294Sstas 158178825Sdfr return ret; 159178825Sdfr} 160178825Sdfr 161178825Sdfrint 162178825Sdfr_gss_ntlm_get_user_cred(const ntlm_name target_name, 163178825Sdfr ntlm_cred *rcred) 164178825Sdfr{ 165178825Sdfr ntlm_cred cred; 166178825Sdfr int ret; 167233294Sstas 168178825Sdfr cred = calloc(1, sizeof(*cred)); 169178825Sdfr if (cred == NULL) 170178825Sdfr return ENOMEM; 171233294Sstas 172178825Sdfr ret = get_user_file(target_name, &cred->username, &cred->key); 173178825Sdfr if (ret) 174178825Sdfr ret = get_user_ccache(target_name, &cred->username, &cred->key); 175178825Sdfr if (ret) { 176178825Sdfr free(cred); 177178825Sdfr return ret; 178178825Sdfr } 179233294Sstas 180178825Sdfr cred->domain = strdup(target_name->domain); 181178825Sdfr *rcred = cred; 182178825Sdfr 183178825Sdfr return ret; 184178825Sdfr} 185178825Sdfr 186178825Sdfrstatic int 187178825Sdfr_gss_copy_cred(ntlm_cred from, ntlm_cred *to) 188178825Sdfr{ 189233294Sstas *to = calloc(1, sizeof(**to)); 190178825Sdfr if (*to == NULL) 191178825Sdfr return ENOMEM; 192178825Sdfr (*to)->username = strdup(from->username); 193178825Sdfr if ((*to)->username == NULL) { 194178825Sdfr free(*to); 195178825Sdfr return ENOMEM; 196178825Sdfr } 197178825Sdfr (*to)->domain = strdup(from->domain); 198178825Sdfr if ((*to)->domain == NULL) { 199178825Sdfr free((*to)->username); 200178825Sdfr free(*to); 201178825Sdfr return ENOMEM; 202178825Sdfr } 203178825Sdfr (*to)->key.data = malloc(from->key.length); 204178825Sdfr if ((*to)->key.data == NULL) { 205178825Sdfr free((*to)->domain); 206178825Sdfr free((*to)->username); 207178825Sdfr free(*to); 208178825Sdfr return ENOMEM; 209178825Sdfr } 210178825Sdfr memcpy((*to)->key.data, from->key.data, from->key.length); 211178825Sdfr (*to)->key.length = from->key.length; 212178825Sdfr 213178825Sdfr return 0; 214178825Sdfr} 215178825Sdfr 216233294SstasOM_uint32 GSSAPI_CALLCONV 217178825Sdfr_gss_ntlm_init_sec_context 218178825Sdfr (OM_uint32 * minor_status, 219178825Sdfr const gss_cred_id_t initiator_cred_handle, 220178825Sdfr gss_ctx_id_t * context_handle, 221178825Sdfr const gss_name_t target_name, 222178825Sdfr const gss_OID mech_type, 223178825Sdfr OM_uint32 req_flags, 224178825Sdfr OM_uint32 time_req, 225178825Sdfr const gss_channel_bindings_t input_chan_bindings, 226178825Sdfr const gss_buffer_t input_token, 227178825Sdfr gss_OID * actual_mech_type, 228178825Sdfr gss_buffer_t output_token, 229178825Sdfr OM_uint32 * ret_flags, 230178825Sdfr OM_uint32 * time_rec 231178825Sdfr ) 232178825Sdfr{ 233178825Sdfr ntlm_ctx ctx; 234178825Sdfr ntlm_name name = (ntlm_name)target_name; 235178825Sdfr 236178825Sdfr *minor_status = 0; 237178825Sdfr 238178825Sdfr if (ret_flags) 239178825Sdfr *ret_flags = 0; 240178825Sdfr if (time_rec) 241178825Sdfr *time_rec = 0; 242178825Sdfr if (actual_mech_type) 243178825Sdfr *actual_mech_type = GSS_C_NO_OID; 244178825Sdfr 245178825Sdfr if (*context_handle == GSS_C_NO_CONTEXT) { 246178825Sdfr struct ntlm_type1 type1; 247178825Sdfr struct ntlm_buf data; 248178825Sdfr uint32_t flags = 0; 249178825Sdfr int ret; 250233294Sstas 251178825Sdfr ctx = calloc(1, sizeof(*ctx)); 252178825Sdfr if (ctx == NULL) { 253178825Sdfr *minor_status = EINVAL; 254178825Sdfr return GSS_S_FAILURE; 255178825Sdfr } 256178825Sdfr *context_handle = (gss_ctx_id_t)ctx; 257178825Sdfr 258178825Sdfr if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) { 259178825Sdfr ntlm_cred cred = (ntlm_cred)initiator_cred_handle; 260178825Sdfr ret = _gss_copy_cred(cred, &ctx->client); 261178825Sdfr } else 262178825Sdfr ret = _gss_ntlm_get_user_cred(name, &ctx->client); 263178825Sdfr 264178825Sdfr if (ret) { 265178825Sdfr _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 266178825Sdfr *minor_status = ret; 267178825Sdfr return GSS_S_FAILURE; 268178825Sdfr } 269178825Sdfr 270178825Sdfr if (req_flags & GSS_C_CONF_FLAG) 271178825Sdfr flags |= NTLM_NEG_SEAL; 272178825Sdfr if (req_flags & GSS_C_INTEG_FLAG) 273178825Sdfr flags |= NTLM_NEG_SIGN; 274178825Sdfr else 275178825Sdfr flags |= NTLM_NEG_ALWAYS_SIGN; 276178825Sdfr 277178825Sdfr flags |= NTLM_NEG_UNICODE; 278178825Sdfr flags |= NTLM_NEG_NTLM; 279178825Sdfr flags |= NTLM_NEG_NTLM2_SESSION; 280178825Sdfr flags |= NTLM_NEG_KEYEX; 281178825Sdfr 282178825Sdfr memset(&type1, 0, sizeof(type1)); 283233294Sstas 284178825Sdfr type1.flags = flags; 285178825Sdfr type1.domain = name->domain; 286178825Sdfr type1.hostname = NULL; 287178825Sdfr type1.os[0] = 0; 288178825Sdfr type1.os[1] = 0; 289233294Sstas 290178825Sdfr ret = heim_ntlm_encode_type1(&type1, &data); 291178825Sdfr if (ret) { 292178825Sdfr _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 293178825Sdfr *minor_status = ret; 294178825Sdfr return GSS_S_FAILURE; 295178825Sdfr } 296233294Sstas 297178825Sdfr output_token->value = data.data; 298178825Sdfr output_token->length = data.length; 299233294Sstas 300178825Sdfr return GSS_S_CONTINUE_NEEDED; 301178825Sdfr } else { 302178825Sdfr krb5_error_code ret; 303178825Sdfr struct ntlm_type2 type2; 304178825Sdfr struct ntlm_type3 type3; 305178825Sdfr struct ntlm_buf data; 306178825Sdfr 307178825Sdfr ctx = (ntlm_ctx)*context_handle; 308178825Sdfr 309178825Sdfr data.data = input_token->value; 310178825Sdfr data.length = input_token->length; 311178825Sdfr 312178825Sdfr ret = heim_ntlm_decode_type2(&data, &type2); 313178825Sdfr if (ret) { 314178825Sdfr _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 315178825Sdfr *minor_status = ret; 316178825Sdfr return GSS_S_FAILURE; 317178825Sdfr } 318178825Sdfr 319178825Sdfr ctx->flags = type2.flags; 320178825Sdfr 321233294Sstas /* XXX check that type2.targetinfo matches `target_name�� */ 322178825Sdfr /* XXX check verify targetinfo buffer */ 323178825Sdfr 324178825Sdfr memset(&type3, 0, sizeof(type3)); 325178825Sdfr 326178825Sdfr type3.username = ctx->client->username; 327178825Sdfr type3.flags = type2.flags; 328178825Sdfr type3.targetname = type2.targetname; 329178825Sdfr type3.ws = rk_UNCONST("workstation"); 330178825Sdfr 331178825Sdfr /* 332178825Sdfr * NTLM Version 1 if no targetinfo buffer. 333178825Sdfr */ 334178825Sdfr 335178825Sdfr if (1 || type2.targetinfo.length == 0) { 336178825Sdfr struct ntlm_buf sessionkey; 337178825Sdfr 338178825Sdfr if (type2.flags & NTLM_NEG_NTLM2_SESSION) { 339178825Sdfr unsigned char nonce[8]; 340178825Sdfr 341178825Sdfr if (RAND_bytes(nonce, sizeof(nonce)) != 1) { 342233294Sstas _gss_ntlm_delete_sec_context(minor_status, 343178825Sdfr context_handle, NULL); 344178825Sdfr *minor_status = EINVAL; 345178825Sdfr return GSS_S_FAILURE; 346178825Sdfr } 347178825Sdfr 348178825Sdfr ret = heim_ntlm_calculate_ntlm2_sess(nonce, 349233294Sstas type2.challenge, 350178825Sdfr ctx->client->key.data, 351178825Sdfr &type3.lm, 352178825Sdfr &type3.ntlm); 353178825Sdfr } else { 354233294Sstas ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data, 355178825Sdfr ctx->client->key.length, 356233294Sstas type2.challenge, 357178825Sdfr &type3.ntlm); 358178825Sdfr 359178825Sdfr } 360178825Sdfr if (ret) { 361178825Sdfr _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 362178825Sdfr *minor_status = ret; 363178825Sdfr return GSS_S_FAILURE; 364178825Sdfr } 365178825Sdfr 366233294Sstas ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data, 367178825Sdfr ctx->client->key.length, 368178825Sdfr &sessionkey, 369178825Sdfr &type3.sessionkey); 370178825Sdfr if (ret) { 371178825Sdfr if (type3.lm.data) 372178825Sdfr free(type3.lm.data); 373178825Sdfr if (type3.ntlm.data) 374178825Sdfr free(type3.ntlm.data); 375178825Sdfr _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 376178825Sdfr *minor_status = ret; 377178825Sdfr return GSS_S_FAILURE; 378178825Sdfr } 379178825Sdfr 380233294Sstas ret = krb5_data_copy(&ctx->sessionkey, 381178825Sdfr sessionkey.data, sessionkey.length); 382178825Sdfr free(sessionkey.data); 383178825Sdfr if (ret) { 384178825Sdfr if (type3.lm.data) 385178825Sdfr free(type3.lm.data); 386178825Sdfr if (type3.ntlm.data) 387178825Sdfr free(type3.ntlm.data); 388178825Sdfr _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 389178825Sdfr *minor_status = ret; 390178825Sdfr return GSS_S_FAILURE; 391178825Sdfr } 392233294Sstas ctx->status |= STATUS_SESSIONKEY; 393178825Sdfr 394178825Sdfr } else { 395178825Sdfr struct ntlm_buf sessionkey; 396178825Sdfr unsigned char ntlmv2[16]; 397178825Sdfr struct ntlm_targetinfo ti; 398178825Sdfr 399178825Sdfr /* verify infotarget */ 400233294Sstas 401178825Sdfr ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); 402178825Sdfr if(ret) { 403233294Sstas _gss_ntlm_delete_sec_context(minor_status, 404178825Sdfr context_handle, NULL); 405178825Sdfr *minor_status = ret; 406178825Sdfr return GSS_S_FAILURE; 407178825Sdfr } 408178825Sdfr 409178825Sdfr if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { 410233294Sstas _gss_ntlm_delete_sec_context(minor_status, 411178825Sdfr context_handle, NULL); 412178825Sdfr *minor_status = EINVAL; 413178825Sdfr return GSS_S_FAILURE; 414178825Sdfr } 415178825Sdfr 416178825Sdfr ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, 417178825Sdfr ctx->client->key.length, 418178825Sdfr ctx->client->username, 419178825Sdfr name->domain, 420233294Sstas type2.challenge, 421178825Sdfr &type2.targetinfo, 422178825Sdfr ntlmv2, 423178825Sdfr &type3.ntlm); 424178825Sdfr if (ret) { 425233294Sstas _gss_ntlm_delete_sec_context(minor_status, 426178825Sdfr context_handle, NULL); 427178825Sdfr *minor_status = ret; 428178825Sdfr return GSS_S_FAILURE; 429178825Sdfr } 430178825Sdfr 431178825Sdfr ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), 432178825Sdfr &sessionkey, 433178825Sdfr &type3.sessionkey); 434178825Sdfr memset(ntlmv2, 0, sizeof(ntlmv2)); 435178825Sdfr if (ret) { 436233294Sstas _gss_ntlm_delete_sec_context(minor_status, 437178825Sdfr context_handle, NULL); 438178825Sdfr *minor_status = ret; 439178825Sdfr return GSS_S_FAILURE; 440178825Sdfr } 441233294Sstas 442178825Sdfr ctx->flags |= NTLM_NEG_NTLM2_SESSION; 443178825Sdfr 444233294Sstas ret = krb5_data_copy(&ctx->sessionkey, 445178825Sdfr sessionkey.data, sessionkey.length); 446178825Sdfr free(sessionkey.data); 447233294Sstas if (ret) { 448233294Sstas _gss_ntlm_delete_sec_context(minor_status, 449233294Sstas context_handle, NULL); 450233294Sstas *minor_status = ret; 451233294Sstas return GSS_S_FAILURE; 452233294Sstas } 453178825Sdfr } 454178825Sdfr 455178825Sdfr if (ctx->flags & NTLM_NEG_NTLM2_SESSION) { 456233294Sstas ctx->status |= STATUS_SESSIONKEY; 457178825Sdfr _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), 458178825Sdfr ctx->sessionkey.data, 459178825Sdfr ctx->sessionkey.length); 460178825Sdfr _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), 461178825Sdfr ctx->sessionkey.data, 462178825Sdfr ctx->sessionkey.length); 463178825Sdfr } else { 464233294Sstas ctx->status |= STATUS_SESSIONKEY; 465233294Sstas RC4_set_key(&ctx->u.v1.crypto_recv.key, 466178825Sdfr ctx->sessionkey.length, 467178825Sdfr ctx->sessionkey.data); 468233294Sstas RC4_set_key(&ctx->u.v1.crypto_send.key, 469178825Sdfr ctx->sessionkey.length, 470178825Sdfr ctx->sessionkey.data); 471178825Sdfr } 472178825Sdfr 473178825Sdfr 474233294Sstas 475178825Sdfr ret = heim_ntlm_encode_type3(&type3, &data); 476178825Sdfr free(type3.sessionkey.data); 477178825Sdfr if (type3.lm.data) 478178825Sdfr free(type3.lm.data); 479178825Sdfr if (type3.ntlm.data) 480178825Sdfr free(type3.ntlm.data); 481178825Sdfr if (ret) { 482178825Sdfr _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 483178825Sdfr *minor_status = ret; 484178825Sdfr return GSS_S_FAILURE; 485178825Sdfr } 486178825Sdfr 487178825Sdfr output_token->length = data.length; 488178825Sdfr output_token->value = data.data; 489178825Sdfr 490178825Sdfr if (actual_mech_type) 491178825Sdfr *actual_mech_type = GSS_NTLM_MECHANISM; 492178825Sdfr if (ret_flags) 493178825Sdfr *ret_flags = 0; 494178825Sdfr if (time_rec) 495178825Sdfr *time_rec = GSS_C_INDEFINITE; 496178825Sdfr 497178825Sdfr ctx->status |= STATUS_OPEN; 498178825Sdfr 499178825Sdfr return GSS_S_COMPLETE; 500178825Sdfr } 501178825Sdfr} 502