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