1 /* 2 * Copyright (c) 2006 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/ntlm.h" 35 36 RCSID("$Id: init_sec_context.c 22382 2007-12-30 12:13:17Z lha $"); 37 38 static int 39 from_file(const char *fn, const char *target_domain, 40 char **username, struct ntlm_buf *key) 41 { 42 char *str, buf[1024]; 43 FILE *f; 44 45 f = fopen(fn, "r"); 46 if (f == NULL) 47 return ENOENT; 48 49 while (fgets(buf, sizeof(buf), f) != NULL) { 50 char *d, *u, *p; 51 buf[strcspn(buf, "\r\n")] = '\0'; 52 if (buf[0] == '#') 53 continue; 54 str = NULL; 55 d = strtok_r(buf, ":", &str); 56 if (d && strcasecmp(target_domain, d) != 0) 57 continue; 58 u = strtok_r(NULL, ":", &str); 59 p = strtok_r(NULL, ":", &str); 60 if (u == NULL || p == NULL) 61 continue; 62 63 *username = strdup(u); 64 65 heim_ntlm_nt_key(p, key); 66 67 memset(buf, 0, sizeof(buf)); 68 fclose(f); 69 return 0; 70 } 71 memset(buf, 0, sizeof(buf)); 72 fclose(f); 73 return ENOENT; 74 } 75 76 static int 77 get_user_file(const ntlm_name target_name, 78 char **username, struct ntlm_buf *key) 79 { 80 const char *fn; 81 82 if (issuid()) 83 return ENOENT; 84 85 fn = getenv("NTLM_USER_FILE"); 86 if (fn == NULL) 87 return ENOENT; 88 if (from_file(fn, target_name->domain, username, key) == 0) 89 return 0; 90 91 return ENOENT; 92 } 93 94 /* 95 * Pick up the ntlm cred from the default krb5 credential cache. 96 */ 97 98 static int 99 get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key) 100 { 101 krb5_principal client; 102 krb5_context context = NULL; 103 krb5_error_code ret; 104 krb5_ccache id = NULL; 105 krb5_creds mcreds, creds; 106 107 *username = NULL; 108 key->length = 0; 109 key->data = NULL; 110 111 memset(&creds, 0, sizeof(creds)); 112 memset(&mcreds, 0, sizeof(mcreds)); 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 if (ret) 130 goto out; 131 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); 136 if (ret) 137 goto out; 138 139 mcreds.session.keytype = ENCTYPE_ARCFOUR_HMAC_MD5; 140 ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_MATCH_KEYTYPE, 141 &mcreds, &creds); 142 if (ret) { 143 char *s = krb5_get_error_message(context, ret); 144 krb5_free_error_string(context, s); 145 goto out; 146 } 147 148 key->data = malloc(creds.session.keyvalue.length); 149 if (key->data == NULL) 150 goto out; 151 key->length = creds.session.keyvalue.length; 152 memcpy(key->data, creds.session.keyvalue.data, key->length); 153 154 krb5_free_cred_contents(context, &creds); 155 156 return 0; 157 158 out: 159 if (*username) { 160 free(*username); 161 *username = NULL; 162 } 163 krb5_free_cred_contents(context, &creds); 164 if (mcreds.server) 165 krb5_free_principal(context, mcreds.server); 166 if (id) 167 krb5_cc_close(context, id); 168 if (context) 169 krb5_free_context(context); 170 171 return ret; 172 } 173 174 int 175 _gss_ntlm_get_user_cred(const ntlm_name target_name, 176 ntlm_cred *rcred) 177 { 178 ntlm_cred cred; 179 int ret; 180 181 cred = calloc(1, sizeof(*cred)); 182 if (cred == NULL) 183 return ENOMEM; 184 185 ret = get_user_file(target_name, &cred->username, &cred->key); 186 if (ret) 187 ret = get_user_ccache(target_name, &cred->username, &cred->key); 188 if (ret) { 189 free(cred); 190 return ret; 191 } 192 193 cred->domain = strdup(target_name->domain); 194 *rcred = cred; 195 196 return ret; 197 } 198 199 static int 200 _gss_copy_cred(ntlm_cred from, ntlm_cred *to) 201 { 202 *to = calloc(1, sizeof(*to)); 203 if (*to == NULL) 204 return ENOMEM; 205 (*to)->username = strdup(from->username); 206 if ((*to)->username == NULL) { 207 free(*to); 208 return ENOMEM; 209 } 210 (*to)->domain = strdup(from->domain); 211 if ((*to)->domain == NULL) { 212 free((*to)->username); 213 free(*to); 214 return ENOMEM; 215 } 216 (*to)->key.data = malloc(from->key.length); 217 if ((*to)->key.data == NULL) { 218 free((*to)->domain); 219 free((*to)->username); 220 free(*to); 221 return ENOMEM; 222 } 223 memcpy((*to)->key.data, from->key.data, from->key.length); 224 (*to)->key.length = from->key.length; 225 226 return 0; 227 } 228 229 OM_uint32 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, 236 OM_uint32 req_flags, 237 OM_uint32 time_req, 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, 243 OM_uint32 * time_rec 244 ) 245 { 246 ntlm_ctx ctx; 247 ntlm_name name = (ntlm_name)target_name; 248 249 *minor_status = 0; 250 251 if (ret_flags) 252 *ret_flags = 0; 253 if (time_rec) 254 *time_rec = 0; 255 if (actual_mech_type) 256 *actual_mech_type = GSS_C_NO_OID; 257 258 if (*context_handle == GSS_C_NO_CONTEXT) { 259 struct ntlm_type1 type1; 260 struct ntlm_buf data; 261 uint32_t flags = 0; 262 int ret; 263 264 ctx = calloc(1, sizeof(*ctx)); 265 if (ctx == NULL) { 266 *minor_status = EINVAL; 267 return GSS_S_FAILURE; 268 } 269 *context_handle = (gss_ctx_id_t)ctx; 270 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); 274 } else 275 ret = _gss_ntlm_get_user_cred(name, &ctx->client); 276 277 if (ret) { 278 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 279 *minor_status = ret; 280 return GSS_S_FAILURE; 281 } 282 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; 287 else 288 flags |= NTLM_NEG_ALWAYS_SIGN; 289 290 flags |= NTLM_NEG_UNICODE; 291 flags |= NTLM_NEG_NTLM; 292 flags |= NTLM_NEG_NTLM2_SESSION; 293 flags |= NTLM_NEG_KEYEX; 294 295 memset(&type1, 0, sizeof(type1)); 296 297 type1.flags = flags; 298 type1.domain = name->domain; 299 type1.hostname = NULL; 300 type1.os[0] = 0; 301 type1.os[1] = 0; 302 303 ret = heim_ntlm_encode_type1(&type1, &data); 304 if (ret) { 305 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 306 *minor_status = ret; 307 return GSS_S_FAILURE; 308 } 309 310 output_token->value = data.data; 311 output_token->length = data.length; 312 313 return GSS_S_CONTINUE_NEEDED; 314 } else { 315 krb5_error_code ret; 316 struct ntlm_type2 type2; 317 struct ntlm_type3 type3; 318 struct ntlm_buf data; 319 320 ctx = (ntlm_ctx)*context_handle; 321 322 data.data = input_token->value; 323 data.length = input_token->length; 324 325 ret = heim_ntlm_decode_type2(&data, &type2); 326 if (ret) { 327 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 328 *minor_status = ret; 329 return GSS_S_FAILURE; 330 } 331 332 ctx->flags = type2.flags; 333 334 /* XXX check that type2.targetinfo matches `target_name� */ 335 /* XXX check verify targetinfo buffer */ 336 337 memset(&type3, 0, sizeof(type3)); 338 339 type3.username = ctx->client->username; 340 type3.flags = type2.flags; 341 type3.targetname = type2.targetname; 342 type3.ws = rk_UNCONST("workstation"); 343 344 /* 345 * NTLM Version 1 if no targetinfo buffer. 346 */ 347 348 if (1 || type2.targetinfo.length == 0) { 349 struct ntlm_buf sessionkey; 350 351 if (type2.flags & NTLM_NEG_NTLM2_SESSION) { 352 unsigned char nonce[8]; 353 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; 359 } 360 361 ret = heim_ntlm_calculate_ntlm2_sess(nonce, 362 type2.challange, 363 ctx->client->key.data, 364 &type3.lm, 365 &type3.ntlm); 366 } else { 367 ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data, 368 ctx->client->key.length, 369 type2.challange, 370 &type3.ntlm); 371 372 } 373 if (ret) { 374 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 375 *minor_status = ret; 376 return GSS_S_FAILURE; 377 } 378 379 ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data, 380 ctx->client->key.length, 381 &sessionkey, 382 &type3.sessionkey); 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 393 ret = krb5_data_copy(&ctx->sessionkey, 394 sessionkey.data, sessionkey.length); 395 free(sessionkey.data); 396 if (ret) { 397 if (type3.lm.data) 398 free(type3.lm.data); 399 if (type3.ntlm.data) 400 free(type3.ntlm.data); 401 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 402 *minor_status = ret; 403 return GSS_S_FAILURE; 404 } 405 ctx->status |= STATUS_SESSIONKEY; 406 407 } else { 408 struct ntlm_buf sessionkey; 409 unsigned char ntlmv2[16]; 410 struct ntlm_targetinfo ti; 411 412 /* verify infotarget */ 413 414 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); 415 if(ret) { 416 _gss_ntlm_delete_sec_context(minor_status, 417 context_handle, NULL); 418 *minor_status = ret; 419 return GSS_S_FAILURE; 420 } 421 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; 427 } 428 429 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, 430 ctx->client->key.length, 431 ctx->client->username, 432 name->domain, 433 type2.challange, 434 &type2.targetinfo, 435 ntlmv2, 436 &type3.ntlm); 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 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), 445 &sessionkey, 446 &type3.sessionkey); 447 memset(ntlmv2, 0, sizeof(ntlmv2)); 448 if (ret) { 449 _gss_ntlm_delete_sec_context(minor_status, 450 context_handle, NULL); 451 *minor_status = ret; 452 return GSS_S_FAILURE; 453 } 454 455 ctx->flags |= NTLM_NEG_NTLM2_SESSION; 456 457 ret = krb5_data_copy(&ctx->sessionkey, 458 sessionkey.data, sessionkey.length); 459 free(sessionkey.data); 460 } 461 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); 470 } else { 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); 478 } 479 480 481 482 ret = heim_ntlm_encode_type3(&type3, &data); 483 free(type3.sessionkey.data); 484 if (type3.lm.data) 485 free(type3.lm.data); 486 if (type3.ntlm.data) 487 free(type3.ntlm.data); 488 if (ret) { 489 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 490 *minor_status = ret; 491 return GSS_S_FAILURE; 492 } 493 494 output_token->length = data.length; 495 output_token->value = data.data; 496 497 if (actual_mech_type) 498 *actual_mech_type = GSS_NTLM_MECHANISM; 499 if (ret_flags) 500 *ret_flags = 0; 501 if (time_rec) 502 *time_rec = GSS_C_INDEFINITE; 503 504 ctx->status |= STATUS_OPEN; 505 506 return GSS_S_COMPLETE; 507 } 508 } 509