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 81 c->backends = calloc(c->num_backends, sizeof(c->backends[0])); 82 if (c->backends == NULL) { 83 free(c); 84 *minor_status = ENOMEM; 85 return GSS_S_FAILURE; 86 } 87 88 for (i = 0; i < c->num_backends; i++) { 89 struct ntlm_server_interface *iface = _ntlm_interfaces[i]; 90 91 maj_stat = (*iface->nsi_init)(minor_status, &c->backends[i].ctx); 92 if (GSS_ERROR(maj_stat)) 93 continue; 94 95 maj_stat = (*iface->nsi_probe)(minor_status, c->backends[i].ctx, domain, &c->probe_flags); 96 if (GSS_ERROR(maj_stat)) { 97 OM_uint32 junk; 98 (*iface->nsi_destroy)(&junk, c->backends[i].ctx); 99 c->backends[i].ctx = NULL; 100 } else { 101 _gss_mg_log(1, "ntlm-asc-probe: %s supported", iface->nsi_name); 102 } 103 104 c->backends[i].interface = iface; 105 found = 1; 106 } 107 108 if (!found) { 109 OM_uint32 junk; 110 gss_ctx_id_t gctx = (gss_ctx_id_t)c; 111 _gss_ntlm_delete_sec_context(&junk, &gctx, NULL); 112 return GSS_S_UNAVAILABLE; 113 } 114 115 *ctx = c; 116 117 return GSS_S_COMPLETE; 118 119} 120 121 122static OM_uint32 123build_type2_packet(OM_uint32 *minor_status, 124 ntlm_ctx c, 125 uint32_t flags, 126 const char *hostname, 127 const char *domain, 128 uint32_t *ret_flags, 129 struct ntlm_buf *out) 130{ 131 struct ntlm_type2 type2; 132 struct ntlm_buf data; 133 size_t i; 134 int ret; 135 136 _gss_mg_log(1, "ntlm-asc-type2"); 137 138 memset(&type2, 0, sizeof(type2)); 139 140 type2.flags = NTLM_NEG_UNICODE | NTLM_NEG_NTLM; 141 142 if ((flags & NTLM_NEG_UNICODE) == 0) { 143 *minor_status = HNTLM_ERR_OEM; 144 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 145 HNTLM_ERR_OEM, "ntlm: ntlm"); 146 } 147 148 memcpy(type2.challenge, c->challenge, sizeof(type2.challenge)); 149 150 type2.flags |= 151 NTLM_NEG_TARGET | 152 NTLM_NEG_VERSION | 153 NTLM_TARGET_DOMAIN | 154 NTLM_ENC_128 | 155 NTLM_NEG_TARGET_INFO | 156 NTLM_NEG_SIGN | 157 NTLM_NEG_SEAL | 158 NTLM_NEG_ALWAYS_SIGN | 159 NTLM_NEG_NTLM2_SESSION | 160 NTLM_NEG_KEYEX; 161 162 type2.os[0] = 0x0601b01d; 163 type2.os[1] = 0x0000000f; 164 165 /* Go over backends and find best match, if not found use empty targetinfo */ 166 for (i = 0; i < c->num_backends; i++) { 167 uint32_t negNtlmFlags = 0; 168 if ( c->backends[i].ctx == NULL) 169 continue; 170 c->backends[i].interface->nsi_ti(minor_status, c, 171 c->backends[i].ctx, 172 hostname, domain, &negNtlmFlags); 173 /* figure out flags that the plugin doesn't support */ 174 type2.flags &= ~negNtlmFlags; 175 } 176 177 if (c->ti.domainname) { 178 struct ntlm_buf d; 179 180 ret = heim_ntlm_encode_targetinfo(&c->ti, 1, &d); 181 if (ret) { 182 *minor_status = ENOMEM; 183 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 184 *minor_status, "out of memory"); 185 } 186 c->targetinfo.data = d.data; 187 c->targetinfo.length = d.length; 188 189 } else { 190 /* Send end of seq of targetinfo */ 191 c->targetinfo.data = malloc(4); 192 c->targetinfo.length = 4; 193 memcpy(c->targetinfo.data, "\x00\x00\x00\x00", 4); 194 } 195 196 type2.targetinfo = c->targetinfo; 197 type2.targetname = c->ti.domainname; 198 199 /* If we can't find a targetname, provide one, not having one make heim_ntlm_encode_type2 crash */ 200 if (type2.targetname == NULL) 201 type2.targetname = "BUILTIN"; 202 203 ret = heim_ntlm_encode_type2(&type2, &data); 204 if (ret) { 205 *minor_status = ret; 206 return GSS_S_FAILURE; 207 } 208 209 out->data = data.data; 210 out->length = data.length; 211 212 *ret_flags = type2.flags; 213 214 return GSS_S_COMPLETE; 215} 216 217 218 219krb5_error_code 220_krb5_kcm_ntlm_challenge(krb5_context context, int op __attribute((__unused__)), 221 uint8_t chal[8]); 222 223 224/* 225 * 226 */ 227 228OM_uint32 229_gss_ntlm_accept_sec_context 230(OM_uint32 * minor_status, 231 gss_ctx_id_t * context_handle, 232 const gss_cred_id_t acceptor_cred_handle, 233 const gss_buffer_t input_token_buffer, 234 const gss_channel_bindings_t input_chan_bindings, 235 gss_name_t * src_name, 236 gss_OID * mech_type, 237 gss_buffer_t output_token, 238 OM_uint32 * ret_flags, 239 OM_uint32 * time_rec, 240 gss_cred_id_t * delegated_cred_handle 241 ) 242{ 243 krb5_error_code ret; 244 struct ntlm_buf data; 245 OM_uint32 junk; 246 ntlm_ctx ctx; 247 248 output_token->value = NULL; 249 output_token->length = 0; 250 251 *minor_status = 0; 252 253 if (context_handle == NULL) 254 return GSS_S_FAILURE; 255 256 if (input_token_buffer == GSS_C_NO_BUFFER) 257 return GSS_S_FAILURE; 258 259 if (src_name) 260 *src_name = GSS_C_NO_NAME; 261 if (mech_type) 262 *mech_type = GSS_C_NO_OID; 263 if (ret_flags) 264 *ret_flags = 0; 265 if (time_rec) 266 *time_rec = 0; 267 if (delegated_cred_handle) 268 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 269 270 if (*context_handle == GSS_C_NO_CONTEXT) { 271 struct ntlm_type1 type1; 272 OM_uint32 major_status; 273 OM_uint32 retflags; 274 struct ntlm_buf out; 275 276 major_status = _gss_ntlm_allocate_ctx(minor_status, NULL, &ctx); 277 if (major_status) 278 return major_status; 279 *context_handle = (gss_ctx_id_t)ctx; 280 281 if (CCRandomCopyBytes(kCCRandomDefault, ctx->challenge, sizeof(ctx->challenge))) { 282 *minor_status = HNTLM_ERR_RAND; 283 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 284 HNTLM_ERR_RAND, "rand failed"); 285 } 286 287 { 288 static krb5_context context; 289 static dispatch_once_t once; 290 dispatch_once(&once, ^{ 291 krb5_init_context(&context); 292 }); 293 if (context) 294 _krb5_kcm_ntlm_challenge(context, 0, ctx->challenge); 295 } 296 297 data.data = input_token_buffer->value; 298 data.length = input_token_buffer->length; 299 300 ret = heim_ntlm_decode_type1(&data, &type1); 301 if (ret) { 302 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 303 *minor_status = ret; 304 return GSS_S_FAILURE; 305 } 306 307 if ((type1.flags & NTLM_NEG_UNICODE) == 0) { 308 heim_ntlm_free_type1(&type1); 309 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 310 *minor_status = HNTLM_ERR_OEM; 311 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 312 HNTLM_ERR_OEM, "not unicode"); 313 } 314 315 ret = krb5_data_copy(&ctx->type1, data.data, data.length); 316 if (ret) { 317 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 318 *minor_status = ret; 319 return GSS_S_FAILURE; 320 } 321 322 major_status = build_type2_packet(minor_status, 323 ctx, 324 type1.flags, 325 type1.hostname, 326 type1.domain, 327 &retflags, 328 &out); 329 heim_ntlm_free_type1(&type1); 330 if (major_status != GSS_S_COMPLETE) { 331 _gss_mg_log(1, "ntlm-asc-type2 failed with: %d", (int)major_status); 332 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 333 return major_status; 334 } 335 336 output_token->value = malloc(out.length); 337 if (output_token->value == NULL && out.length != 0) { 338 heim_ntlm_free_buf(&out); 339 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 340 _gss_mg_log(1, "ntlm-asc-type2 failed with no packet"); 341 *minor_status = ENOMEM; 342 return GSS_S_FAILURE; 343 } 344 memcpy(output_token->value, out.data, out.length); 345 output_token->length = out.length; 346 347 ret = krb5_data_copy(&ctx->type2, out.data, out.length); 348 heim_ntlm_free_buf(&out); 349 if (ret) { 350 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 351 _gss_mg_log(1, "ntlm-asc-type2 failed to copy type2 packet"); 352 *minor_status = ret; 353 return GSS_S_FAILURE; 354 } 355 356 if (mech_type) 357 *mech_type = GSS_NTLM_MECHANISM; 358 359 ctx->flags = retflags; 360 361 return GSS_S_CONTINUE_NEEDED; 362 } else { 363 ntlm_cred acceptor_cred = (ntlm_name)acceptor_cred_handle; 364 struct ntlm_buf session, uuid, pac; 365 uint32_t ntlmflags, avflags; 366 struct ntlm_type3 type3; 367 ntlm_name name = NULL; 368 OM_uint32 maj_stat; 369 size_t i; 370 371 ctx = (ntlm_ctx)*context_handle; 372 373 _gss_mg_log(1, "ntlm-asc-type3"); 374 375 data.data = input_token_buffer->value; 376 data.length = input_token_buffer->length; 377 378 ret = heim_ntlm_decode_type3(&data, 379 (ctx->flags & NTLM_NEG_UNICODE), 380 &type3); 381 if (ret) { 382 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 383 *minor_status = ret; 384 return GSS_S_FAILURE; 385 } 386 387 ret = krb5_data_copy(&ctx->type3, data.data, data.length); 388 if (ret) { 389 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 390 *minor_status = ret; 391 return GSS_S_FAILURE; 392 } 393 394 /* Go over backends and check for a success */ 395 maj_stat = GSS_S_FAILURE; 396 *minor_status = 0; 397 for (i = 0; i < ctx->num_backends; i++) { 398 if (ctx->backends[i].ctx == NULL) 399 continue; 400 401 maj_stat = ctx->backends[i].interface->nsi_type3(minor_status, 402 ctx, 403 ctx->backends[i].ctx, 404 &type3, 405 acceptor_cred, 406 &ntlmflags, 407 &avflags, 408 &session, 409 &name, 410 &uuid, 411 &pac); 412 _gss_mg_log(10, "ntlm-asc-type3: tried %s -> %d/%d", 413 ctx->backends[i].interface->nsi_name, 414 (int)maj_stat, (int)*minor_status); 415 if (maj_stat == GSS_S_COMPLETE) 416 break; 417 } 418 if (i >= ctx->num_backends) { 419 heim_ntlm_free_type3(&type3); 420 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 421 return maj_stat; 422 } 423 424 if ((avflags & NTLM_TI_AV_FLAG_MIC) && type3.mic_offset == 0) { 425 _gss_mg_log(1, "ntlm-asc-type3 mic missing from reply"); 426 *minor_status = EAUTH; 427 return GSS_S_FAILURE; 428 } 429 430 if (session.length && type3.mic_offset) { 431 uint8_t *p = (uint8_t *)ctx->type3.data + type3.mic_offset; 432 CCHmacContext c; 433 memset(p, 0, sizeof(type3.mic)); 434 435 CCHmacInit(&c, kCCHmacAlgMD5, session.data, session.length); 436 CCHmacUpdate(&c, ctx->type1.data, ctx->type1.length); 437 CCHmacUpdate(&c, ctx->type2.data, ctx->type2.length); 438 CCHmacUpdate(&c, ctx->type3.data, ctx->type3.length); 439 CCHmacFinal(&c, p); 440 441 if (memcmp(p, type3.mic, sizeof(type3.mic)) != 0) { 442 _gss_mg_log(1, "ntlm-asc-type3 mic invalid"); 443 free(session.data); 444 *minor_status = HNTLM_ERR_INVALID_MIC; 445 return GSS_S_FAILURE; 446 } 447 } 448 449 if (ntlmflags & NTLM_NEG_ANONYMOUS) 450 ctx->gssflags |= GSS_C_ANON_FLAG; 451 452 ctx->pac.value = pac.data; 453 ctx->pac.length = pac.length; 454 455 if (name) { 456 _gss_ntlm_release_name(&junk, &ctx->srcname); 457 ctx->srcname = (gss_name_t)name; 458 } else { 459 460 ctx->srcname = _gss_ntlm_create_name(minor_status, type3.username, type3.targetname, 0); 461 if (ctx->srcname == NULL) { 462 free(session.data); 463 heim_ntlm_free_type3(&type3); 464 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 465 return maj_stat; 466 } 467 } 468 /* XXX pull out the uuid and store in name */ 469 if (uuid.data) { 470 ntlm_name n = (ntlm_name) ctx->srcname; 471 if (uuid.length == 16) { 472 memcpy(n->ds_uuid, uuid.data, uuid.length); 473 n->flags |= NTLM_DS_UUID; 474 } 475 free(uuid.data); 476 } 477 478 /* do some logging */ 479 { 480 ntlm_name n = (ntlm_name) ctx->srcname; 481 _gss_mg_log(1, "ntlm-asc-type3: %s\\%s", 482 n->domain, n->user); 483 } 484 485 if (src_name) 486 _gss_ntlm_duplicate_name(&junk, ctx->srcname, src_name); 487 488 heim_ntlm_free_type3(&type3); 489 490 ret = krb5_data_copy(&ctx->sessionkey, 491 session.data, session.length); 492 free(session.data); 493 if (ret) { 494 if (src_name) 495 _gss_ntlm_release_name(&junk, src_name); 496 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 497 *minor_status = ret; 498 return GSS_S_FAILURE; 499 } 500 501 _gss_ntlm_set_keys(ctx); 502 503 if (mech_type) 504 *mech_type = GSS_NTLM_MECHANISM; 505 if (time_rec) 506 *time_rec = GSS_C_INDEFINITE; 507 508 ctx->status |= STATUS_OPEN; 509 510 if (ret_flags) 511 *ret_flags = ctx->gssflags; 512 513 return GSS_S_COMPLETE; 514 } 515} 516