1/* 2 * Copyright (c) 2006 - 2007 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 36#ifdef DIGEST 37 38/* 39 * 40 */ 41 42struct ntlmkrb5 { 43 krb5_context context; 44 krb5_ntlm ntlm; 45 krb5_realm kerberos_realm; 46 krb5_ccache id; 47 krb5_data opaque; 48 int destroy; 49 OM_uint32 flags; 50 struct ntlm_buf key; 51 krb5_data sessionkey; 52}; 53 54static OM_uint32 kdc_destroy(OM_uint32 *, void *); 55 56/* 57 * Get credential cache that the ntlm code can use to talk to the KDC 58 * using the digest API. 59 */ 60 61static krb5_error_code 62get_ccache(krb5_context context, int *destroy, krb5_ccache *id) 63{ 64 krb5_principal principal = NULL; 65 krb5_error_code ret; 66 krb5_keytab kt = NULL; 67 68 *id = NULL; 69 70 if (!issuid()) { 71 const char *cache; 72 73 cache = getenv("NTLM_ACCEPTOR_CCACHE"); 74 if (cache) { 75 ret = krb5_cc_resolve(context, cache, id); 76 if (ret) 77 goto out; 78 return 0; 79 } 80 } 81 82 ret = krb5_sname_to_principal(context, NULL, "host", 83 KRB5_NT_SRV_HST, &principal); 84 if (ret) 85 goto out; 86 87 ret = krb5_cc_cache_match(context, principal, id); 88 if (ret == 0) 89 return 0; 90 91 /* did not find in default credcache, lets try default keytab */ 92 ret = krb5_kt_default(context, &kt); 93 if (ret) 94 goto out; 95 96 /* XXX check in keytab */ 97 { 98 krb5_get_init_creds_opt *opt; 99 krb5_creds cred; 100 101 memset(&cred, 0, sizeof(cred)); 102 103 ret = krb5_cc_new_unique(context, "MEMORY", NULL, id); 104 if (ret) 105 goto out; 106 *destroy = 1; 107 ret = krb5_get_init_creds_opt_alloc(context, &opt); 108 if (ret) 109 goto out; 110 ret = krb5_get_init_creds_keytab (context, 111 &cred, 112 principal, 113 kt, 114 0, 115 NULL, 116 opt); 117 krb5_get_init_creds_opt_free(context, opt); 118 if (ret) 119 goto out; 120 ret = krb5_cc_initialize (context, *id, cred.client); 121 if (ret) { 122 krb5_free_cred_contents (context, &cred); 123 goto out; 124 } 125 ret = krb5_cc_store_cred (context, *id, &cred); 126 krb5_free_cred_contents (context, &cred); 127 if (ret) 128 goto out; 129 } 130 131 krb5_kt_close(context, kt); 132 133 return 0; 134 135out: 136 if (*id) { 137 if (*destroy) 138 krb5_cc_destroy(context, *id); 139 else 140 krb5_cc_close(context, *id); 141 *id = NULL; 142 } 143 144 if (kt) 145 krb5_kt_close(context, kt); 146 147 if (principal) 148 krb5_free_principal(context, principal); 149 return ret; 150} 151 152/* 153 * 154 */ 155 156static OM_uint32 157kdc_alloc(OM_uint32 *minor, void **ctx) 158{ 159 krb5_error_code ret; 160 struct ntlmkrb5 *c; 161 OM_uint32 junk; 162 163 c = calloc(1, sizeof(*c)); 164 if (c == NULL) { 165 *minor = ENOMEM; 166 return GSS_S_FAILURE; 167 } 168 169 ret = krb5_init_context(&c->context); 170 if (ret) { 171 kdc_destroy(&junk, c); 172 *minor = ret; 173 return GSS_S_FAILURE; 174 } 175 176 ret = get_ccache(c->context, &c->destroy, &c->id); 177 if (ret) { 178 kdc_destroy(&junk, c); 179 *minor = ret; 180 return GSS_S_FAILURE; 181 } 182 183 ret = krb5_ntlm_alloc(c->context, &c->ntlm); 184 if (ret) { 185 kdc_destroy(&junk, c); 186 *minor = ret; 187 return GSS_S_FAILURE; 188 } 189 190 *ctx = c; 191 192 return GSS_S_COMPLETE; 193} 194 195static int 196kdc_probe(OM_uint32 *minor, void *ctx, const char *realm, unsigned int *flags) 197{ 198 struct ntlmkrb5 *c = ctx; 199 krb5_error_code ret; 200 unsigned flags; 201 202 ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags); 203 if (ret) 204 return ret; 205 206 if ((flags & (1|2|4)) == 0) 207 return HNTLM_ERR_AUTH; 208 209 return 0; 210} 211 212/* 213 * 214 */ 215 216static OM_uint32 217kdc_destroy(OM_uint32 *minor, void *ctx) 218{ 219 struct ntlmkrb5 *c = ctx; 220 krb5_data_free(&c->opaque); 221 krb5_data_free(&c->sessionkey); 222 if (c->ntlm) 223 krb5_ntlm_free(c->context, c->ntlm); 224 if (c->id) { 225 if (c->destroy) 226 krb5_cc_destroy(c->context, c->id); 227 else 228 krb5_cc_close(c->context, c->id); 229 } 230 if (c->context) 231 krb5_free_context(c->context); 232 memset(c, 0, sizeof(*c)); 233 free(c); 234 235 return GSS_S_COMPLETE; 236} 237 238/* 239 * 240 */ 241 242static OM_uint32 243kdc_type2(OM_uint32 *minor_status, 244 ntlm_ctx ntlmctx, 245 void *ctx, 246 uint32_t flags, 247 const char *hostname, 248 const char *domain, 249 uint32_t *ret_flags, 250 struct ntlm_buf *out) 251{ 252 struct ntlmkrb5 *c = ctx; 253 krb5_error_code ret; 254 struct ntlm_type2 type2; 255 krb5_data challenge; 256 struct ntlm_buf data; 257 krb5_data ti; 258 259 memset(&type2, 0, sizeof(type2)); 260 261 /* 262 * Request data for type 2 packet from the KDC. 263 */ 264 ret = krb5_ntlm_init_request(c->context, 265 c->ntlm, 266 NULL, 267 c->id, 268 flags, 269 hostname, 270 domain); 271 if (ret) { 272 *minor_status = ret; 273 return GSS_S_FAILURE; 274 } 275 276 /* 277 * 278 */ 279 280 ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque); 281 if (ret) { 282 *minor_status = ret; 283 return GSS_S_FAILURE; 284 } 285 286 /* 287 * 288 */ 289 290 ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags); 291 if (ret) { 292 *minor_status = ret; 293 return GSS_S_FAILURE; 294 } 295 *ret_flags = type2.flags; 296 297 ret = krb5_ntlm_init_get_challenge(c->context, c->ntlm, &challenge); 298 if (ret) { 299 *minor_status = ret; 300 return GSS_S_FAILURE; 301 } 302 303 if (challenge.length != sizeof(type2.challenge)) { 304 *minor_status = HNTLM_ERR_INVALID_LENGTH; 305 return GSS_S_FAILURE; 306 } 307 memcpy(type2.challenge, challenge.data, sizeof(type2.challenge)); 308 krb5_data_free(&challenge); 309 310 ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm, 311 &type2.targetname); 312 if (ret) { 313 *minor_status = ret; 314 return GSS_S_FAILURE; 315 } 316 317 ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti); 318 if (ret) { 319 free(type2.targetname); 320 *minor_status = ret; 321 return GSS_S_FAILURE; 322 } 323 324 type2.targetinfo.data = ti.data; 325 type2.targetinfo.length = ti.length; 326 327 ret = heim_ntlm_encode_type2(&type2, &data); 328 free(type2.targetname); 329 krb5_data_free(&ti); 330 if (ret) { 331 *minor_status = ret; 332 return GSS_S_FAILURE; 333 } 334 335 out->data = data.data; 336 out->length = data.length; 337 338 return GSS_S_COMPLETE; 339} 340 341static OM_uint32 342kdc_ti(OM_uint32 *minor_status, 343 ntlm_ctx ntlmctx, 344 void *ctx, 345 const char *hostname, 346 const char *domain) 347{ 348 return GSS_S_FAILURE; 349} 350 351/* 352 * 353 */ 354 355static OM_uint32 356kdc_type3(OM_uint32 *minor_status, 357 void *ctx, 358 const struct ntlm_type3 *type3, 359 ntlm_cred accept_cred, 360 uint32_t *flags, 361 uint32_t *avflags, 362 struct ntlm_buf *sessionkey, 363 ntlm_name *name, struct ntlm_buf *uuid, 364 struct ntlm_buf *pac) 365{ 366 struct ntlmkrb5 *c = ctx; 367 krb5_error_code ret; 368 369 *avflags = *flags = 0; 370 371 sessionkey->data = NULL; 372 sessionkey->length = 0; 373 *name = NULL; 374 uuid->data = NULL; 375 uuid->length = 0; 376 pac->data = NULL; 377 pac->length = 0; 378 379 ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags); 380 if (ret) goto out; 381 ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username); 382 if (ret) goto out; 383 ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm, 384 type3->targetname); 385 if (ret) goto out; 386 ret = krb5_ntlm_req_set_lm(c->context, c->ntlm, 387 type3->lm.data, type3->lm.length); 388 if (ret) goto out; 389 ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm, 390 type3->ntlm.data, type3->ntlm.length); 391 if (ret) goto out; 392 ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque); 393 if (ret) goto out; 394 395 if (type3->sessionkey.length) { 396 ret = krb5_ntlm_req_set_session(c->context, c->ntlm, 397 type3->sessionkey.data, 398 type3->sessionkey.length); 399 if (ret) goto out; 400 } 401 402 /* 403 * Verify with the KDC the type3 packet is ok 404 */ 405 ret = krb5_ntlm_request(c->context, 406 c->ntlm, 407 NULL, 408 c->id); 409 if (ret) 410 goto out; 411 412 if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) { 413 ret = HNTLM_ERR_AUTH; 414 goto out; 415 } 416 417 if (type3->sessionkey.length) { 418 ret = krb5_ntlm_rep_get_sessionkey(c->context, 419 c->ntlm, 420 &c->sessionkey); 421 if (ret) 422 goto out; 423 424 sessionkey->data = c->sessionkey.data; 425 sessionkey->length = c->sessionkey.length; 426 } 427 428 return 0; 429 430 out: 431 *minor_status = ret; 432 return GSS_S_FAILURE; 433} 434 435/* 436 * 437 */ 438 439static void 440kdc_free_buffer(struct ntlm_buf *sessionkey) 441{ 442 if (sessionkey->data) 443 free(sessionkey->data); 444 sessionkey->data = NULL; 445 sessionkey->length = 0; 446} 447 448/* 449 * 450 */ 451 452struct ntlm_server_interface ntlmsspi_kdc_digest = { 453 "kdc" 454 kdc_alloc, 455 kdc_destroy, 456 kdc_probe, 457 kdc_type3, 458 kdc_free_buffer, 459 kdc_ti 460}; 461 462#endif /* DIGEST */ 463