1178825Sdfr/* 2178825Sdfr * Copyright (c) 2006 - 2007 Kungliga Tekniska H�gskolan 3178825Sdfr * (Royal Institute of Technology, Stockholm, Sweden). 4178825Sdfr * All rights reserved. 5178825Sdfr * 6178825Sdfr * Redistribution and use in source and binary forms, with or without 7178825Sdfr * modification, are permitted provided that the following conditions 8178825Sdfr * are met: 9178825Sdfr * 10178825Sdfr * 1. Redistributions of source code must retain the above copyright 11178825Sdfr * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13178825Sdfr * 2. Redistributions in binary form must reproduce the above copyright 14178825Sdfr * notice, this list of conditions and the following disclaimer in the 15178825Sdfr * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17178825Sdfr * 3. Neither the name of the Institute nor the names of its contributors 18178825Sdfr * may be used to endorse or promote products derived from this software 19178825Sdfr * without specific prior written permission. 20178825Sdfr * 21178825Sdfr * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22178825Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23178825Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24178825Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25178825Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26178825Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27178825Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28178825Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29178825Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30178825Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31178825Sdfr * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#include "ntlm/ntlm.h" 35178825Sdfr 36178825SdfrRCSID("$Id: digest.c 22169 2007-12-04 22:19:16Z lha $"); 37178825Sdfr 38178825Sdfr/* 39178825Sdfr * 40178825Sdfr */ 41178825Sdfr 42178825Sdfrstruct ntlmkrb5 { 43178825Sdfr krb5_context context; 44178825Sdfr krb5_ntlm ntlm; 45178825Sdfr krb5_realm kerberos_realm; 46178825Sdfr krb5_ccache id; 47178825Sdfr krb5_data opaque; 48178825Sdfr int destroy; 49178825Sdfr OM_uint32 flags; 50178825Sdfr struct ntlm_buf key; 51178825Sdfr krb5_data sessionkey; 52178825Sdfr}; 53178825Sdfr 54178825Sdfrstatic OM_uint32 kdc_destroy(OM_uint32 *, void *); 55178825Sdfr 56178825Sdfr/* 57178825Sdfr * Get credential cache that the ntlm code can use to talk to the KDC 58178825Sdfr * using the digest API. 59178825Sdfr */ 60178825Sdfr 61178825Sdfrstatic krb5_error_code 62178825Sdfrget_ccache(krb5_context context, int *destroy, krb5_ccache *id) 63178825Sdfr{ 64178825Sdfr krb5_principal principal = NULL; 65178825Sdfr krb5_error_code ret; 66178825Sdfr krb5_keytab kt = NULL; 67178825Sdfr 68178825Sdfr *id = NULL; 69178825Sdfr 70178825Sdfr if (!issuid()) { 71178825Sdfr const char *cache; 72178825Sdfr 73178825Sdfr cache = getenv("NTLM_ACCEPTOR_CCACHE"); 74178825Sdfr if (cache) { 75178825Sdfr ret = krb5_cc_resolve(context, cache, id); 76178825Sdfr if (ret) 77178825Sdfr goto out; 78178825Sdfr return 0; 79178825Sdfr } 80178825Sdfr } 81178825Sdfr 82178825Sdfr ret = krb5_sname_to_principal(context, NULL, "host", 83178825Sdfr KRB5_NT_SRV_HST, &principal); 84178825Sdfr if (ret) 85178825Sdfr goto out; 86178825Sdfr 87178825Sdfr ret = krb5_cc_cache_match(context, principal, NULL, id); 88178825Sdfr if (ret == 0) 89178825Sdfr return 0; 90178825Sdfr 91178825Sdfr /* did not find in default credcache, lets try default keytab */ 92178825Sdfr ret = krb5_kt_default(context, &kt); 93178825Sdfr if (ret) 94178825Sdfr goto out; 95178825Sdfr 96178825Sdfr /* XXX check in keytab */ 97178825Sdfr { 98178825Sdfr krb5_get_init_creds_opt *opt; 99178825Sdfr krb5_creds cred; 100178825Sdfr 101178825Sdfr memset(&cred, 0, sizeof(cred)); 102178825Sdfr 103178825Sdfr ret = krb5_cc_new_unique(context, "MEMORY", NULL, id); 104178825Sdfr if (ret) 105178825Sdfr goto out; 106178825Sdfr *destroy = 1; 107178825Sdfr ret = krb5_get_init_creds_opt_alloc(context, &opt); 108178825Sdfr if (ret) 109178825Sdfr goto out; 110178825Sdfr ret = krb5_get_init_creds_keytab (context, 111178825Sdfr &cred, 112178825Sdfr principal, 113178825Sdfr kt, 114178825Sdfr 0, 115178825Sdfr NULL, 116178825Sdfr opt); 117178825Sdfr krb5_get_init_creds_opt_free(context, opt); 118178825Sdfr if (ret) 119178825Sdfr goto out; 120178825Sdfr ret = krb5_cc_initialize (context, *id, cred.client); 121178825Sdfr if (ret) { 122178825Sdfr krb5_free_cred_contents (context, &cred); 123178825Sdfr goto out; 124178825Sdfr } 125178825Sdfr ret = krb5_cc_store_cred (context, *id, &cred); 126178825Sdfr krb5_free_cred_contents (context, &cred); 127178825Sdfr if (ret) 128178825Sdfr goto out; 129178825Sdfr } 130178825Sdfr 131178825Sdfr krb5_kt_close(context, kt); 132178825Sdfr 133178825Sdfr return 0; 134178825Sdfr 135178825Sdfrout: 136178825Sdfr if (*destroy) 137178825Sdfr krb5_cc_destroy(context, *id); 138178825Sdfr else 139178825Sdfr krb5_cc_close(context, *id); 140178825Sdfr 141178825Sdfr *id = NULL; 142178825Sdfr 143178825Sdfr if (kt) 144178825Sdfr krb5_kt_close(context, kt); 145178825Sdfr 146178825Sdfr if (principal) 147178825Sdfr krb5_free_principal(context, principal); 148178825Sdfr return ret; 149178825Sdfr} 150178825Sdfr 151178825Sdfr/* 152178825Sdfr * 153178825Sdfr */ 154178825Sdfr 155178825Sdfrstatic OM_uint32 156178825Sdfrkdc_alloc(OM_uint32 *minor, void **ctx) 157178825Sdfr{ 158178825Sdfr krb5_error_code ret; 159178825Sdfr struct ntlmkrb5 *c; 160178825Sdfr OM_uint32 junk; 161178825Sdfr 162178825Sdfr c = calloc(1, sizeof(*c)); 163178825Sdfr if (c == NULL) { 164178825Sdfr *minor = ENOMEM; 165178825Sdfr return GSS_S_FAILURE; 166178825Sdfr } 167178825Sdfr 168178825Sdfr ret = krb5_init_context(&c->context); 169178825Sdfr if (ret) { 170178825Sdfr kdc_destroy(&junk, c); 171178825Sdfr *minor = ret; 172178825Sdfr return GSS_S_FAILURE; 173178825Sdfr } 174178825Sdfr 175178825Sdfr ret = get_ccache(c->context, &c->destroy, &c->id); 176178825Sdfr if (ret) { 177178825Sdfr kdc_destroy(&junk, c); 178178825Sdfr *minor = ret; 179178825Sdfr return GSS_S_FAILURE; 180178825Sdfr } 181178825Sdfr 182178825Sdfr ret = krb5_ntlm_alloc(c->context, &c->ntlm); 183178825Sdfr if (ret) { 184178825Sdfr kdc_destroy(&junk, c); 185178825Sdfr *minor = ret; 186178825Sdfr return GSS_S_FAILURE; 187178825Sdfr } 188178825Sdfr 189178825Sdfr *ctx = c; 190178825Sdfr 191178825Sdfr return GSS_S_COMPLETE; 192178825Sdfr} 193178825Sdfr 194178825Sdfrstatic int 195178825Sdfrkdc_probe(OM_uint32 *minor, void *ctx, const char *realm) 196178825Sdfr{ 197178825Sdfr struct ntlmkrb5 *c = ctx; 198178825Sdfr krb5_error_code ret; 199178825Sdfr unsigned flags; 200178825Sdfr 201178825Sdfr ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags); 202178825Sdfr if (ret) 203178825Sdfr return ret; 204178825Sdfr 205178825Sdfr if ((flags & (1|2|4)) == 0) 206178825Sdfr return EINVAL; 207178825Sdfr 208178825Sdfr return 0; 209178825Sdfr} 210178825Sdfr 211178825Sdfr/* 212178825Sdfr * 213178825Sdfr */ 214178825Sdfr 215178825Sdfrstatic OM_uint32 216178825Sdfrkdc_destroy(OM_uint32 *minor, void *ctx) 217178825Sdfr{ 218178825Sdfr struct ntlmkrb5 *c = ctx; 219178825Sdfr krb5_data_free(&c->opaque); 220178825Sdfr krb5_data_free(&c->sessionkey); 221178825Sdfr if (c->ntlm) 222178825Sdfr krb5_ntlm_free(c->context, c->ntlm); 223178825Sdfr if (c->id) { 224178825Sdfr if (c->destroy) 225178825Sdfr krb5_cc_destroy(c->context, c->id); 226178825Sdfr else 227178825Sdfr krb5_cc_close(c->context, c->id); 228178825Sdfr } 229178825Sdfr if (c->context) 230178825Sdfr krb5_free_context(c->context); 231178825Sdfr memset(c, 0, sizeof(*c)); 232178825Sdfr free(c); 233178825Sdfr 234178825Sdfr return GSS_S_COMPLETE; 235178825Sdfr} 236178825Sdfr 237178825Sdfr/* 238178825Sdfr * 239178825Sdfr */ 240178825Sdfr 241178825Sdfrstatic OM_uint32 242178825Sdfrkdc_type2(OM_uint32 *minor_status, 243178825Sdfr void *ctx, 244178825Sdfr uint32_t flags, 245178825Sdfr const char *hostname, 246178825Sdfr const char *domain, 247178825Sdfr uint32_t *ret_flags, 248178825Sdfr struct ntlm_buf *out) 249178825Sdfr{ 250178825Sdfr struct ntlmkrb5 *c = ctx; 251178825Sdfr krb5_error_code ret; 252178825Sdfr struct ntlm_type2 type2; 253178825Sdfr krb5_data challange; 254178825Sdfr struct ntlm_buf data; 255178825Sdfr krb5_data ti; 256178825Sdfr 257178825Sdfr memset(&type2, 0, sizeof(type2)); 258178825Sdfr 259178825Sdfr /* 260178825Sdfr * Request data for type 2 packet from the KDC. 261178825Sdfr */ 262178825Sdfr ret = krb5_ntlm_init_request(c->context, 263178825Sdfr c->ntlm, 264178825Sdfr NULL, 265178825Sdfr c->id, 266178825Sdfr flags, 267178825Sdfr hostname, 268178825Sdfr domain); 269178825Sdfr if (ret) { 270178825Sdfr *minor_status = ret; 271178825Sdfr return GSS_S_FAILURE; 272178825Sdfr } 273178825Sdfr 274178825Sdfr /* 275178825Sdfr * 276178825Sdfr */ 277178825Sdfr 278178825Sdfr ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque); 279178825Sdfr if (ret) { 280178825Sdfr *minor_status = ret; 281178825Sdfr return GSS_S_FAILURE; 282178825Sdfr } 283178825Sdfr 284178825Sdfr /* 285178825Sdfr * 286178825Sdfr */ 287178825Sdfr 288178825Sdfr ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags); 289178825Sdfr if (ret) { 290178825Sdfr *minor_status = ret; 291178825Sdfr return GSS_S_FAILURE; 292178825Sdfr } 293178825Sdfr *ret_flags = type2.flags; 294178825Sdfr 295178825Sdfr ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange); 296178825Sdfr if (ret) { 297178825Sdfr *minor_status = ret; 298178825Sdfr return GSS_S_FAILURE; 299178825Sdfr } 300178825Sdfr 301178825Sdfr if (challange.length != sizeof(type2.challange)) { 302178825Sdfr *minor_status = EINVAL; 303178825Sdfr return GSS_S_FAILURE; 304178825Sdfr } 305178825Sdfr memcpy(type2.challange, challange.data, sizeof(type2.challange)); 306178825Sdfr krb5_data_free(&challange); 307178825Sdfr 308178825Sdfr ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm, 309178825Sdfr &type2.targetname); 310178825Sdfr if (ret) { 311178825Sdfr *minor_status = ret; 312178825Sdfr return GSS_S_FAILURE; 313178825Sdfr } 314178825Sdfr 315178825Sdfr ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti); 316178825Sdfr if (ret) { 317178825Sdfr free(type2.targetname); 318178825Sdfr *minor_status = ret; 319178825Sdfr return GSS_S_FAILURE; 320178825Sdfr } 321178825Sdfr 322178825Sdfr type2.targetinfo.data = ti.data; 323178825Sdfr type2.targetinfo.length = ti.length; 324178825Sdfr 325178825Sdfr ret = heim_ntlm_encode_type2(&type2, &data); 326178825Sdfr free(type2.targetname); 327178825Sdfr krb5_data_free(&ti); 328178825Sdfr if (ret) { 329178825Sdfr *minor_status = ret; 330178825Sdfr return GSS_S_FAILURE; 331178825Sdfr } 332178825Sdfr 333178825Sdfr out->data = data.data; 334178825Sdfr out->length = data.length; 335178825Sdfr 336178825Sdfr return GSS_S_COMPLETE; 337178825Sdfr} 338178825Sdfr 339178825Sdfr/* 340178825Sdfr * 341178825Sdfr */ 342178825Sdfr 343178825Sdfrstatic OM_uint32 344178825Sdfrkdc_type3(OM_uint32 *minor_status, 345178825Sdfr void *ctx, 346178825Sdfr const struct ntlm_type3 *type3, 347178825Sdfr struct ntlm_buf *sessionkey) 348178825Sdfr{ 349178825Sdfr struct ntlmkrb5 *c = ctx; 350178825Sdfr krb5_error_code ret; 351178825Sdfr 352178825Sdfr sessionkey->data = NULL; 353178825Sdfr sessionkey->length = 0; 354178825Sdfr 355178825Sdfr ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags); 356178825Sdfr if (ret) goto out; 357178825Sdfr ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username); 358178825Sdfr if (ret) goto out; 359178825Sdfr ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm, 360178825Sdfr type3->targetname); 361178825Sdfr if (ret) goto out; 362178825Sdfr ret = krb5_ntlm_req_set_lm(c->context, c->ntlm, 363178825Sdfr type3->lm.data, type3->lm.length); 364178825Sdfr if (ret) goto out; 365178825Sdfr ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm, 366178825Sdfr type3->ntlm.data, type3->ntlm.length); 367178825Sdfr if (ret) goto out; 368178825Sdfr ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque); 369178825Sdfr if (ret) goto out; 370178825Sdfr 371178825Sdfr if (type3->sessionkey.length) { 372178825Sdfr ret = krb5_ntlm_req_set_session(c->context, c->ntlm, 373178825Sdfr type3->sessionkey.data, 374178825Sdfr type3->sessionkey.length); 375178825Sdfr if (ret) goto out; 376178825Sdfr } 377178825Sdfr 378178825Sdfr /* 379178825Sdfr * Verify with the KDC the type3 packet is ok 380178825Sdfr */ 381178825Sdfr ret = krb5_ntlm_request(c->context, 382178825Sdfr c->ntlm, 383178825Sdfr NULL, 384178825Sdfr c->id); 385178825Sdfr if (ret) 386178825Sdfr goto out; 387178825Sdfr 388178825Sdfr if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) { 389178825Sdfr ret = EINVAL; 390178825Sdfr goto out; 391178825Sdfr } 392178825Sdfr 393178825Sdfr if (type3->sessionkey.length) { 394178825Sdfr ret = krb5_ntlm_rep_get_sessionkey(c->context, 395178825Sdfr c->ntlm, 396178825Sdfr &c->sessionkey); 397178825Sdfr if (ret) 398178825Sdfr goto out; 399178825Sdfr 400178825Sdfr sessionkey->data = c->sessionkey.data; 401178825Sdfr sessionkey->length = c->sessionkey.length; 402178825Sdfr } 403178825Sdfr 404178825Sdfr return 0; 405178825Sdfr 406178825Sdfr out: 407178825Sdfr *minor_status = ret; 408178825Sdfr return GSS_S_FAILURE; 409178825Sdfr} 410178825Sdfr 411178825Sdfr/* 412178825Sdfr * 413178825Sdfr */ 414178825Sdfr 415178825Sdfrstatic void 416178825Sdfrkdc_free_buffer(struct ntlm_buf *sessionkey) 417178825Sdfr{ 418178825Sdfr if (sessionkey->data) 419178825Sdfr free(sessionkey->data); 420178825Sdfr sessionkey->data = NULL; 421178825Sdfr sessionkey->length = 0; 422178825Sdfr} 423178825Sdfr 424178825Sdfr/* 425178825Sdfr * 426178825Sdfr */ 427178825Sdfr 428178825Sdfrstruct ntlm_server_interface ntlmsspi_kdc_digest = { 429178825Sdfr kdc_alloc, 430178825Sdfr kdc_destroy, 431178825Sdfr kdc_probe, 432178825Sdfr kdc_type2, 433178825Sdfr kdc_type3, 434178825Sdfr kdc_free_buffer 435178825Sdfr}; 436