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 && 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; 61 62 *username = strdup(u); 63 64 heim_ntlm_nt_key(p, key); 65 66 memset(buf, 0, sizeof(buf)); 67 fclose(f); 68 return 0; 69 } 70 memset(buf, 0, sizeof(buf)); 71 fclose(f); 72 return ENOENT; 73 } 74 75 static int 76 get_user_file(const ntlm_name target_name, 77 char **username, struct ntlm_buf *key) 78 { 79 const char *fn; 80 81 if (issuid()) 82 return ENOENT; 83 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; 89 90 return ENOENT; 91 } 92 93 /* 94 * Pick up the ntlm cred from the default krb5 credential cache. 95 */ 96 97 static int 98 get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key) 99 { 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 107 *username = NULL; 108 krb5_data_zero(&data); 109 key->length = 0; 110 key->data = NULL; 111 112 ret = krb5_init_context(&context); 113 if (ret) 114 return ret; 115 116 ret = krb5_cc_default(context, &id); 117 if (ret) 118 goto out; 119 120 ret = krb5_cc_get_principal(context, id, &client); 121 if (ret) 122 goto out; 123 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; 130 131 asprintf(&confname, "ntlm-key-%s", name->domain); 132 if (confname == NULL) { 133 krb5_clear_error_message(context); 134 ret = ENOMEM; 135 goto out; 136 } 137 138 ret = krb5_cc_get_config(context, id, NULL, 139 confname, &data); 140 if (ret) 141 goto out; 142 143 key->data = malloc(data.length); 144 if (key->data == NULL) { 145 ret = ENOMEM; 146 goto out; 147 } 148 key->length = data.length; 149 memcpy(key->data, data.data, data.length); 150 151 out: 152 krb5_data_free(&data); 153 if (id) 154 krb5_cc_close(context, id); 155 156 krb5_free_context(context); 157 158 return ret; 159 } 160 161 int 162 _gss_ntlm_get_user_cred(const ntlm_name target_name, 163 ntlm_cred *rcred) 164 { 165 ntlm_cred cred; 166 int ret; 167 168 cred = calloc(1, sizeof(*cred)); 169 if (cred == NULL) 170 return ENOMEM; 171 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; 178 } 179 180 cred->domain = strdup(target_name->domain); 181 *rcred = cred; 182 183 return ret; 184 } 185 186 static int 187 _gss_copy_cred(ntlm_cred from, ntlm_cred *to) 188 { 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; 196 } 197 (*to)->domain = strdup(from->domain); 198 if ((*to)->domain == NULL) { 199 free((*to)->username); 200 free(*to); 201 return ENOMEM; 202 } 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; 209 } 210 memcpy((*to)->key.data, from->key.data, from->key.length); 211 (*to)->key.length = from->key.length; 212 213 return 0; 214 } 215 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 231 ) 232 { 233 ntlm_ctx ctx; 234 ntlm_name name = (ntlm_name)target_name; 235 236 *minor_status = 0; 237 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; 244 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; 250 251 ctx = calloc(1, sizeof(*ctx)); 252 if (ctx == NULL) { 253 *minor_status = EINVAL; 254 return GSS_S_FAILURE; 255 } 256 *context_handle = (gss_ctx_id_t)ctx; 257 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); 263 264 if (ret) { 265 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 266 *minor_status = ret; 267 return GSS_S_FAILURE; 268 } 269 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; 276 277 flags |= NTLM_NEG_UNICODE; 278 flags |= NTLM_NEG_NTLM; 279 flags |= NTLM_NEG_NTLM2_SESSION; 280 flags |= NTLM_NEG_KEYEX; 281 282 memset(&type1, 0, sizeof(type1)); 283 284 type1.flags = flags; 285 type1.domain = name->domain; 286 type1.hostname = NULL; 287 type1.os[0] = 0; 288 type1.os[1] = 0; 289 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; 295 } 296 297 output_token->value = data.data; 298 output_token->length = data.length; 299 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; 306 307 ctx = (ntlm_ctx)*context_handle; 308 309 data.data = input_token->value; 310 data.length = input_token->length; 311 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; 317 } 318 319 ctx->flags = type2.flags; 320 321 /* XXX check that type2.targetinfo matches `target_name´ */ 322 /* XXX check verify targetinfo buffer */ 323 324 memset(&type3, 0, sizeof(type3)); 325 326 type3.username = ctx->client->username; 327 type3.flags = type2.flags; 328 type3.targetname = type2.targetname; 329 type3.ws = rk_UNCONST("workstation"); 330 331 /* 332 * NTLM Version 1 if no targetinfo buffer. 333 */ 334 335 if (1 || type2.targetinfo.length == 0) { 336 struct ntlm_buf sessionkey; 337 338 if (type2.flags & NTLM_NEG_NTLM2_SESSION) { 339 unsigned char nonce[8]; 340 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; 346 } 347 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); 358 359 } 360 if (ret) { 361 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 362 *minor_status = ret; 363 return GSS_S_FAILURE; 364 } 365 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; 378 } 379 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; 391 } 392 ctx->status |= STATUS_SESSIONKEY; 393 394 } else { 395 struct ntlm_buf sessionkey; 396 unsigned char ntlmv2[16]; 397 struct ntlm_targetinfo ti; 398 399 /* verify infotarget */ 400 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; 407 } 408 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; 414 } 415 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; 429 } 430 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; 440 } 441 442 ctx->flags |= NTLM_NEG_NTLM2_SESSION; 443 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; 452 } 453 } 454 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); 471 } 472 473 474 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; 485 } 486 487 output_token->length = data.length; 488 output_token->value = data.data; 489 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; 496 497 ctx->status |= STATUS_OPEN; 498 499 return GSS_S_COMPLETE; 500 } 501 } 502