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 42 struct 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 54 static 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 61 static krb5_error_code 62 get_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 135 out: 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 156 static OM_uint32 157 kdc_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 195 static int 196 kdc_probe(OM_uint32 *minor, void *ctx, const char *realm) 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 EINVAL; 208 209 return 0; 210 } 211 212 /* 213 * 214 */ 215 216 static OM_uint32 217 kdc_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 242 static OM_uint32 243 kdc_type2(OM_uint32 *minor_status, 244 void *ctx, 245 uint32_t flags, 246 const char *hostname, 247 const char *domain, 248 uint32_t *ret_flags, 249 struct ntlm_buf *out) 250 { 251 struct ntlmkrb5 *c = ctx; 252 krb5_error_code ret; 253 struct ntlm_type2 type2; 254 krb5_data challange; 255 struct ntlm_buf data; 256 krb5_data ti; 257 258 memset(&type2, 0, sizeof(type2)); 259 260 /* 261 * Request data for type 2 packet from the KDC. 262 */ 263 ret = krb5_ntlm_init_request(c->context, 264 c->ntlm, 265 NULL, 266 c->id, 267 flags, 268 hostname, 269 domain); 270 if (ret) { 271 *minor_status = ret; 272 return GSS_S_FAILURE; 273 } 274 275 /* 276 * 277 */ 278 279 ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque); 280 if (ret) { 281 *minor_status = ret; 282 return GSS_S_FAILURE; 283 } 284 285 /* 286 * 287 */ 288 289 ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags); 290 if (ret) { 291 *minor_status = ret; 292 return GSS_S_FAILURE; 293 } 294 *ret_flags = type2.flags; 295 296 ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange); 297 if (ret) { 298 *minor_status = ret; 299 return GSS_S_FAILURE; 300 } 301 302 if (challange.length != sizeof(type2.challenge)) { 303 *minor_status = EINVAL; 304 return GSS_S_FAILURE; 305 } 306 memcpy(type2.challenge, challange.data, sizeof(type2.challenge)); 307 krb5_data_free(&challange); 308 309 ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm, 310 &type2.targetname); 311 if (ret) { 312 *minor_status = ret; 313 return GSS_S_FAILURE; 314 } 315 316 ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti); 317 if (ret) { 318 free(type2.targetname); 319 *minor_status = ret; 320 return GSS_S_FAILURE; 321 } 322 323 type2.targetinfo.data = ti.data; 324 type2.targetinfo.length = ti.length; 325 326 ret = heim_ntlm_encode_type2(&type2, &data); 327 free(type2.targetname); 328 krb5_data_free(&ti); 329 if (ret) { 330 *minor_status = ret; 331 return GSS_S_FAILURE; 332 } 333 334 out->data = data.data; 335 out->length = data.length; 336 337 return GSS_S_COMPLETE; 338 } 339 340 /* 341 * 342 */ 343 344 static OM_uint32 345 kdc_type3(OM_uint32 *minor_status, 346 void *ctx, 347 const struct ntlm_type3 *type3, 348 struct ntlm_buf *sessionkey) 349 { 350 struct ntlmkrb5 *c = ctx; 351 krb5_error_code ret; 352 353 sessionkey->data = NULL; 354 sessionkey->length = 0; 355 356 ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags); 357 if (ret) goto out; 358 ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username); 359 if (ret) goto out; 360 ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm, 361 type3->targetname); 362 if (ret) goto out; 363 ret = krb5_ntlm_req_set_lm(c->context, c->ntlm, 364 type3->lm.data, type3->lm.length); 365 if (ret) goto out; 366 ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm, 367 type3->ntlm.data, type3->ntlm.length); 368 if (ret) goto out; 369 ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque); 370 if (ret) goto out; 371 372 if (type3->sessionkey.length) { 373 ret = krb5_ntlm_req_set_session(c->context, c->ntlm, 374 type3->sessionkey.data, 375 type3->sessionkey.length); 376 if (ret) goto out; 377 } 378 379 /* 380 * Verify with the KDC the type3 packet is ok 381 */ 382 ret = krb5_ntlm_request(c->context, 383 c->ntlm, 384 NULL, 385 c->id); 386 if (ret) 387 goto out; 388 389 if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) { 390 ret = EINVAL; 391 goto out; 392 } 393 394 if (type3->sessionkey.length) { 395 ret = krb5_ntlm_rep_get_sessionkey(c->context, 396 c->ntlm, 397 &c->sessionkey); 398 if (ret) 399 goto out; 400 401 sessionkey->data = c->sessionkey.data; 402 sessionkey->length = c->sessionkey.length; 403 } 404 405 return 0; 406 407 out: 408 *minor_status = ret; 409 return GSS_S_FAILURE; 410 } 411 412 /* 413 * 414 */ 415 416 static void 417 kdc_free_buffer(struct ntlm_buf *sessionkey) 418 { 419 if (sessionkey->data) 420 free(sessionkey->data); 421 sessionkey->data = NULL; 422 sessionkey->length = 0; 423 } 424 425 /* 426 * 427 */ 428 429 struct ntlm_server_interface ntlmsspi_kdc_digest = { 430 kdc_alloc, 431 kdc_destroy, 432 kdc_probe, 433 kdc_type2, 434 kdc_type3, 435 kdc_free_buffer 436 }; 437 438 #endif /* DIGEST */ 439