1234370Sjasone/* $NetBSD: init_sec_context.c,v 1.4 2023/06/19 21:41:43 christos Exp $ */ 2234370Sjasone 3234370Sjasone/* 4234370Sjasone * Copyright (c) 2006 - 2008 Kungliga Tekniska H��gskolan 5234370Sjasone * (Royal Institute of Technology, Stockholm, Sweden). 6234370Sjasone * All rights reserved. 7234370Sjasone * 8234370Sjasone * Redistribution and use in source and binary forms, with or without 9234370Sjasone * modification, are permitted provided that the following conditions 10234370Sjasone * are met: 11234370Sjasone * 12234370Sjasone * 1. Redistributions of source code must retain the above copyright 13234370Sjasone * notice, this list of conditions and the following disclaimer. 14234370Sjasone * 15234370Sjasone * 2. Redistributions in binary form must reproduce the above copyright 16234370Sjasone * notice, this list of conditions and the following disclaimer in the 17235238Sjasone * documentation and/or other materials provided with the distribution. 18234370Sjasone * 19234370Sjasone * 3. Neither the name of the Institute nor the names of its contributors 20234370Sjasone * may be used to endorse or promote products derived from this software 21234370Sjasone * without specific prior written permission. 22234370Sjasone * 23234370Sjasone * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24234370Sjasone * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25234370Sjasone * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26234370Sjasone * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27234370Sjasone * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28234370Sjasone * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29234370Sjasone * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30234370Sjasone * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31234370Sjasone * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32234370Sjasone * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33234370Sjasone * SUCH DAMAGE. 34235238Sjasone */ 35235238Sjasone 36235238Sjasone#include "ntlm.h" 37235238Sjasone 38234370Sjasonestatic int 39234370Sjasonefrom_file(const char *fn, const char *target_domain, 40234370Sjasone char **domainp, char **usernamep, struct ntlm_buf *key) 41235238Sjasone{ 42234370Sjasone char *str, buf[1024]; 43234370Sjasone FILE *f; 44234370Sjasone 45234370Sjasone *domainp = NULL; 46234370Sjasone 47234370Sjasone f = fopen(fn, "r"); 48234370Sjasone if (f == NULL) 49234370Sjasone return ENOENT; 50234370Sjasone rk_cloexec_file(f); 51234543Sjasone 52234370Sjasone while (fgets(buf, sizeof(buf), f) != NULL) { 53234370Sjasone char *d, *u, *p; 54234370Sjasone buf[strcspn(buf, "\r\n")] = '\0'; 55234370Sjasone if (buf[0] == '#') 56234370Sjasone continue; 57234370Sjasone str = NULL; 58234370Sjasone d = strtok_r(buf, ":", &str); 59234370Sjasone free(*domainp); 60234370Sjasone *domainp = NULL; 61234543Sjasone if (!d) 62234370Sjasone continue; 63234370Sjasone if (d && target_domain != NULL && strcasecmp(target_domain, d) != 0) 64234370Sjasone continue; 65234543Sjasone *domainp = strdup(d); 66234370Sjasone if (*domainp == NULL) 67234370Sjasone return ENOMEM; 68234370Sjasone u = strtok_r(NULL, ":", &str); 69234370Sjasone p = strtok_r(NULL, ":", &str); 70234370Sjasone if (u == NULL || p == NULL) 71234370Sjasone continue; 72234370Sjasone 73234370Sjasone *usernamep = strdup(u); 74234370Sjasone if (*usernamep == NULL) 75235238Sjasone return ENOMEM; 76235238Sjasone 77235238Sjasone heim_ntlm_nt_key(p, key); 78235238Sjasone 79235238Sjasone memset_s(buf, sizeof(buf), 0, sizeof(buf)); 80235238Sjasone fclose(f); 81235238Sjasone return 0; 82235238Sjasone } 83235238Sjasone memset_s(buf, sizeof(buf), 0, sizeof(buf)); 84235238Sjasone fclose(f); 85235238Sjasone return ENOENT; 86235238Sjasone} 87235238Sjasone 88235238Sjasonestatic int 89235238Sjasoneget_user_file(const ntlm_name target_name, 90235238Sjasone char **domainp, char **usernamep, struct ntlm_buf *key) 91235238Sjasone{ 92235238Sjasone const char *domain; 93235238Sjasone const char *fn; 94235238Sjasone 95235238Sjasone *domainp = NULL; 96235238Sjasone 97235238Sjasone if (issuid()) 98235238Sjasone return ENOENT; 99235238Sjasone 100235238Sjasone domain = target_name != NULL ? target_name->domain : NULL; 101235238Sjasone 102235238Sjasone fn = getenv("NTLM_USER_FILE"); 103235238Sjasone if (fn == NULL) 104235238Sjasone return ENOENT; 105235238Sjasone if (from_file(fn, domain, domainp, usernamep, key) == 0) 106235238Sjasone return 0; 107235238Sjasone 108 return ENOENT; 109} 110 111/* 112 * Pick up the ntlm cred from the default krb5 credential cache. 113 */ 114 115static int 116get_user_ccache(const ntlm_name name, char **domainp, char **usernamep, struct ntlm_buf *key) 117{ 118 krb5_context context = NULL; 119 krb5_principal client; 120 krb5_ccache id = NULL; 121 krb5_error_code ret; 122 char *confname; 123 krb5_data data; 124 int aret; 125 126 *domainp = NULL; 127 *usernamep = NULL; 128 krb5_data_zero(&data); 129 key->length = 0; 130 key->data = NULL; 131 132 ret = krb5_init_context(&context); 133 if (ret) 134 return ret; 135 136 ret = krb5_cc_default(context, &id); 137 if (ret) 138 goto out; 139 140 ret = krb5_cc_get_principal(context, id, &client); 141 if (ret) 142 goto out; 143 144 ret = krb5_unparse_name_flags(context, client, 145 KRB5_PRINCIPAL_UNPARSE_NO_REALM, 146 usernamep); 147 krb5_free_principal(context, client); 148 if (ret) 149 goto out; 150 151 if (name != NULL) { 152 *domainp = strdup(name->domain); 153 } else { 154 krb5_data data_domain; 155 156 krb5_data_zero(&data_domain); 157 ret = krb5_cc_get_config(context, id, NULL, "default-ntlm-domain", 158 &data_domain); 159 if (ret) 160 goto out; 161 162 *domainp = strndup(data_domain.data, data_domain.length); 163 krb5_data_free(&data_domain); 164 } 165 166 if (*domainp == NULL) { 167 ret = krb5_enomem(context); 168 goto out; 169 } 170 171 aret = asprintf(&confname, "ntlm-key-%s", *domainp); 172 if (aret == -1) { 173 ret = krb5_enomem(context); 174 goto out; 175 } 176 177 ret = krb5_cc_get_config(context, id, NULL, confname, &data); 178 if (ret) 179 goto out; 180 181 key->data = malloc(data.length); 182 if (key->data == NULL) { 183 ret = ENOMEM; 184 goto out; 185 } 186 key->length = data.length; 187 memcpy(key->data, data.data, data.length); 188 189 out: 190 krb5_data_free(&data); 191 if (id) 192 krb5_cc_close(context, id); 193 194 krb5_free_context(context); 195 196 return ret; 197} 198 199int 200_gss_ntlm_get_user_cred(const ntlm_name target_name, 201 ntlm_cred *rcred) 202{ 203 ntlm_cred cred; 204 int ret; 205 206 cred = calloc(1, sizeof(*cred)); 207 if (cred == NULL) 208 return ENOMEM; 209 210 ret = get_user_file(target_name, 211 &cred->domain, &cred->username, &cred->key); 212 if (ret) 213 ret = get_user_ccache(target_name, 214 &cred->domain, &cred->username, &cred->key); 215 if (ret) { 216 free(cred); 217 return ret; 218 } 219 220 *rcred = cred; 221 222 return ret; 223} 224 225static int 226_gss_copy_cred(ntlm_cred from, ntlm_cred *to) 227{ 228 *to = calloc(1, sizeof(**to)); 229 if (*to == NULL) 230 return ENOMEM; 231 (*to)->username = strdup(from->username); 232 if ((*to)->username == NULL) { 233 free(*to); 234 return ENOMEM; 235 } 236 (*to)->domain = strdup(from->domain); 237 if ((*to)->domain == NULL) { 238 free((*to)->username); 239 free(*to); 240 return ENOMEM; 241 } 242 (*to)->key.data = malloc(from->key.length); 243 if ((*to)->key.data == NULL) { 244 free((*to)->domain); 245 free((*to)->username); 246 free(*to); 247 return ENOMEM; 248 } 249 memcpy((*to)->key.data, from->key.data, from->key.length); 250 (*to)->key.length = from->key.length; 251 252 return 0; 253} 254 255OM_uint32 GSSAPI_CALLCONV 256_gss_ntlm_init_sec_context 257 (OM_uint32 * minor_status, 258 gss_const_cred_id_t initiator_cred_handle, 259 gss_ctx_id_t * context_handle, 260 gss_const_name_t target_name, 261 const gss_OID mech_type, 262 OM_uint32 req_flags, 263 OM_uint32 time_req, 264 const gss_channel_bindings_t input_chan_bindings, 265 const gss_buffer_t input_token, 266 gss_OID * actual_mech_type, 267 gss_buffer_t output_token, 268 OM_uint32 * ret_flags, 269 OM_uint32 * time_rec 270 ) 271{ 272 ntlm_ctx ctx; 273 ntlm_name name = (ntlm_name)target_name; 274 275 *minor_status = 0; 276 277 if (ret_flags) 278 *ret_flags = 0; 279 if (time_rec) 280 *time_rec = 0; 281 if (actual_mech_type) 282 *actual_mech_type = GSS_C_NO_OID; 283 284 if (*context_handle == GSS_C_NO_CONTEXT) { 285 struct ntlm_type1 type1; 286 struct ntlm_buf data; 287 uint32_t flags = 0; 288 int ret; 289 290 ctx = calloc(1, sizeof(*ctx)); 291 if (ctx == NULL) { 292 *minor_status = EINVAL; 293 return GSS_S_FAILURE; 294 } 295 *context_handle = (gss_ctx_id_t)ctx; 296 297 if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) { 298 ntlm_cred cred = (ntlm_cred)initiator_cred_handle; 299 ret = _gss_copy_cred(cred, &ctx->client); 300 } else 301 ret = _gss_ntlm_get_user_cred(name, &ctx->client); 302 303 if (ret) { 304 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 305 *minor_status = ret; 306 return GSS_S_FAILURE; 307 } 308 309 if (req_flags & GSS_C_CONF_FLAG) 310 flags |= NTLM_NEG_SEAL; 311 if (req_flags & GSS_C_INTEG_FLAG) 312 flags |= NTLM_NEG_SIGN; 313 else 314 flags |= NTLM_NEG_ALWAYS_SIGN; 315 316 flags |= NTLM_NEG_UNICODE; 317 flags |= NTLM_NEG_NTLM; 318 flags |= NTLM_NEG_NTLM2_SESSION; 319 flags |= NTLM_NEG_KEYEX; 320 321 memset(&type1, 0, sizeof(type1)); 322 323 type1.flags = flags; 324 type1.domain = name->domain; 325 type1.hostname = NULL; 326 type1.os[0] = 0; 327 type1.os[1] = 0; 328 329 ret = heim_ntlm_encode_type1(&type1, &data); 330 if (ret) { 331 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 332 *minor_status = ret; 333 return GSS_S_FAILURE; 334 } 335 336 output_token->value = data.data; 337 output_token->length = data.length; 338 339 return GSS_S_CONTINUE_NEEDED; 340 } else { 341 krb5_error_code ret; 342 struct ntlm_type2 type2; 343 struct ntlm_type3 type3; 344 struct ntlm_buf data; 345 346 ctx = (ntlm_ctx)*context_handle; 347 348 data.data = input_token->value; 349 data.length = input_token->length; 350 351 ret = heim_ntlm_decode_type2(&data, &type2); 352 if (ret) { 353 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 354 *minor_status = ret; 355 return GSS_S_FAILURE; 356 } 357 358 ctx->flags = type2.flags; 359 360 /* XXX check that type2.targetinfo matches `target_name�� */ 361 /* XXX check verify targetinfo buffer */ 362 363 memset(&type3, 0, sizeof(type3)); 364 365 type3.username = ctx->client->username; 366 type3.flags = type2.flags; 367 type3.targetname = type2.targetname; 368 type3.ws = rk_UNCONST("workstation"); 369 370 /* 371 * NTLM Version 1 if no targetinfo buffer. 372 */ 373 374 if (1 || type2.targetinfo.length == 0) { 375 struct ntlm_buf sessionkey; 376 377 if (type2.flags & NTLM_NEG_NTLM2_SESSION) { 378 unsigned char nonce[8]; 379 380 if (RAND_bytes(nonce, sizeof(nonce)) != 1) { 381 _gss_ntlm_delete_sec_context(minor_status, 382 context_handle, NULL); 383 *minor_status = EINVAL; 384 return GSS_S_FAILURE; 385 } 386 387 ret = heim_ntlm_calculate_ntlm2_sess(nonce, 388 type2.challenge, 389 ctx->client->key.data, 390 &type3.lm, 391 &type3.ntlm); 392 } else { 393 ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data, 394 ctx->client->key.length, 395 type2.challenge, 396 &type3.ntlm); 397 398 } 399 if (ret) { 400 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 401 *minor_status = ret; 402 return GSS_S_FAILURE; 403 } 404 405 ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data, 406 ctx->client->key.length, 407 &sessionkey, 408 &type3.sessionkey); 409 if (ret) { 410 if (type3.lm.data) 411 free(type3.lm.data); 412 if (type3.ntlm.data) 413 free(type3.ntlm.data); 414 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 415 *minor_status = ret; 416 return GSS_S_FAILURE; 417 } 418 419 ret = krb5_data_copy(&ctx->sessionkey, 420 sessionkey.data, sessionkey.length); 421 free(sessionkey.data); 422 if (ret) { 423 if (type3.lm.data) 424 free(type3.lm.data); 425 if (type3.ntlm.data) 426 free(type3.ntlm.data); 427 _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); 428 *minor_status = ret; 429 return GSS_S_FAILURE; 430 } 431 ctx->status |= STATUS_SESSIONKEY; 432 433 } else { 434 struct ntlm_buf sessionkey; 435 unsigned char ntlmv2[16]; 436 struct ntlm_targetinfo ti; 437 438 /* verify infotarget */ 439 440 ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); 441 if(ret) { 442 _gss_ntlm_delete_sec_context(minor_status, 443 context_handle, NULL); 444 *minor_status = ret; 445 return GSS_S_FAILURE; 446 } 447 448 if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { 449 _gss_ntlm_delete_sec_context(minor_status, 450 context_handle, NULL); 451 *minor_status = EINVAL; 452 return GSS_S_FAILURE; 453 } 454 455 ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, 456 ctx->client->key.length, 457 ctx->client->username, 458 name->domain, 459 type2.challenge, 460 &type2.targetinfo, 461 ntlmv2, 462 &type3.ntlm); 463 if (ret) { 464 _gss_ntlm_delete_sec_context(minor_status, 465 context_handle, NULL); 466 *minor_status = ret; 467 return GSS_S_FAILURE; 468 } 469 470 ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), 471 &sessionkey, 472 &type3.sessionkey); 473 memset_s(ntlmv2, sizeof(ntlmv2), 0, sizeof(ntlmv2)); 474 if (ret) { 475 _gss_ntlm_delete_sec_context(minor_status, 476 context_handle, NULL); 477 *minor_status = ret; 478 return GSS_S_FAILURE; 479 } 480 481 ctx->flags |= NTLM_NEG_NTLM2_SESSION; 482 483 ret = krb5_data_copy(&ctx->sessionkey, 484 sessionkey.data, sessionkey.length); 485 free(sessionkey.data); 486 if (ret) { 487 _gss_ntlm_delete_sec_context(minor_status, 488 context_handle, NULL); 489 *minor_status = ret; 490 return GSS_S_FAILURE; 491 } 492 } 493 494 if (ctx->flags & NTLM_NEG_NTLM2_SESSION) { 495 ctx->status |= STATUS_SESSIONKEY; 496 _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), 497 ctx->sessionkey.data, 498 ctx->sessionkey.length); 499 _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), 500 ctx->sessionkey.data, 501 ctx->sessionkey.length); 502 } else { 503 ctx->status |= STATUS_SESSIONKEY; 504 RC4_set_key(&ctx->u.v1.crypto_recv.key, 505 ctx->sessionkey.length, 506 ctx->sessionkey.data); 507 RC4_set_key(&ctx->u.v1.crypto_send.key, 508 ctx->sessionkey.length, 509 ctx->sessionkey.data); 510 } 511 512 513 514 ret = heim_ntlm_encode_type3(&type3, &data, NULL); 515 free(type3.sessionkey.data); 516 if (type3.lm.data) 517 free(type3.lm.data); 518 if (type3.ntlm.data) 519 free(type3.ntlm.data); 520 if (ret) { 521 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 522 *minor_status = ret; 523 return GSS_S_FAILURE; 524 } 525 526 output_token->length = data.length; 527 output_token->value = data.data; 528 529 if (actual_mech_type) 530 *actual_mech_type = GSS_NTLM_MECHANISM; 531 if (ret_flags) 532 *ret_flags = 0; 533 if (time_rec) 534 *time_rec = GSS_C_INDEFINITE; 535 536 ctx->status |= STATUS_OPEN; 537 538 return GSS_S_COMPLETE; 539 } 540} 541