1/* 2 * Copyright (c) 2006 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "ntlm.h" 37 38/* 39 * 40 */ 41 42static struct ntlm_server_interface * _ntlm_interfaces[] = { 43#if !defined(__APPLE_TARGET_EMBEDDED__) 44 &ntlmsspi_dstg_digest 45#endif 46}; 47 48/* 49 * Allocate a NTLM context handle for the first provider that 50 * is up and running. 51 */ 52OM_uint32 53_gss_ntlm_allocate_ctx(OM_uint32 *minor_status, const char *domain, ntlm_ctx *ctx) 54{ 55 ntlm_ctx c; 56 OM_uint32 maj_stat; 57 size_t i; 58 int found = 0; 59 60 *ctx = NULL; 61 62 /* 63 * Check that NTLM is enabled before going forward 64 */ 65 66 if (!gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V1, NULL) && 67 !gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V2, NULL)) 68 { 69 *minor_status = 0; 70 return GSS_S_UNAVAILABLE; 71 } 72 73 c = calloc(1, sizeof(*c)); 74 if (c == NULL) { 75 *minor_status = ENOMEM; 76 return GSS_S_FAILURE; 77 } 78 79 c->num_backends = sizeof(_ntlm_interfaces)/sizeof(_ntlm_interfaces[0]); 80 if (c->num_backends == 0) { 81 free(c); 82 *minor_status = EINVAL; 83 return GSS_S_FAILURE; 84 } 85 86 c->backends = calloc(c->num_backends, sizeof(c->backends[0])); 87 if (c->backends == NULL) { 88 free(c); 89 *minor_status = ENOMEM; 90 return GSS_S_FAILURE; 91 } 92 93 for (i = 0; i < c->num_backends; i++) { 94 struct ntlm_server_interface *iface = _ntlm_interfaces[i]; 95 96 maj_stat = (*iface->nsi_init)(minor_status, &c->backends[i].ctx); 97 if (GSS_ERROR(maj_stat)) 98 continue; 99 100 maj_stat = (*iface->nsi_probe)(minor_status, c->backends[i].ctx, domain, &c->probe_flags); 101 if (GSS_ERROR(maj_stat)) { 102 OM_uint32 junk; 103 (*iface->nsi_destroy)(&junk, c->backends[i].ctx); 104 c->backends[i].ctx = NULL; 105 } else { 106 _gss_mg_log(1, "ntlm-asc-probe: %s supported", iface->nsi_name); 107 } 108 109 c->backends[i].interface = iface; 110 found = 1; 111 } 112 113 if (!found) { 114 OM_uint32 junk; 115 gss_ctx_id_t gctx = (gss_ctx_id_t)c; 116 _gss_ntlm_delete_sec_context(&junk, &gctx, NULL); 117 return GSS_S_UNAVAILABLE; 118 } 119 120 *ctx = c; 121 122 return GSS_S_COMPLETE; 123 124} 125 126 127static OM_uint32 128build_type2_packet(OM_uint32 *minor_status, 129 ntlm_ctx c, 130 uint32_t flags, 131 const char *hostname, 132 const char *domain, 133 uint32_t *ret_flags, 134 struct ntlm_buf *out) 135{ 136 struct ntlm_type2 type2; 137 struct ntlm_buf data; 138 size_t i; 139 int ret; 140 141 _gss_mg_log(1, "ntlm-asc-type2"); 142 143 memset(&type2, 0, sizeof(type2)); 144 145 type2.flags = NTLM_NEG_UNICODE | NTLM_NEG_NTLM; 146 147 if ((flags & NTLM_NEG_UNICODE) == 0) { 148 *minor_status = HNTLM_ERR_OEM; 149 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 150 HNTLM_ERR_OEM, "ntlm: ntlm"); 151 } 152 153 memcpy(type2.challenge, c->challenge, sizeof(type2.challenge)); 154 155 type2.flags |= 156 NTLM_NEG_TARGET | 157 NTLM_NEG_VERSION | 158 NTLM_TARGET_DOMAIN | 159 NTLM_ENC_128 | 160 NTLM_NEG_TARGET_INFO | 161 NTLM_NEG_SIGN | 162 NTLM_NEG_SEAL | 163 NTLM_NEG_ALWAYS_SIGN | 164 NTLM_NEG_NTLM2_SESSION | 165 NTLM_NEG_KEYEX; 166 167 type2.os[0] = 0x0601b01d; 168 type2.os[1] = 0x0000000f; 169 170 /* Go over backends and find best match, if not found use empty targetinfo */ 171 for (i = 0; i < c->num_backends; i++) { 172 uint32_t negNtlmFlags = 0; 173 if ( c->backends[i].ctx == NULL) 174 continue; 175 c->backends[i].interface->nsi_ti(minor_status, c, 176 c->backends[i].ctx, 177 hostname, domain, &negNtlmFlags); 178 /* figure out flags that the plugin doesn't support */ 179 type2.flags &= ~negNtlmFlags; 180 } 181 182 if (c->ti.domainname) { 183 struct ntlm_buf d; 184 185 ret = heim_ntlm_encode_targetinfo(&c->ti, 1, &d); 186 if (ret) { 187 *minor_status = ENOMEM; 188 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 189 *minor_status, "out of memory"); 190 } 191 c->targetinfo.data = d.data; 192 c->targetinfo.length = d.length; 193 194 } else { 195 /* Send end of seq of targetinfo */ 196 c->targetinfo.data = malloc(4); 197 c->targetinfo.length = 4; 198 memcpy(c->targetinfo.data, "\x00\x00\x00\x00", 4); 199 } 200 201 type2.targetinfo = c->targetinfo; 202 type2.targetname = c->ti.domainname; 203 204 /* If we can't find a targetname, provide one, not having one make heim_ntlm_encode_type2 crash */ 205 if (type2.targetname == NULL) 206 type2.targetname = "BUILTIN"; 207 208 ret = heim_ntlm_encode_type2(&type2, &data); 209 if (ret) { 210 *minor_status = ret; 211 return GSS_S_FAILURE; 212 } 213 214 out->data = data.data; 215 out->length = data.length; 216 217 *ret_flags = type2.flags; 218 219 return GSS_S_COMPLETE; 220} 221 222 223 224krb5_error_code 225_krb5_kcm_ntlm_challenge(krb5_context context, int op __attribute((__unused__)), 226 uint8_t chal[8]); 227 228 229/* 230 * 231 */ 232 233OM_uint32 234_gss_ntlm_accept_sec_context 235(OM_uint32 * minor_status, 236 gss_ctx_id_t * context_handle, 237 const gss_cred_id_t acceptor_cred_handle, 238 const gss_buffer_t input_token_buffer, 239 const gss_channel_bindings_t input_chan_bindings, 240 gss_name_t * src_name, 241 gss_OID * mech_type, 242 gss_buffer_t output_token, 243 OM_uint32 * ret_flags, 244 OM_uint32 * time_rec, 245 gss_cred_id_t * delegated_cred_handle 246 ) 247{ 248 krb5_error_code ret; 249 struct ntlm_buf data; 250 OM_uint32 junk; 251 ntlm_ctx ctx; 252 253 output_token->value = NULL; 254 output_token->length = 0; 255 256 *minor_status = 0; 257 258 if (context_handle == NULL) 259 return GSS_S_FAILURE; 260 261 if (input_token_buffer == GSS_C_NO_BUFFER) 262 return GSS_S_FAILURE; 263 264 if (src_name) 265 *src_name = GSS_C_NO_NAME; 266 if (mech_type) 267 *mech_type = GSS_C_NO_OID; 268 if (ret_flags) 269 *ret_flags = 0; 270 if (time_rec) 271 *time_rec = 0; 272 if (delegated_cred_handle) 273 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 274 275 if (*context_handle == GSS_C_NO_CONTEXT) { 276 struct ntlm_type1 type1; 277 OM_uint32 major_status; 278 OM_uint32 retflags; 279 struct ntlm_buf out; 280 281 major_status = _gss_ntlm_allocate_ctx(minor_status, NULL, &ctx); 282 if (major_status) 283 return major_status; 284 *context_handle = (gss_ctx_id_t)ctx; 285 286 if (CCRandomCopyBytes(kCCRandomDefault, ctx->challenge, sizeof(ctx->challenge))) { 287 *minor_status = HNTLM_ERR_RAND; 288 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 289 HNTLM_ERR_RAND, "rand failed"); 290 } 291 292 { 293 static krb5_context context; 294 static dispatch_once_t once; 295 dispatch_once(&once, ^{ 296 krb5_init_context(&context); 297 }); 298 if (context) 299 _krb5_kcm_ntlm_challenge(context, 0, ctx->challenge); 300 } 301 302 data.data = input_token_buffer->value; 303 data.length = input_token_buffer->length; 304 305 ret = heim_ntlm_decode_type1(&data, &type1); 306 if (ret) { 307 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 308 *minor_status = ret; 309 return GSS_S_FAILURE; 310 } 311 312 if ((type1.flags & NTLM_NEG_UNICODE) == 0) { 313 heim_ntlm_free_type1(&type1); 314 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 315 *minor_status = HNTLM_ERR_OEM; 316 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 317 HNTLM_ERR_OEM, "not unicode"); 318 } 319 320 ret = krb5_data_copy(&ctx->type1, data.data, data.length); 321 if (ret) { 322 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 323 *minor_status = ret; 324 return GSS_S_FAILURE; 325 } 326 327 major_status = build_type2_packet(minor_status, 328 ctx, 329 type1.flags, 330 type1.hostname, 331 type1.domain, 332 &retflags, 333 &out); 334 heim_ntlm_free_type1(&type1); 335 if (major_status != GSS_S_COMPLETE) { 336 _gss_mg_log(1, "ntlm-asc-type2 failed with: %d", (int)major_status); 337 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 338 return major_status; 339 } 340 341 output_token->value = malloc(out.length); 342 if (output_token->value == NULL && out.length != 0) { 343 heim_ntlm_free_buf(&out); 344 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 345 _gss_mg_log(1, "ntlm-asc-type2 failed with no packet"); 346 *minor_status = ENOMEM; 347 return GSS_S_FAILURE; 348 } 349 memcpy(output_token->value, out.data, out.length); 350 output_token->length = out.length; 351 352 ret = krb5_data_copy(&ctx->type2, out.data, out.length); 353 heim_ntlm_free_buf(&out); 354 if (ret) { 355 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 356 _gss_mg_log(1, "ntlm-asc-type2 failed to copy type2 packet"); 357 *minor_status = ret; 358 return GSS_S_FAILURE; 359 } 360 361 if (mech_type) 362 *mech_type = GSS_NTLM_MECHANISM; 363 364 ctx->flags = retflags; 365 366 return GSS_S_CONTINUE_NEEDED; 367 } else { 368 ntlm_cred acceptor_cred = (ntlm_name)acceptor_cred_handle; 369 struct ntlm_buf session, uuid, pac; 370 uint32_t ntlmflags = 0, avflags = 0; 371 struct ntlm_type3 type3; 372 ntlm_name name = NULL; 373 OM_uint32 maj_stat; 374 size_t i; 375 376 ctx = (ntlm_ctx)*context_handle; 377 378 _gss_mg_log(1, "ntlm-asc-type3"); 379 380 data.data = input_token_buffer->value; 381 data.length = input_token_buffer->length; 382 383 ret = heim_ntlm_decode_type3(&data, 384 (ctx->flags & NTLM_NEG_UNICODE), 385 &type3); 386 if (ret) { 387 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 388 *minor_status = ret; 389 return GSS_S_FAILURE; 390 } 391 392 ret = krb5_data_copy(&ctx->type3, data.data, data.length); 393 if (ret) { 394 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 395 *minor_status = ret; 396 return GSS_S_FAILURE; 397 } 398 399 /* Go over backends and check for a success */ 400 maj_stat = GSS_S_FAILURE; 401 *minor_status = 0; 402 for (i = 0; i < ctx->num_backends; i++) { 403 if (ctx->backends[i].ctx == NULL) 404 continue; 405 406 maj_stat = ctx->backends[i].interface->nsi_type3(minor_status, 407 ctx, 408 ctx->backends[i].ctx, 409 &type3, 410 acceptor_cred, 411 &ntlmflags, 412 &avflags, 413 &session, 414 &name, 415 &uuid, 416 &pac); 417 _gss_mg_log(10, "ntlm-asc-type3: tried %s -> %d/%d", 418 ctx->backends[i].interface->nsi_name, 419 (int)maj_stat, (int)*minor_status); 420 if (maj_stat == GSS_S_COMPLETE) 421 break; 422 } 423 if (i >= ctx->num_backends) { 424 heim_ntlm_free_type3(&type3); 425 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 426 return maj_stat; 427 } 428 429 if ((avflags & NTLM_TI_AV_FLAG_MIC) && type3.mic_offset == 0) { 430 _gss_mg_log(1, "ntlm-asc-type3 mic missing from reply"); 431 *minor_status = EAUTH; 432 return GSS_S_FAILURE; 433 } 434 435 _gss_ntlm_debug_key(10, "session key", session.data, session.length); 436 437 if (session.length && type3.mic_offset) { 438 uint8_t *p = (uint8_t *)ctx->type3.data + type3.mic_offset; 439 CCHmacContext c; 440 memset(p, 0, sizeof(type3.mic)); 441 442 CCHmacInit(&c, kCCHmacAlgMD5, session.data, session.length); 443 CCHmacUpdate(&c, ctx->type1.data, ctx->type1.length); 444 CCHmacUpdate(&c, ctx->type2.data, ctx->type2.length); 445 CCHmacUpdate(&c, ctx->type3.data, ctx->type3.length); 446 CCHmacFinal(&c, p); 447 448 if (memcmp(p, type3.mic, sizeof(type3.mic)) != 0) { 449 _gss_ntlm_debug_hex(10, "mic", type3.mic, sizeof(type3.mic)); 450 _gss_ntlm_debug_hex(10, "ntlm-asc-type3 mic invalid", p, sizeof(type3.mic)); 451 free(session.data); 452 *minor_status = HNTLM_ERR_INVALID_MIC; 453 return GSS_S_FAILURE; 454 } 455 } 456 457 if (ntlmflags & NTLM_NEG_ANONYMOUS) 458 ctx->gssflags |= GSS_C_ANON_FLAG; 459 460 ctx->pac.value = pac.data; 461 ctx->pac.length = pac.length; 462 463 if (name) { 464 _gss_ntlm_release_name(&junk, &ctx->srcname); 465 ctx->srcname = (gss_name_t)name; 466 } else { 467 468 ctx->srcname = _gss_ntlm_create_name(minor_status, type3.username, type3.targetname, 0); 469 if (ctx->srcname == NULL) { 470 free(session.data); 471 heim_ntlm_free_type3(&type3); 472 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 473 return maj_stat; 474 } 475 } 476 /* XXX pull out the uuid and store in name */ 477 if (uuid.data) { 478 ntlm_name n = (ntlm_name) ctx->srcname; 479 if (uuid.length == 16) { 480 memcpy(n->ds_uuid, uuid.data, uuid.length); 481 n->flags |= NTLM_DS_UUID; 482 } 483 free(uuid.data); 484 } 485 486 /* do some logging */ 487 { 488 ntlm_name n = (ntlm_name) ctx->srcname; 489 _gss_mg_log(1, "ntlm-asc-type3: %s\\%s", 490 n->domain, n->user); 491 } 492 493 if (src_name) 494 _gss_ntlm_duplicate_name(&junk, ctx->srcname, src_name); 495 496 heim_ntlm_free_type3(&type3); 497 498 ret = krb5_data_copy(&ctx->sessionkey, 499 session.data, session.length); 500 free(session.data); 501 if (ret) { 502 if (src_name) 503 _gss_ntlm_release_name(&junk, src_name); 504 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 505 *minor_status = ret; 506 return GSS_S_FAILURE; 507 } 508 509 _gss_ntlm_set_keys(ctx); 510 511 if (mech_type) 512 *mech_type = GSS_NTLM_MECHANISM; 513 if (time_rec) 514 *time_rec = GSS_C_INDEFINITE; 515 516 ctx->status |= STATUS_OPEN; 517 518 if (ret_flags) 519 *ret_flags = ctx->gssflags; 520 521 return GSS_S_COMPLETE; 522 } 523} 524