1 /* 2 * Copyright (c) 1997 - 2005 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 "gsskrb5_locl.h" 35 36 OM_uint32 37 __gsskrb5_ccache_lifetime(OM_uint32 *minor_status, 38 krb5_context context, 39 krb5_ccache id, 40 krb5_principal principal, 41 OM_uint32 *lifetime) 42 { 43 krb5_creds in_cred, out_cred; 44 krb5_const_realm realm; 45 krb5_error_code kret; 46 47 memset(&in_cred, 0, sizeof(in_cred)); 48 in_cred.client = principal; 49 50 realm = krb5_principal_get_realm(context, principal); 51 if (realm == NULL) { 52 _gsskrb5_clear_status (); 53 *minor_status = KRB5_PRINC_NOMATCH; /* XXX */ 54 return GSS_S_FAILURE; 55 } 56 57 kret = krb5_make_principal(context, &in_cred.server, 58 realm, KRB5_TGS_NAME, realm, NULL); 59 if (kret) { 60 *minor_status = kret; 61 return GSS_S_FAILURE; 62 } 63 64 kret = krb5_cc_retrieve_cred(context, id, 0, &in_cred, &out_cred); 65 krb5_free_principal(context, in_cred.server); 66 if (kret) { 67 *minor_status = 0; 68 *lifetime = 0; 69 return GSS_S_COMPLETE; 70 } 71 72 *lifetime = out_cred.times.endtime; 73 krb5_free_cred_contents(context, &out_cred); 74 75 return GSS_S_COMPLETE; 76 } 77 78 79 80 81 static krb5_error_code 82 get_keytab(krb5_context context, krb5_keytab *keytab) 83 { 84 krb5_error_code kret; 85 86 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex); 87 88 if (_gsskrb5_keytab != NULL) { 89 char *name = NULL; 90 91 kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name); 92 if (kret == 0) { 93 kret = krb5_kt_resolve(context, name, keytab); 94 krb5_xfree(name); 95 } 96 } else 97 kret = krb5_kt_default(context, keytab); 98 99 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); 100 101 return (kret); 102 } 103 104 static OM_uint32 acquire_initiator_cred 105 (OM_uint32 * minor_status, 106 krb5_context context, 107 gss_const_OID credential_type, 108 const void *credential_data, 109 const gss_name_t desired_name, 110 OM_uint32 time_req, 111 gss_const_OID desired_mech, 112 gss_cred_usage_t cred_usage, 113 gsskrb5_cred handle 114 ) 115 { 116 OM_uint32 ret; 117 krb5_creds cred; 118 krb5_principal def_princ; 119 krb5_get_init_creds_opt *opt; 120 krb5_ccache ccache; 121 krb5_keytab keytab; 122 krb5_error_code kret; 123 124 keytab = NULL; 125 ccache = NULL; 126 def_princ = NULL; 127 ret = GSS_S_FAILURE; 128 memset(&cred, 0, sizeof(cred)); 129 130 /* 131 * If we have a preferred principal, lets try to find it in all 132 * caches, otherwise, fall back to default cache, ignore all 133 * errors while searching. 134 */ 135 136 if (credential_type != GSS_C_NO_OID && 137 !gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { 138 kret = KRB5_NOCREDS_SUPPLIED; /* XXX */ 139 goto end; 140 } 141 142 if (handle->principal) { 143 kret = krb5_cc_cache_match (context, 144 handle->principal, 145 &ccache); 146 if (kret == 0) { 147 ret = GSS_S_COMPLETE; 148 goto found; 149 } 150 } 151 152 if (ccache == NULL) { 153 kret = krb5_cc_default(context, &ccache); 154 if (kret) 155 goto end; 156 } 157 kret = krb5_cc_get_principal(context, ccache, &def_princ); 158 if (kret != 0) { 159 /* we'll try to use a keytab below */ 160 krb5_cc_close(context, ccache); 161 def_princ = NULL; 162 kret = 0; 163 } else if (handle->principal == NULL) { 164 kret = krb5_copy_principal(context, def_princ, &handle->principal); 165 if (kret) 166 goto end; 167 } else if (handle->principal != NULL && 168 krb5_principal_compare(context, handle->principal, 169 def_princ) == FALSE) { 170 krb5_free_principal(context, def_princ); 171 def_princ = NULL; 172 krb5_cc_close(context, ccache); 173 ccache = NULL; 174 } 175 if (def_princ == NULL) { 176 /* We have no existing credentials cache, 177 * so attempt to get a TGT using a keytab. 178 */ 179 if (handle->principal == NULL) { 180 kret = krb5_get_default_principal(context, &handle->principal); 181 if (kret) 182 goto end; 183 } 184 kret = krb5_get_init_creds_opt_alloc(context, &opt); 185 if (kret) 186 goto end; 187 if (credential_type != GSS_C_NO_OID && 188 gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { 189 gss_buffer_t password = (gss_buffer_t)credential_data; 190 191 /* XXX are we requiring password to be NUL terminated? */ 192 193 kret = krb5_get_init_creds_password(context, &cred, 194 handle->principal, 195 password->value, 196 NULL, NULL, 0, NULL, opt); 197 } else { 198 kret = get_keytab(context, &keytab); 199 if (kret) { 200 krb5_get_init_creds_opt_free(context, opt); 201 goto end; 202 } 203 kret = krb5_get_init_creds_keytab(context, &cred, 204 handle->principal, keytab, 205 0, NULL, opt); 206 } 207 krb5_get_init_creds_opt_free(context, opt); 208 if (kret) 209 goto end; 210 kret = krb5_cc_new_unique(context, krb5_cc_type_memory, 211 NULL, &ccache); 212 if (kret) 213 goto end; 214 kret = krb5_cc_initialize(context, ccache, cred.client); 215 if (kret) { 216 krb5_cc_destroy(context, ccache); 217 goto end; 218 } 219 kret = krb5_cc_store_cred(context, ccache, &cred); 220 if (kret) { 221 krb5_cc_destroy(context, ccache); 222 goto end; 223 } 224 handle->lifetime = cred.times.endtime; 225 handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 226 } else { 227 228 ret = __gsskrb5_ccache_lifetime(minor_status, 229 context, 230 ccache, 231 handle->principal, 232 &handle->lifetime); 233 if (ret != GSS_S_COMPLETE) { 234 krb5_cc_close(context, ccache); 235 goto end; 236 } 237 kret = 0; 238 } 239 found: 240 handle->ccache = ccache; 241 ret = GSS_S_COMPLETE; 242 243 end: 244 if (cred.client != NULL) 245 krb5_free_cred_contents(context, &cred); 246 if (def_princ != NULL) 247 krb5_free_principal(context, def_princ); 248 if (keytab != NULL) 249 krb5_kt_close(context, keytab); 250 if (ret != GSS_S_COMPLETE && kret != 0) 251 *minor_status = kret; 252 return (ret); 253 } 254 255 static OM_uint32 acquire_acceptor_cred 256 (OM_uint32 * minor_status, 257 krb5_context context, 258 gss_const_OID credential_type, 259 const void *credential_data, 260 const gss_name_t desired_name, 261 OM_uint32 time_req, 262 gss_const_OID desired_mech, 263 gss_cred_usage_t cred_usage, 264 gsskrb5_cred handle 265 ) 266 { 267 OM_uint32 ret; 268 krb5_error_code kret; 269 270 ret = GSS_S_FAILURE; 271 272 if (credential_type != GSS_C_NO_OID) { 273 kret = EINVAL; 274 goto end; 275 } 276 277 kret = get_keytab(context, &handle->keytab); 278 if (kret) 279 goto end; 280 281 /* check that the requested principal exists in the keytab */ 282 if (handle->principal) { 283 krb5_keytab_entry entry; 284 285 kret = krb5_kt_get_entry(context, handle->keytab, 286 handle->principal, 0, 0, &entry); 287 if (kret) 288 goto end; 289 krb5_kt_free_entry(context, &entry); 290 ret = GSS_S_COMPLETE; 291 } else { 292 /* 293 * Check if there is at least one entry in the keytab before 294 * declaring it as an useful keytab. 295 */ 296 krb5_keytab_entry tmp; 297 krb5_kt_cursor c; 298 299 kret = krb5_kt_start_seq_get (context, handle->keytab, &c); 300 if (kret) 301 goto end; 302 if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) { 303 krb5_kt_free_entry(context, &tmp); 304 ret = GSS_S_COMPLETE; /* ok found one entry */ 305 } 306 krb5_kt_end_seq_get (context, handle->keytab, &c); 307 } 308 end: 309 if (ret != GSS_S_COMPLETE) { 310 if (handle->keytab != NULL) 311 krb5_kt_close(context, handle->keytab); 312 if (kret != 0) { 313 *minor_status = kret; 314 } 315 } 316 return (ret); 317 } 318 319 OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred 320 (OM_uint32 * minor_status, 321 const gss_name_t desired_name, 322 OM_uint32 time_req, 323 const gss_OID_set desired_mechs, 324 gss_cred_usage_t cred_usage, 325 gss_cred_id_t * output_cred_handle, 326 gss_OID_set * actual_mechs, 327 OM_uint32 * time_rec 328 ) 329 { 330 OM_uint32 ret; 331 332 if (desired_mechs) { 333 int present = 0; 334 335 ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 336 desired_mechs, &present); 337 if (ret) 338 return ret; 339 if (!present) { 340 *minor_status = 0; 341 return GSS_S_BAD_MECH; 342 } 343 } 344 345 ret = _gsskrb5_acquire_cred_ext(minor_status, 346 desired_name, 347 GSS_C_NO_OID, 348 NULL, 349 time_req, 350 GSS_KRB5_MECHANISM, 351 cred_usage, 352 output_cred_handle); 353 if (ret) 354 return ret; 355 356 357 ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle, 358 NULL, time_rec, NULL, actual_mechs); 359 if (ret) { 360 OM_uint32 tmp; 361 _gsskrb5_release_cred(&tmp, output_cred_handle); 362 } 363 364 return ret; 365 } 366 367 OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext 368 (OM_uint32 * minor_status, 369 const gss_name_t desired_name, 370 gss_const_OID credential_type, 371 const void *credential_data, 372 OM_uint32 time_req, 373 gss_const_OID desired_mech, 374 gss_cred_usage_t cred_usage, 375 gss_cred_id_t * output_cred_handle 376 ) 377 { 378 krb5_context context; 379 gsskrb5_cred handle; 380 OM_uint32 ret; 381 382 cred_usage &= GSS_C_OPTION_MASK; 383 384 if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { 385 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 386 return GSS_S_FAILURE; 387 } 388 389 GSSAPI_KRB5_INIT(&context); 390 391 *output_cred_handle = NULL; 392 393 handle = calloc(1, sizeof(*handle)); 394 if (handle == NULL) { 395 *minor_status = ENOMEM; 396 return (GSS_S_FAILURE); 397 } 398 399 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 400 401 if (desired_name != GSS_C_NO_NAME) { 402 ret = _gsskrb5_canon_name(minor_status, context, 1, NULL, 403 desired_name, &handle->principal); 404 if (ret) { 405 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 406 free(handle); 407 return ret; 408 } 409 } 410 if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { 411 ret = acquire_initiator_cred(minor_status, context, 412 credential_type, credential_data, 413 desired_name, time_req, 414 desired_mech, cred_usage, handle); 415 if (ret != GSS_S_COMPLETE) { 416 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 417 krb5_free_principal(context, handle->principal); 418 free(handle); 419 return (ret); 420 } 421 } 422 if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { 423 ret = acquire_acceptor_cred(minor_status, context, 424 credential_type, credential_data, 425 desired_name, time_req, 426 desired_mech, cred_usage, handle); 427 if (ret != GSS_S_COMPLETE) { 428 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 429 krb5_free_principal(context, handle->principal); 430 free(handle); 431 return (ret); 432 } 433 } 434 ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms); 435 if (ret == GSS_S_COMPLETE) 436 ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 437 &handle->mechanisms); 438 if (ret != GSS_S_COMPLETE) { 439 if (handle->mechanisms != NULL) 440 gss_release_oid_set(NULL, &handle->mechanisms); 441 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 442 krb5_free_principal(context, handle->principal); 443 free(handle); 444 return (ret); 445 } 446 handle->usage = cred_usage; 447 *minor_status = 0; 448 *output_cred_handle = (gss_cred_id_t)handle; 449 return (GSS_S_COMPLETE); 450 } 451