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/ntlm.h" 35 36RCSID("$Id: digest.c 22169 2007-12-04 22:19:16Z lha $"); 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, NULL, 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 (*destroy) 137 krb5_cc_destroy(context, *id); 138 else 139 krb5_cc_close(context, *id); 140 141 *id = NULL; 142 143 if (kt) 144 krb5_kt_close(context, kt); 145 146 if (principal) 147 krb5_free_principal(context, principal); 148 return ret; 149} 150 151/* 152 * 153 */ 154 155static OM_uint32 156kdc_alloc(OM_uint32 *minor, void **ctx) 157{ 158 krb5_error_code ret; 159 struct ntlmkrb5 *c; 160 OM_uint32 junk; 161 162 c = calloc(1, sizeof(*c)); 163 if (c == NULL) { 164 *minor = ENOMEM; 165 return GSS_S_FAILURE; 166 } 167 168 ret = krb5_init_context(&c->context); 169 if (ret) { 170 kdc_destroy(&junk, c); 171 *minor = ret; 172 return GSS_S_FAILURE; 173 } 174 175 ret = get_ccache(c->context, &c->destroy, &c->id); 176 if (ret) { 177 kdc_destroy(&junk, c); 178 *minor = ret; 179 return GSS_S_FAILURE; 180 } 181 182 ret = krb5_ntlm_alloc(c->context, &c->ntlm); 183 if (ret) { 184 kdc_destroy(&junk, c); 185 *minor = ret; 186 return GSS_S_FAILURE; 187 } 188 189 *ctx = c; 190 191 return GSS_S_COMPLETE; 192} 193 194static int 195kdc_probe(OM_uint32 *minor, void *ctx, const char *realm) 196{ 197 struct ntlmkrb5 *c = ctx; 198 krb5_error_code ret; 199 unsigned flags; 200 201 ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags); 202 if (ret) 203 return ret; 204 205 if ((flags & (1|2|4)) == 0) 206 return EINVAL; 207 208 return 0; 209} 210 211/* 212 * 213 */ 214 215static OM_uint32 216kdc_destroy(OM_uint32 *minor, void *ctx) 217{ 218 struct ntlmkrb5 *c = ctx; 219 krb5_data_free(&c->opaque); 220 krb5_data_free(&c->sessionkey); 221 if (c->ntlm) 222 krb5_ntlm_free(c->context, c->ntlm); 223 if (c->id) { 224 if (c->destroy) 225 krb5_cc_destroy(c->context, c->id); 226 else 227 krb5_cc_close(c->context, c->id); 228 } 229 if (c->context) 230 krb5_free_context(c->context); 231 memset(c, 0, sizeof(*c)); 232 free(c); 233 234 return GSS_S_COMPLETE; 235} 236 237/* 238 * 239 */ 240 241static OM_uint32 242kdc_type2(OM_uint32 *minor_status, 243 void *ctx, 244 uint32_t flags, 245 const char *hostname, 246 const char *domain, 247 uint32_t *ret_flags, 248 struct ntlm_buf *out) 249{ 250 struct ntlmkrb5 *c = ctx; 251 krb5_error_code ret; 252 struct ntlm_type2 type2; 253 krb5_data challange; 254 struct ntlm_buf data; 255 krb5_data ti; 256 257 memset(&type2, 0, sizeof(type2)); 258 259 /* 260 * Request data for type 2 packet from the KDC. 261 */ 262 ret = krb5_ntlm_init_request(c->context, 263 c->ntlm, 264 NULL, 265 c->id, 266 flags, 267 hostname, 268 domain); 269 if (ret) { 270 *minor_status = ret; 271 return GSS_S_FAILURE; 272 } 273 274 /* 275 * 276 */ 277 278 ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque); 279 if (ret) { 280 *minor_status = ret; 281 return GSS_S_FAILURE; 282 } 283 284 /* 285 * 286 */ 287 288 ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags); 289 if (ret) { 290 *minor_status = ret; 291 return GSS_S_FAILURE; 292 } 293 *ret_flags = type2.flags; 294 295 ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange); 296 if (ret) { 297 *minor_status = ret; 298 return GSS_S_FAILURE; 299 } 300 301 if (challange.length != sizeof(type2.challange)) { 302 *minor_status = EINVAL; 303 return GSS_S_FAILURE; 304 } 305 memcpy(type2.challange, challange.data, sizeof(type2.challange)); 306 krb5_data_free(&challange); 307 308 ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm, 309 &type2.targetname); 310 if (ret) { 311 *minor_status = ret; 312 return GSS_S_FAILURE; 313 } 314 315 ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti); 316 if (ret) { 317 free(type2.targetname); 318 *minor_status = ret; 319 return GSS_S_FAILURE; 320 } 321 322 type2.targetinfo.data = ti.data; 323 type2.targetinfo.length = ti.length; 324 325 ret = heim_ntlm_encode_type2(&type2, &data); 326 free(type2.targetname); 327 krb5_data_free(&ti); 328 if (ret) { 329 *minor_status = ret; 330 return GSS_S_FAILURE; 331 } 332 333 out->data = data.data; 334 out->length = data.length; 335 336 return GSS_S_COMPLETE; 337} 338 339/* 340 * 341 */ 342 343static OM_uint32 344kdc_type3(OM_uint32 *minor_status, 345 void *ctx, 346 const struct ntlm_type3 *type3, 347 struct ntlm_buf *sessionkey) 348{ 349 struct ntlmkrb5 *c = ctx; 350 krb5_error_code ret; 351 352 sessionkey->data = NULL; 353 sessionkey->length = 0; 354 355 ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags); 356 if (ret) goto out; 357 ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username); 358 if (ret) goto out; 359 ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm, 360 type3->targetname); 361 if (ret) goto out; 362 ret = krb5_ntlm_req_set_lm(c->context, c->ntlm, 363 type3->lm.data, type3->lm.length); 364 if (ret) goto out; 365 ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm, 366 type3->ntlm.data, type3->ntlm.length); 367 if (ret) goto out; 368 ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque); 369 if (ret) goto out; 370 371 if (type3->sessionkey.length) { 372 ret = krb5_ntlm_req_set_session(c->context, c->ntlm, 373 type3->sessionkey.data, 374 type3->sessionkey.length); 375 if (ret) goto out; 376 } 377 378 /* 379 * Verify with the KDC the type3 packet is ok 380 */ 381 ret = krb5_ntlm_request(c->context, 382 c->ntlm, 383 NULL, 384 c->id); 385 if (ret) 386 goto out; 387 388 if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) { 389 ret = EINVAL; 390 goto out; 391 } 392 393 if (type3->sessionkey.length) { 394 ret = krb5_ntlm_rep_get_sessionkey(c->context, 395 c->ntlm, 396 &c->sessionkey); 397 if (ret) 398 goto out; 399 400 sessionkey->data = c->sessionkey.data; 401 sessionkey->length = c->sessionkey.length; 402 } 403 404 return 0; 405 406 out: 407 *minor_status = ret; 408 return GSS_S_FAILURE; 409} 410 411/* 412 * 413 */ 414 415static void 416kdc_free_buffer(struct ntlm_buf *sessionkey) 417{ 418 if (sessionkey->data) 419 free(sessionkey->data); 420 sessionkey->data = NULL; 421 sessionkey->length = 0; 422} 423 424/* 425 * 426 */ 427 428struct ntlm_server_interface ntlmsspi_kdc_digest = { 429 kdc_alloc, 430 kdc_destroy, 431 kdc_probe, 432 kdc_type2, 433 kdc_type3, 434 kdc_free_buffer 435}; 436