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#include <fnmatch.h> 36 37static int 38validate_hostname(ntlm_name name) 39{ 40 CFArrayRef array; 41 CFStringRef el; 42 CFIndex n, max; 43 char *str; 44 int res = 0; 45 46 if (name->domain == NULL) 47 return 0; 48 49 array = _gss_mg_copy_key(CFSTR("com.apple.GSS.NTLM"), CFSTR("AllowedHosts")); 50 if (array == NULL) 51 return 1; 52 53 if (CFGetTypeID(array) != CFArrayGetTypeID()) { 54 _gss_mg_log(1, "NTLM: invalid type of AllowedHosts"); 55 CFRelease(array); 56 return 0; 57 } 58 59 max = CFArrayGetCount(array); 60 61 for (n = 0, res = 0; n < max && !res; n++) { 62 el = CFArrayGetValueAtIndex(array, n); 63 if (el == NULL || CFGetTypeID(el) != CFStringGetTypeID()) 64 continue; 65 str = rk_cfstring2cstring(el); 66 if (str == NULL) 67 continue; 68 res = (fnmatch(str, name->domain, FNM_CASEFOLD) == 0); 69 free(str); 70 } 71 72 CFRelease(array); 73 74 return res; 75} 76 77 78static OM_uint32 79_gss_ntlm_copy_cred(OM_uint32 * minor, ntlm_cred from, ntlm_cred *to) 80{ 81 return _gss_ntlm_duplicate_name(minor, (gss_name_t)from, (gss_name_t *)to); 82} 83 84OM_uint32 85_gss_ntlm_init_sec_context(OM_uint32 * minor_status, 86 const gss_cred_id_t initiator_cred_handle, 87 gss_ctx_id_t * context_handle, 88 const gss_name_t target_name, 89 const gss_OID mech_type, 90 OM_uint32 req_flags, 91 OM_uint32 time_req, 92 const gss_channel_bindings_t input_chan_bindings, 93 const gss_buffer_t input_token, 94 gss_OID * actual_mech_type, 95 gss_buffer_t output_token, 96 OM_uint32 * ret_flags, 97 OM_uint32 * time_rec) 98{ 99 OM_uint32 major; 100 ntlm_ctx ctx; 101 ntlm_name name = (ntlm_name) target_name; 102 103 *minor_status = 0; 104 105 if (ret_flags) 106 *ret_flags = 0; 107 if (time_rec) 108 *time_rec = 0; 109 if (actual_mech_type) 110 *actual_mech_type = GSS_C_NO_OID; 111 if (name == NULL) 112 return GSS_S_BAD_NAME; 113 114 /* 115 * Check that NTLM is enabled before going forward 116 */ 117 118 if (!gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V1, NULL) && 119 !gss_mo_get(GSS_NTLM_MECHANISM, GSS_C_NTLM_V2, NULL)) 120 { 121 *minor_status = 0; 122 return GSS_S_UNAVAILABLE; 123 } 124 125 ctx = (ntlm_ctx) *context_handle; 126 127 if (actual_mech_type) 128 *actual_mech_type = GSS_NTLM_MECHANISM; 129 if (ret_flags) 130 *ret_flags = 0; 131 132 if (ctx == NULL) { 133 struct ntlm_type1 type1; 134 struct ntlm_buf data; 135 uint32_t flags = 0; 136 int ret; 137 138 if (!validate_hostname(name)) 139 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 140 (*minor_status = EAUTH), 141 "Not allowed to use NTLM to host %s", 142 name->domain ? name->domain : "???"); 143 144 ctx = calloc(1, sizeof(*ctx)); 145 if (ctx == NULL) { 146 *minor_status = ENOMEM; 147 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, 148 ENOMEM, "out of memory"); 149 } 150 *context_handle = (gss_ctx_id_t) ctx; 151 152 ctx->status |= STATUS_CLIENT; 153 154 if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) { 155 ntlm_cred cred = (ntlm_cred) initiator_cred_handle; 156 major = _gss_ntlm_copy_cred(minor_status, cred, &ctx->client); 157 } else { 158 ntlm_name_desc client_name; 159 client_name.user = ""; 160 client_name.domain = ""; 161 client_name.flags = 0; 162 163 major = _gss_ntlm_have_cred(minor_status, &client_name, &ctx->client); 164 } 165 166 if (major) { 167 OM_uint32 junk; 168 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 169 return major; 170 } 171 172 major = _gss_ntlm_duplicate_name(minor_status, (gss_name_t)name, &ctx->targetname); 173 if (major) { 174 OM_uint32 junk; 175 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 176 return major; 177 } 178 179 ret = asprintf(&ctx->clientsuppliedtargetname, "%s/%s", name->user, name->domain); 180 if (ret < 0 || ctx->clientsuppliedtargetname == NULL) { 181 OM_uint32 junk; 182 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 183 return major; 184 } 185 186 flags |= NTLM_NEG_UNICODE; 187 flags |= NTLM_NEG_NTLM; 188 flags |= NTLM_NEG_TARGET; 189 flags |= NTLM_NEG_VERSION; 190 191 if (req_flags & GSS_C_ANON_FLAG) { 192 flags |= NTLM_NEG_ANONYMOUS; 193 } else { 194 /* can't work with anon cred, just stop now */ 195 if ((ctx->client->flags & NTLM_ANON_NAME) != 0) { 196 OM_uint32 junk; 197 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 198 gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_NO_CRED, 0, 199 "Cant authenticate with anon name"); 200 *minor_status = 0; 201 return GSS_S_NO_CRED; 202 } 203 204 if (req_flags & GSS_C_CONF_FLAG) { 205 flags |= NTLM_NEG_SEAL; 206 flags |= NTLM_NEG_SIGN; 207 flags |= NTLM_NEG_ALWAYS_SIGN; 208 } 209 if (req_flags & GSS_C_INTEG_FLAG) { 210 flags |= NTLM_NEG_SIGN; 211 flags |= NTLM_NEG_ALWAYS_SIGN; 212 } 213 214 215 flags |= NTLM_NEG_NTLM2_SESSION; 216 flags |= NTLM_ENC_128; 217 flags |= NTLM_NEG_KEYEX; 218 flags |= NTLM_NEG_TARGET_INFO; 219 } 220 221 memset(&type1, 0, sizeof(type1)); 222 223 type1.flags = flags; 224 type1.domain = NULL; 225 type1.hostname = NULL; 226 type1.os[0] = 0x0601b01d; 227 type1.os[1] = 0x0000000f; 228 229 ret = heim_ntlm_encode_type1(&type1, &data); 230 if (ret) { 231 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 232 *minor_status = ret; 233 return GSS_S_FAILURE; 234 } 235 output_token->value = data.data; 236 output_token->length = data.length; 237 238 ret = krb5_data_copy(&ctx->type1, data.data, data.length); 239 if (ret) { 240 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 241 *minor_status = ret; 242 return GSS_S_FAILURE; 243 } 244 245 ctx->flags = flags; 246 247 _gss_mg_log(1, "ntlm-isc-type1: %s\\%s", 248 ctx->client->domain, ctx->client->user); 249 250 return GSS_S_CONTINUE_NEEDED; 251 252 } else if (ctx->flags & NTLM_NEG_ANONYMOUS) { 253 struct ntlm_type2 type2; 254 struct ntlm_type3 type3; 255 struct ntlm_buf ndata; 256 int ret; 257 258 memset(&type2, 0, sizeof(type2)); 259 memset(&type3, 0, sizeof(type3)); 260 261 if (input_token == GSS_C_NO_BUFFER) 262 return GSS_S_FAILURE; 263 264 ndata.data = input_token->value; 265 ndata.length = input_token->length; 266 267 ret = heim_ntlm_decode_type2(&ndata, &type2); 268 if (ret) { 269 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 270 *minor_status = ret; 271 return GSS_S_FAILURE; 272 } 273 274 type3.username = ""; 275 type3.flags = ctx->flags; 276 type3.targetname = ""; 277 type3.ws = ""; 278 279 /* This is crazy, but apperently needed ? */ 280 type3.lm.data = "\x00"; 281 type3.lm.length = 1; 282 283 ret = heim_ntlm_encode_type3(&type3, &ndata, NULL); 284 heim_ntlm_free_type2(&type2); 285 if (ret) { 286 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 287 *minor_status = ret; 288 return GSS_S_FAILURE; 289 } 290 291 output_token->value = ndata.data; 292 output_token->length = ndata.length; 293 294 ctx->gssflags |= GSS_C_ANON_FLAG; 295 296 ctx->srcname = _gss_ntlm_create_name(minor_status, "", "", 0); 297 if (ctx->srcname == NULL) { 298 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 299 return GSS_S_FAILURE; 300 } 301 302 _gss_mg_log(1, "ntlm-isc-type3: anonymous"); 303 304 } else { 305 krb5_context context; 306 krb5_error_code ret; 307 krb5_storage *request, *response = NULL; 308 krb5_data response_data, data, cb; 309 char *ruser = NULL, *rdomain = NULL; 310 uint8_t channelbinding[16]; 311 312 data.data = input_token->value; 313 data.length = input_token->length; 314 315 if (input_chan_bindings) { 316 317 major = gss_mg_gen_cb(minor_status, input_chan_bindings, 318 channelbinding, NULL); 319 if (major) { 320 OM_uint32 junk; 321 _gss_ntlm_delete_sec_context(&junk, context_handle, NULL); 322 return major; 323 } 324 325 cb.data = channelbinding; 326 cb.length = sizeof(channelbinding); 327 } else 328 krb5_data_zero(&cb); 329 330 ret = krb5_init_context(&context); 331 if (ret) { 332 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 333 return ret; 334 } 335 ret = krb5_kcm_storage_request(context, KCM_OP_DO_NTLM_AUTH, &request); 336 if (ret) { 337 krb5_free_context(context); 338 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 339 *minor_status = ret; 340 return GSS_S_FAILURE; 341 } 342 ret = krb5_store_stringz(request, ctx->client->user); 343 if (ret == 0) 344 ret = krb5_store_stringz(request, ctx->client->domain); 345 if (ret == 0) 346 ret = krb5_store_data(request, data); 347 if (ret == 0) 348 ret = krb5_store_data(request, cb); 349 if (ret == 0) 350 ret = krb5_store_data(request, ctx->type1); 351 if (ret == 0) 352 ret = krb5_store_stringz(request, ctx->clientsuppliedtargetname); 353 if (ret == 0) 354 ret = krb5_store_uint32(request, ctx->flags); 355 356 if (ret == 0) 357 ret = krb5_kcm_call(context, request, &response, &response_data); 358 359 krb5_free_context(context); 360 krb5_storage_free(request); 361 if (ret) { 362 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 363 *minor_status = ret; 364 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret, 365 "failed to create ntlm response"); 366 } 367 ret = krb5_ret_data(response, &data); 368 if (ret == 0) 369 ret = krb5_ret_uint32(response, &ctx->kcmflags); 370 if (ret == 0) 371 ret = krb5_ret_data(response, &ctx->sessionkey); 372 if (ret == 0) 373 ret = krb5_ret_string(response, &ruser); 374 if (ret == 0) 375 ret = krb5_ret_string(response, &rdomain); 376 if (ret == 0) 377 ret = krb5_ret_uint32(response, &ctx->flags); 378 if (ret) 379 ret = KRB5_CC_IO; 380 381 krb5_storage_free(response); 382 krb5_data_free(&response_data); 383 384 _gss_mg_log(1, "ntlm-isc-type3: kcm returned %d", ret); 385 386 if (ret) { 387 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 388 krb5_data_free(&data); 389 free(ruser); 390 free(rdomain); 391 *minor_status = ret; 392 return gss_mg_set_error_string(GSS_NTLM_MECHANISM, GSS_S_FAILURE, ret, 393 "failed parse kcm reply"); 394 } 395 396 _gss_mg_log(1, "ntlm-isc-type3: %s\\%s", rdomain, ruser); 397 398 ctx->srcname = _gss_ntlm_create_name(minor_status, ruser, rdomain, 0); 399 free(ruser); 400 free(rdomain); 401 if (ctx->srcname == NULL) { 402 _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); 403 krb5_data_free(&data); 404 return GSS_S_FAILURE; 405 } 406 407 output_token->length = data.length; 408 output_token->value = data.data; 409 410 } 411 412 ctx->status |= STATUS_OPEN; 413 414 /* 415 * Now that we have a session key, let setup crypto layer 416 */ 417 418 _gss_ntlm_set_keys(ctx); 419 420 if (ret_flags) 421 *ret_flags = ctx->gssflags; 422 423 if (time_rec) 424 *time_rec = GSS_C_INDEFINITE; 425 426 427 428 return GSS_S_COMPLETE; 429} 430