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