1226031Sstas/* 2226031Sstas * Copyright (c) 2006 - 2007 Kungliga Tekniska H��gskolan 3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4226031Sstas * All rights reserved. 5226031Sstas * 6226031Sstas * Redistribution and use in source and binary forms, with or without 7226031Sstas * modification, are permitted provided that the following conditions 8226031Sstas * are met: 9226031Sstas * 10226031Sstas * 1. Redistributions of source code must retain the above copyright 11226031Sstas * notice, this list of conditions and the following disclaimer. 12226031Sstas * 13226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 14226031Sstas * notice, this list of conditions and the following disclaimer in the 15226031Sstas * documentation and/or other materials provided with the distribution. 16226031Sstas * 17226031Sstas * 3. Neither the name of the Institute nor the names of its contributors 18226031Sstas * may be used to endorse or promote products derived from this software 19226031Sstas * without specific prior written permission. 20226031Sstas * 21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31226031Sstas * SUCH DAMAGE. 32226031Sstas */ 33226031Sstas 34226031Sstas#include "ntlm.h" 35226031Sstas 36226031Sstas#ifdef DIGEST 37226031Sstas 38226031Sstas/* 39226031Sstas * 40226031Sstas */ 41226031Sstas 42226031Sstasstruct ntlmkrb5 { 43226031Sstas krb5_context context; 44226031Sstas krb5_ntlm ntlm; 45226031Sstas krb5_realm kerberos_realm; 46226031Sstas krb5_ccache id; 47226031Sstas krb5_data opaque; 48226031Sstas int destroy; 49226031Sstas OM_uint32 flags; 50226031Sstas struct ntlm_buf key; 51226031Sstas krb5_data sessionkey; 52226031Sstas}; 53226031Sstas 54226031Sstasstatic OM_uint32 kdc_destroy(OM_uint32 *, void *); 55226031Sstas 56226031Sstas/* 57226031Sstas * Get credential cache that the ntlm code can use to talk to the KDC 58226031Sstas * using the digest API. 59226031Sstas */ 60226031Sstas 61226031Sstasstatic krb5_error_code 62226031Sstasget_ccache(krb5_context context, int *destroy, krb5_ccache *id) 63226031Sstas{ 64226031Sstas krb5_principal principal = NULL; 65226031Sstas krb5_error_code ret; 66226031Sstas krb5_keytab kt = NULL; 67226031Sstas 68226031Sstas *id = NULL; 69226031Sstas 70226031Sstas if (!issuid()) { 71226031Sstas const char *cache; 72226031Sstas 73226031Sstas cache = getenv("NTLM_ACCEPTOR_CCACHE"); 74226031Sstas if (cache) { 75226031Sstas ret = krb5_cc_resolve(context, cache, id); 76226031Sstas if (ret) 77226031Sstas goto out; 78226031Sstas return 0; 79226031Sstas } 80226031Sstas } 81226031Sstas 82226031Sstas ret = krb5_sname_to_principal(context, NULL, "host", 83226031Sstas KRB5_NT_SRV_HST, &principal); 84226031Sstas if (ret) 85226031Sstas goto out; 86226031Sstas 87226031Sstas ret = krb5_cc_cache_match(context, principal, id); 88226031Sstas if (ret == 0) 89226031Sstas return 0; 90226031Sstas 91226031Sstas /* did not find in default credcache, lets try default keytab */ 92226031Sstas ret = krb5_kt_default(context, &kt); 93226031Sstas if (ret) 94226031Sstas goto out; 95226031Sstas 96226031Sstas /* XXX check in keytab */ 97226031Sstas { 98226031Sstas krb5_get_init_creds_opt *opt; 99226031Sstas krb5_creds cred; 100226031Sstas 101226031Sstas memset(&cred, 0, sizeof(cred)); 102226031Sstas 103226031Sstas ret = krb5_cc_new_unique(context, "MEMORY", NULL, id); 104226031Sstas if (ret) 105226031Sstas goto out; 106226031Sstas *destroy = 1; 107226031Sstas ret = krb5_get_init_creds_opt_alloc(context, &opt); 108226031Sstas if (ret) 109226031Sstas goto out; 110226031Sstas ret = krb5_get_init_creds_keytab (context, 111226031Sstas &cred, 112226031Sstas principal, 113226031Sstas kt, 114226031Sstas 0, 115226031Sstas NULL, 116226031Sstas opt); 117226031Sstas krb5_get_init_creds_opt_free(context, opt); 118226031Sstas if (ret) 119226031Sstas goto out; 120226031Sstas ret = krb5_cc_initialize (context, *id, cred.client); 121226031Sstas if (ret) { 122226031Sstas krb5_free_cred_contents (context, &cred); 123226031Sstas goto out; 124226031Sstas } 125226031Sstas ret = krb5_cc_store_cred (context, *id, &cred); 126226031Sstas krb5_free_cred_contents (context, &cred); 127226031Sstas if (ret) 128226031Sstas goto out; 129226031Sstas } 130226031Sstas 131226031Sstas krb5_kt_close(context, kt); 132226031Sstas 133226031Sstas return 0; 134226031Sstas 135226031Sstasout: 136226031Sstas if (*id) { 137226031Sstas if (*destroy) 138226031Sstas krb5_cc_destroy(context, *id); 139226031Sstas else 140226031Sstas krb5_cc_close(context, *id); 141226031Sstas *id = NULL; 142226031Sstas } 143226031Sstas 144226031Sstas if (kt) 145226031Sstas krb5_kt_close(context, kt); 146226031Sstas 147226031Sstas if (principal) 148226031Sstas krb5_free_principal(context, principal); 149226031Sstas return ret; 150226031Sstas} 151226031Sstas 152226031Sstas/* 153226031Sstas * 154226031Sstas */ 155226031Sstas 156226031Sstasstatic OM_uint32 157226031Sstaskdc_alloc(OM_uint32 *minor, void **ctx) 158226031Sstas{ 159226031Sstas krb5_error_code ret; 160226031Sstas struct ntlmkrb5 *c; 161226031Sstas OM_uint32 junk; 162226031Sstas 163226031Sstas c = calloc(1, sizeof(*c)); 164226031Sstas if (c == NULL) { 165226031Sstas *minor = ENOMEM; 166226031Sstas return GSS_S_FAILURE; 167226031Sstas } 168226031Sstas 169226031Sstas ret = krb5_init_context(&c->context); 170226031Sstas if (ret) { 171226031Sstas kdc_destroy(&junk, c); 172226031Sstas *minor = ret; 173226031Sstas return GSS_S_FAILURE; 174226031Sstas } 175226031Sstas 176226031Sstas ret = get_ccache(c->context, &c->destroy, &c->id); 177226031Sstas if (ret) { 178226031Sstas kdc_destroy(&junk, c); 179226031Sstas *minor = ret; 180226031Sstas return GSS_S_FAILURE; 181226031Sstas } 182226031Sstas 183226031Sstas ret = krb5_ntlm_alloc(c->context, &c->ntlm); 184226031Sstas if (ret) { 185226031Sstas kdc_destroy(&junk, c); 186226031Sstas *minor = ret; 187226031Sstas return GSS_S_FAILURE; 188226031Sstas } 189226031Sstas 190226031Sstas *ctx = c; 191226031Sstas 192226031Sstas return GSS_S_COMPLETE; 193226031Sstas} 194226031Sstas 195226031Sstasstatic int 196226031Sstaskdc_probe(OM_uint32 *minor, void *ctx, const char *realm) 197226031Sstas{ 198226031Sstas struct ntlmkrb5 *c = ctx; 199226031Sstas krb5_error_code ret; 200226031Sstas unsigned flags; 201226031Sstas 202226031Sstas ret = krb5_digest_probe(c->context, rk_UNCONST(realm), c->id, &flags); 203226031Sstas if (ret) 204226031Sstas return ret; 205226031Sstas 206226031Sstas if ((flags & (1|2|4)) == 0) 207226031Sstas return EINVAL; 208226031Sstas 209226031Sstas return 0; 210226031Sstas} 211226031Sstas 212226031Sstas/* 213226031Sstas * 214226031Sstas */ 215226031Sstas 216226031Sstasstatic OM_uint32 217226031Sstaskdc_destroy(OM_uint32 *minor, void *ctx) 218226031Sstas{ 219226031Sstas struct ntlmkrb5 *c = ctx; 220226031Sstas krb5_data_free(&c->opaque); 221226031Sstas krb5_data_free(&c->sessionkey); 222226031Sstas if (c->ntlm) 223226031Sstas krb5_ntlm_free(c->context, c->ntlm); 224226031Sstas if (c->id) { 225226031Sstas if (c->destroy) 226226031Sstas krb5_cc_destroy(c->context, c->id); 227226031Sstas else 228226031Sstas krb5_cc_close(c->context, c->id); 229226031Sstas } 230226031Sstas if (c->context) 231226031Sstas krb5_free_context(c->context); 232226031Sstas memset(c, 0, sizeof(*c)); 233226031Sstas free(c); 234226031Sstas 235226031Sstas return GSS_S_COMPLETE; 236226031Sstas} 237226031Sstas 238226031Sstas/* 239226031Sstas * 240226031Sstas */ 241226031Sstas 242226031Sstasstatic OM_uint32 243226031Sstaskdc_type2(OM_uint32 *minor_status, 244226031Sstas void *ctx, 245226031Sstas uint32_t flags, 246226031Sstas const char *hostname, 247226031Sstas const char *domain, 248226031Sstas uint32_t *ret_flags, 249226031Sstas struct ntlm_buf *out) 250226031Sstas{ 251226031Sstas struct ntlmkrb5 *c = ctx; 252226031Sstas krb5_error_code ret; 253226031Sstas struct ntlm_type2 type2; 254226031Sstas krb5_data challange; 255226031Sstas struct ntlm_buf data; 256226031Sstas krb5_data ti; 257226031Sstas 258226031Sstas memset(&type2, 0, sizeof(type2)); 259226031Sstas 260226031Sstas /* 261226031Sstas * Request data for type 2 packet from the KDC. 262226031Sstas */ 263226031Sstas ret = krb5_ntlm_init_request(c->context, 264226031Sstas c->ntlm, 265226031Sstas NULL, 266226031Sstas c->id, 267226031Sstas flags, 268226031Sstas hostname, 269226031Sstas domain); 270226031Sstas if (ret) { 271226031Sstas *minor_status = ret; 272226031Sstas return GSS_S_FAILURE; 273226031Sstas } 274226031Sstas 275226031Sstas /* 276226031Sstas * 277226031Sstas */ 278226031Sstas 279226031Sstas ret = krb5_ntlm_init_get_opaque(c->context, c->ntlm, &c->opaque); 280226031Sstas if (ret) { 281226031Sstas *minor_status = ret; 282226031Sstas return GSS_S_FAILURE; 283226031Sstas } 284226031Sstas 285226031Sstas /* 286226031Sstas * 287226031Sstas */ 288226031Sstas 289226031Sstas ret = krb5_ntlm_init_get_flags(c->context, c->ntlm, &type2.flags); 290226031Sstas if (ret) { 291226031Sstas *minor_status = ret; 292226031Sstas return GSS_S_FAILURE; 293226031Sstas } 294226031Sstas *ret_flags = type2.flags; 295226031Sstas 296226031Sstas ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange); 297226031Sstas if (ret) { 298226031Sstas *minor_status = ret; 299226031Sstas return GSS_S_FAILURE; 300226031Sstas } 301226031Sstas 302226031Sstas if (challange.length != sizeof(type2.challenge)) { 303226031Sstas *minor_status = EINVAL; 304226031Sstas return GSS_S_FAILURE; 305226031Sstas } 306226031Sstas memcpy(type2.challenge, challange.data, sizeof(type2.challenge)); 307226031Sstas krb5_data_free(&challange); 308226031Sstas 309226031Sstas ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm, 310226031Sstas &type2.targetname); 311226031Sstas if (ret) { 312226031Sstas *minor_status = ret; 313226031Sstas return GSS_S_FAILURE; 314226031Sstas } 315226031Sstas 316226031Sstas ret = krb5_ntlm_init_get_targetinfo(c->context, c->ntlm, &ti); 317226031Sstas if (ret) { 318226031Sstas free(type2.targetname); 319226031Sstas *minor_status = ret; 320226031Sstas return GSS_S_FAILURE; 321226031Sstas } 322226031Sstas 323226031Sstas type2.targetinfo.data = ti.data; 324226031Sstas type2.targetinfo.length = ti.length; 325226031Sstas 326226031Sstas ret = heim_ntlm_encode_type2(&type2, &data); 327226031Sstas free(type2.targetname); 328226031Sstas krb5_data_free(&ti); 329226031Sstas if (ret) { 330226031Sstas *minor_status = ret; 331226031Sstas return GSS_S_FAILURE; 332226031Sstas } 333226031Sstas 334226031Sstas out->data = data.data; 335226031Sstas out->length = data.length; 336226031Sstas 337226031Sstas return GSS_S_COMPLETE; 338226031Sstas} 339226031Sstas 340226031Sstas/* 341226031Sstas * 342226031Sstas */ 343226031Sstas 344226031Sstasstatic OM_uint32 345226031Sstaskdc_type3(OM_uint32 *minor_status, 346226031Sstas void *ctx, 347226031Sstas const struct ntlm_type3 *type3, 348226031Sstas struct ntlm_buf *sessionkey) 349226031Sstas{ 350226031Sstas struct ntlmkrb5 *c = ctx; 351226031Sstas krb5_error_code ret; 352226031Sstas 353226031Sstas sessionkey->data = NULL; 354226031Sstas sessionkey->length = 0; 355226031Sstas 356226031Sstas ret = krb5_ntlm_req_set_flags(c->context, c->ntlm, type3->flags); 357226031Sstas if (ret) goto out; 358226031Sstas ret = krb5_ntlm_req_set_username(c->context, c->ntlm, type3->username); 359226031Sstas if (ret) goto out; 360226031Sstas ret = krb5_ntlm_req_set_targetname(c->context, c->ntlm, 361226031Sstas type3->targetname); 362226031Sstas if (ret) goto out; 363226031Sstas ret = krb5_ntlm_req_set_lm(c->context, c->ntlm, 364226031Sstas type3->lm.data, type3->lm.length); 365226031Sstas if (ret) goto out; 366226031Sstas ret = krb5_ntlm_req_set_ntlm(c->context, c->ntlm, 367226031Sstas type3->ntlm.data, type3->ntlm.length); 368226031Sstas if (ret) goto out; 369226031Sstas ret = krb5_ntlm_req_set_opaque(c->context, c->ntlm, &c->opaque); 370226031Sstas if (ret) goto out; 371226031Sstas 372226031Sstas if (type3->sessionkey.length) { 373226031Sstas ret = krb5_ntlm_req_set_session(c->context, c->ntlm, 374226031Sstas type3->sessionkey.data, 375226031Sstas type3->sessionkey.length); 376226031Sstas if (ret) goto out; 377226031Sstas } 378226031Sstas 379226031Sstas /* 380226031Sstas * Verify with the KDC the type3 packet is ok 381226031Sstas */ 382226031Sstas ret = krb5_ntlm_request(c->context, 383226031Sstas c->ntlm, 384226031Sstas NULL, 385226031Sstas c->id); 386226031Sstas if (ret) 387226031Sstas goto out; 388226031Sstas 389226031Sstas if (krb5_ntlm_rep_get_status(c->context, c->ntlm) != TRUE) { 390226031Sstas ret = EINVAL; 391226031Sstas goto out; 392226031Sstas } 393226031Sstas 394226031Sstas if (type3->sessionkey.length) { 395226031Sstas ret = krb5_ntlm_rep_get_sessionkey(c->context, 396226031Sstas c->ntlm, 397226031Sstas &c->sessionkey); 398226031Sstas if (ret) 399226031Sstas goto out; 400226031Sstas 401226031Sstas sessionkey->data = c->sessionkey.data; 402226031Sstas sessionkey->length = c->sessionkey.length; 403226031Sstas } 404226031Sstas 405226031Sstas return 0; 406226031Sstas 407226031Sstas out: 408226031Sstas *minor_status = ret; 409226031Sstas return GSS_S_FAILURE; 410226031Sstas} 411226031Sstas 412226031Sstas/* 413226031Sstas * 414226031Sstas */ 415226031Sstas 416226031Sstasstatic void 417226031Sstaskdc_free_buffer(struct ntlm_buf *sessionkey) 418226031Sstas{ 419226031Sstas if (sessionkey->data) 420226031Sstas free(sessionkey->data); 421226031Sstas sessionkey->data = NULL; 422226031Sstas sessionkey->length = 0; 423226031Sstas} 424226031Sstas 425226031Sstas/* 426226031Sstas * 427226031Sstas */ 428226031Sstas 429226031Sstasstruct ntlm_server_interface ntlmsspi_kdc_digest = { 430226031Sstas kdc_alloc, 431226031Sstas kdc_destroy, 432226031Sstas kdc_probe, 433226031Sstas kdc_type2, 434226031Sstas kdc_type3, 435226031Sstas kdc_free_buffer 436226031Sstas}; 437226031Sstas 438226031Sstas#endif /* DIGEST */ 439