1178825Sdfr/* 2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 5178825Sdfr * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 9178825Sdfr * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 20178825Sdfr * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34233294Sstas#include "gsskrb5_locl.h" 35178825Sdfr 36178825SdfrOM_uint32 37178825Sdfr__gsskrb5_ccache_lifetime(OM_uint32 *minor_status, 38178825Sdfr krb5_context context, 39178825Sdfr krb5_ccache id, 40178825Sdfr krb5_principal principal, 41178825Sdfr OM_uint32 *lifetime) 42178825Sdfr{ 43233294Sstas krb5_creds in_cred, out_cred; 44178825Sdfr krb5_const_realm realm; 45178825Sdfr krb5_error_code kret; 46178825Sdfr 47178825Sdfr memset(&in_cred, 0, sizeof(in_cred)); 48178825Sdfr in_cred.client = principal; 49233294Sstas 50178825Sdfr realm = krb5_principal_get_realm(context, principal); 51178825Sdfr if (realm == NULL) { 52178825Sdfr _gsskrb5_clear_status (); 53178825Sdfr *minor_status = KRB5_PRINC_NOMATCH; /* XXX */ 54178825Sdfr return GSS_S_FAILURE; 55178825Sdfr } 56178825Sdfr 57233294Sstas kret = krb5_make_principal(context, &in_cred.server, 58178825Sdfr realm, KRB5_TGS_NAME, realm, NULL); 59178825Sdfr if (kret) { 60178825Sdfr *minor_status = kret; 61178825Sdfr return GSS_S_FAILURE; 62178825Sdfr } 63178825Sdfr 64233294Sstas kret = krb5_cc_retrieve_cred(context, id, 0, &in_cred, &out_cred); 65178825Sdfr krb5_free_principal(context, in_cred.server); 66178825Sdfr if (kret) { 67233294Sstas *minor_status = 0; 68233294Sstas *lifetime = 0; 69233294Sstas return GSS_S_COMPLETE; 70178825Sdfr } 71178825Sdfr 72233294Sstas *lifetime = out_cred.times.endtime; 73233294Sstas krb5_free_cred_contents(context, &out_cred); 74178825Sdfr 75178825Sdfr return GSS_S_COMPLETE; 76178825Sdfr} 77178825Sdfr 78178825Sdfr 79178825Sdfr 80178825Sdfr 81178825Sdfrstatic krb5_error_code 82178825Sdfrget_keytab(krb5_context context, krb5_keytab *keytab) 83178825Sdfr{ 84178825Sdfr krb5_error_code kret; 85178825Sdfr 86178825Sdfr HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex); 87178825Sdfr 88178825Sdfr if (_gsskrb5_keytab != NULL) { 89233294Sstas char *name = NULL; 90233294Sstas 91233294Sstas kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name); 92233294Sstas if (kret == 0) { 93233294Sstas kret = krb5_kt_resolve(context, name, keytab); 94233294Sstas krb5_xfree(name); 95233294Sstas } 96178825Sdfr } else 97178825Sdfr kret = krb5_kt_default(context, keytab); 98178825Sdfr 99178825Sdfr HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); 100178825Sdfr 101178825Sdfr return (kret); 102178825Sdfr} 103178825Sdfr 104178825Sdfrstatic OM_uint32 acquire_initiator_cred 105178825Sdfr (OM_uint32 * minor_status, 106178825Sdfr krb5_context context, 107233294Sstas gss_const_OID credential_type, 108233294Sstas const void *credential_data, 109178825Sdfr const gss_name_t desired_name, 110178825Sdfr OM_uint32 time_req, 111233294Sstas gss_const_OID desired_mech, 112178825Sdfr gss_cred_usage_t cred_usage, 113233294Sstas gsskrb5_cred handle 114178825Sdfr ) 115178825Sdfr{ 116178825Sdfr OM_uint32 ret; 117178825Sdfr krb5_creds cred; 118178825Sdfr krb5_principal def_princ; 119178825Sdfr krb5_get_init_creds_opt *opt; 120178825Sdfr krb5_ccache ccache; 121178825Sdfr krb5_keytab keytab; 122178825Sdfr krb5_error_code kret; 123178825Sdfr 124178825Sdfr keytab = NULL; 125178825Sdfr ccache = NULL; 126178825Sdfr def_princ = NULL; 127178825Sdfr ret = GSS_S_FAILURE; 128178825Sdfr memset(&cred, 0, sizeof(cred)); 129178825Sdfr 130233294Sstas /* 131233294Sstas * If we have a preferred principal, lets try to find it in all 132233294Sstas * caches, otherwise, fall back to default cache, ignore all 133233294Sstas * errors while searching. 134233294Sstas */ 135233294Sstas 136233294Sstas if (credential_type != GSS_C_NO_OID && 137233294Sstas !gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { 138233294Sstas kret = KRB5_NOCREDS_SUPPLIED; /* XXX */ 139233294Sstas goto end; 140233294Sstas } 141233294Sstas 142233294Sstas if (handle->principal) { 143178825Sdfr kret = krb5_cc_cache_match (context, 144178825Sdfr handle->principal, 145178825Sdfr &ccache); 146233294Sstas if (kret == 0) { 147233294Sstas ret = GSS_S_COMPLETE; 148233294Sstas goto found; 149233294Sstas } 150233294Sstas } 151233294Sstas 152178825Sdfr if (ccache == NULL) { 153178825Sdfr kret = krb5_cc_default(context, &ccache); 154178825Sdfr if (kret) 155178825Sdfr goto end; 156178825Sdfr } 157233294Sstas kret = krb5_cc_get_principal(context, ccache, &def_princ); 158178825Sdfr if (kret != 0) { 159178825Sdfr /* we'll try to use a keytab below */ 160233294Sstas krb5_cc_close(context, ccache); 161233294Sstas def_princ = NULL; 162178825Sdfr kret = 0; 163178825Sdfr } else if (handle->principal == NULL) { 164233294Sstas kret = krb5_copy_principal(context, def_princ, &handle->principal); 165178825Sdfr if (kret) 166178825Sdfr goto end; 167178825Sdfr } else if (handle->principal != NULL && 168233294Sstas krb5_principal_compare(context, handle->principal, 169233294Sstas def_princ) == FALSE) { 170178825Sdfr krb5_free_principal(context, def_princ); 171178825Sdfr def_princ = NULL; 172233294Sstas krb5_cc_close(context, ccache); 173233294Sstas ccache = NULL; 174178825Sdfr } 175178825Sdfr if (def_princ == NULL) { 176178825Sdfr /* We have no existing credentials cache, 177178825Sdfr * so attempt to get a TGT using a keytab. 178178825Sdfr */ 179178825Sdfr if (handle->principal == NULL) { 180233294Sstas kret = krb5_get_default_principal(context, &handle->principal); 181178825Sdfr if (kret) 182178825Sdfr goto end; 183178825Sdfr } 184178825Sdfr kret = krb5_get_init_creds_opt_alloc(context, &opt); 185178825Sdfr if (kret) 186178825Sdfr goto end; 187233294Sstas if (credential_type != GSS_C_NO_OID && 188233294Sstas gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { 189233294Sstas gss_buffer_t password = (gss_buffer_t)credential_data; 190233294Sstas 191233294Sstas /* XXX are we requiring password to be NUL terminated? */ 192233294Sstas 193233294Sstas kret = krb5_get_init_creds_password(context, &cred, 194233294Sstas handle->principal, 195233294Sstas password->value, 196233294Sstas NULL, NULL, 0, NULL, opt); 197233294Sstas } else { 198233294Sstas kret = get_keytab(context, &keytab); 199233294Sstas if (kret) { 200233294Sstas krb5_get_init_creds_opt_free(context, opt); 201233294Sstas goto end; 202233294Sstas } 203233294Sstas kret = krb5_get_init_creds_keytab(context, &cred, 204233294Sstas handle->principal, keytab, 205233294Sstas 0, NULL, opt); 206233294Sstas } 207178825Sdfr krb5_get_init_creds_opt_free(context, opt); 208178825Sdfr if (kret) 209178825Sdfr goto end; 210233294Sstas kret = krb5_cc_new_unique(context, krb5_cc_type_memory, 211233294Sstas NULL, &ccache); 212178825Sdfr if (kret) 213178825Sdfr goto end; 214178825Sdfr kret = krb5_cc_initialize(context, ccache, cred.client); 215233294Sstas if (kret) { 216233294Sstas krb5_cc_destroy(context, ccache); 217178825Sdfr goto end; 218233294Sstas } 219178825Sdfr kret = krb5_cc_store_cred(context, ccache, &cred); 220233294Sstas if (kret) { 221233294Sstas krb5_cc_destroy(context, ccache); 222178825Sdfr goto end; 223233294Sstas } 224178825Sdfr handle->lifetime = cred.times.endtime; 225178825Sdfr handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 226178825Sdfr } else { 227178825Sdfr 228178825Sdfr ret = __gsskrb5_ccache_lifetime(minor_status, 229178825Sdfr context, 230178825Sdfr ccache, 231178825Sdfr handle->principal, 232178825Sdfr &handle->lifetime); 233233294Sstas if (ret != GSS_S_COMPLETE) { 234233294Sstas krb5_cc_close(context, ccache); 235178825Sdfr goto end; 236233294Sstas } 237178825Sdfr kret = 0; 238178825Sdfr } 239233294Sstas found: 240178825Sdfr handle->ccache = ccache; 241178825Sdfr ret = GSS_S_COMPLETE; 242178825Sdfr 243178825Sdfrend: 244178825Sdfr if (cred.client != NULL) 245178825Sdfr krb5_free_cred_contents(context, &cred); 246178825Sdfr if (def_princ != NULL) 247178825Sdfr krb5_free_principal(context, def_princ); 248178825Sdfr if (keytab != NULL) 249178825Sdfr krb5_kt_close(context, keytab); 250233294Sstas if (ret != GSS_S_COMPLETE && kret != 0) 251233294Sstas *minor_status = kret; 252178825Sdfr return (ret); 253178825Sdfr} 254178825Sdfr 255178825Sdfrstatic OM_uint32 acquire_acceptor_cred 256178825Sdfr (OM_uint32 * minor_status, 257178825Sdfr krb5_context context, 258233294Sstas gss_const_OID credential_type, 259233294Sstas const void *credential_data, 260178825Sdfr const gss_name_t desired_name, 261178825Sdfr OM_uint32 time_req, 262233294Sstas gss_const_OID desired_mech, 263178825Sdfr gss_cred_usage_t cred_usage, 264233294Sstas gsskrb5_cred handle 265178825Sdfr ) 266178825Sdfr{ 267178825Sdfr OM_uint32 ret; 268178825Sdfr krb5_error_code kret; 269178825Sdfr 270178825Sdfr ret = GSS_S_FAILURE; 271233294Sstas 272233294Sstas if (credential_type != GSS_C_NO_OID) { 273233294Sstas kret = EINVAL; 274233294Sstas goto end; 275233294Sstas } 276233294Sstas 277178825Sdfr kret = get_keytab(context, &handle->keytab); 278178825Sdfr if (kret) 279178825Sdfr goto end; 280233294Sstas 281178825Sdfr /* check that the requested principal exists in the keytab */ 282178825Sdfr if (handle->principal) { 283178825Sdfr krb5_keytab_entry entry; 284178825Sdfr 285233294Sstas kret = krb5_kt_get_entry(context, handle->keytab, 286178825Sdfr handle->principal, 0, 0, &entry); 287178825Sdfr if (kret) 288178825Sdfr goto end; 289178825Sdfr krb5_kt_free_entry(context, &entry); 290178825Sdfr ret = GSS_S_COMPLETE; 291178825Sdfr } else { 292233294Sstas /* 293178825Sdfr * Check if there is at least one entry in the keytab before 294178825Sdfr * declaring it as an useful keytab. 295178825Sdfr */ 296178825Sdfr krb5_keytab_entry tmp; 297178825Sdfr krb5_kt_cursor c; 298178825Sdfr 299178825Sdfr kret = krb5_kt_start_seq_get (context, handle->keytab, &c); 300178825Sdfr if (kret) 301178825Sdfr goto end; 302178825Sdfr if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) { 303178825Sdfr krb5_kt_free_entry(context, &tmp); 304178825Sdfr ret = GSS_S_COMPLETE; /* ok found one entry */ 305178825Sdfr } 306178825Sdfr krb5_kt_end_seq_get (context, handle->keytab, &c); 307233294Sstas } 308178825Sdfrend: 309178825Sdfr if (ret != GSS_S_COMPLETE) { 310178825Sdfr if (handle->keytab != NULL) 311178825Sdfr krb5_kt_close(context, handle->keytab); 312178825Sdfr if (kret != 0) { 313178825Sdfr *minor_status = kret; 314178825Sdfr } 315178825Sdfr } 316178825Sdfr return (ret); 317178825Sdfr} 318178825Sdfr 319233294SstasOM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred 320178825Sdfr(OM_uint32 * minor_status, 321178825Sdfr const gss_name_t desired_name, 322178825Sdfr OM_uint32 time_req, 323178825Sdfr const gss_OID_set desired_mechs, 324178825Sdfr gss_cred_usage_t cred_usage, 325178825Sdfr gss_cred_id_t * output_cred_handle, 326178825Sdfr gss_OID_set * actual_mechs, 327178825Sdfr OM_uint32 * time_rec 328178825Sdfr ) 329178825Sdfr{ 330178825Sdfr OM_uint32 ret; 331178825Sdfr 332178825Sdfr if (desired_mechs) { 333178825Sdfr int present = 0; 334178825Sdfr 335178825Sdfr ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 336233294Sstas desired_mechs, &present); 337178825Sdfr if (ret) 338178825Sdfr return ret; 339178825Sdfr if (!present) { 340178825Sdfr *minor_status = 0; 341178825Sdfr return GSS_S_BAD_MECH; 342178825Sdfr } 343178825Sdfr } 344178825Sdfr 345233294Sstas ret = _gsskrb5_acquire_cred_ext(minor_status, 346233294Sstas desired_name, 347233294Sstas GSS_C_NO_OID, 348233294Sstas NULL, 349233294Sstas time_req, 350233294Sstas GSS_KRB5_MECHANISM, 351233294Sstas cred_usage, 352233294Sstas output_cred_handle); 353233294Sstas if (ret) 354233294Sstas return ret; 355233294Sstas 356233294Sstas 357233294Sstas ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle, 358233294Sstas NULL, time_rec, NULL, actual_mechs); 359233294Sstas if (ret) { 360233294Sstas OM_uint32 tmp; 361233294Sstas _gsskrb5_release_cred(&tmp, output_cred_handle); 362233294Sstas } 363233294Sstas 364233294Sstas return ret; 365233294Sstas} 366233294Sstas 367233294SstasOM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext 368233294Sstas(OM_uint32 * minor_status, 369233294Sstas const gss_name_t desired_name, 370233294Sstas gss_const_OID credential_type, 371233294Sstas const void *credential_data, 372233294Sstas OM_uint32 time_req, 373233294Sstas gss_const_OID desired_mech, 374233294Sstas gss_cred_usage_t cred_usage, 375233294Sstas gss_cred_id_t * output_cred_handle 376233294Sstas ) 377233294Sstas{ 378233294Sstas krb5_context context; 379233294Sstas gsskrb5_cred handle; 380233294Sstas OM_uint32 ret; 381233294Sstas 382233294Sstas cred_usage &= GSS_C_OPTION_MASK; 383233294Sstas 384233294Sstas if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { 385233294Sstas *minor_status = GSS_KRB5_S_G_BAD_USAGE; 386233294Sstas return GSS_S_FAILURE; 387233294Sstas } 388233294Sstas 389233294Sstas GSSAPI_KRB5_INIT(&context); 390233294Sstas 391233294Sstas *output_cred_handle = NULL; 392233294Sstas 393178825Sdfr handle = calloc(1, sizeof(*handle)); 394178825Sdfr if (handle == NULL) { 395178825Sdfr *minor_status = ENOMEM; 396178825Sdfr return (GSS_S_FAILURE); 397178825Sdfr } 398178825Sdfr 399178825Sdfr HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 400178825Sdfr 401178825Sdfr if (desired_name != GSS_C_NO_NAME) { 402233294Sstas ret = _gsskrb5_canon_name(minor_status, context, 1, NULL, 403233294Sstas desired_name, &handle->principal); 404178825Sdfr if (ret) { 405178825Sdfr HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 406178825Sdfr free(handle); 407233294Sstas return ret; 408178825Sdfr } 409178825Sdfr } 410178825Sdfr if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { 411178825Sdfr ret = acquire_initiator_cred(minor_status, context, 412233294Sstas credential_type, credential_data, 413178825Sdfr desired_name, time_req, 414233294Sstas desired_mech, cred_usage, handle); 415178825Sdfr if (ret != GSS_S_COMPLETE) { 416178825Sdfr HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 417178825Sdfr krb5_free_principal(context, handle->principal); 418178825Sdfr free(handle); 419178825Sdfr return (ret); 420178825Sdfr } 421178825Sdfr } 422178825Sdfr if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { 423178825Sdfr ret = acquire_acceptor_cred(minor_status, context, 424233294Sstas credential_type, credential_data, 425178825Sdfr desired_name, time_req, 426233294Sstas desired_mech, cred_usage, handle); 427178825Sdfr if (ret != GSS_S_COMPLETE) { 428178825Sdfr HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 429178825Sdfr krb5_free_principal(context, handle->principal); 430178825Sdfr free(handle); 431178825Sdfr return (ret); 432178825Sdfr } 433178825Sdfr } 434178825Sdfr ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms); 435178825Sdfr if (ret == GSS_S_COMPLETE) 436178825Sdfr ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 437178825Sdfr &handle->mechanisms); 438178825Sdfr if (ret != GSS_S_COMPLETE) { 439178825Sdfr if (handle->mechanisms != NULL) 440178825Sdfr gss_release_oid_set(NULL, &handle->mechanisms); 441178825Sdfr HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 442178825Sdfr krb5_free_principal(context, handle->principal); 443178825Sdfr free(handle); 444178825Sdfr return (ret); 445178825Sdfr } 446178825Sdfr handle->usage = cred_usage; 447233294Sstas *minor_status = 0; 448178825Sdfr *output_cred_handle = (gss_cred_id_t)handle; 449178825Sdfr return (GSS_S_COMPLETE); 450178825Sdfr} 451