1 /* $NetBSD: acquire_cred.c,v 1.1.1.2 2014/04/24 12:45:29 pettai Exp $ */ 2 3 /* 4 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include "gsskrb5_locl.h" 37 38 OM_uint32 39 __gsskrb5_ccache_lifetime(OM_uint32 *minor_status, 40 krb5_context context, 41 krb5_ccache id, 42 krb5_principal principal, 43 OM_uint32 *lifetime) 44 { 45 krb5_creds in_cred, out_cred; 46 krb5_const_realm realm; 47 krb5_error_code kret; 48 49 memset(&in_cred, 0, sizeof(in_cred)); 50 in_cred.client = principal; 51 52 realm = krb5_principal_get_realm(context, principal); 53 if (realm == NULL) { 54 _gsskrb5_clear_status (); 55 *minor_status = KRB5_PRINC_NOMATCH; /* XXX */ 56 return GSS_S_FAILURE; 57 } 58 59 kret = krb5_make_principal(context, &in_cred.server, 60 realm, KRB5_TGS_NAME, realm, NULL); 61 if (kret) { 62 *minor_status = kret; 63 return GSS_S_FAILURE; 64 } 65 66 kret = krb5_cc_retrieve_cred(context, id, 0, &in_cred, &out_cred); 67 krb5_free_principal(context, in_cred.server); 68 if (kret) { 69 *minor_status = 0; 70 *lifetime = 0; 71 return GSS_S_COMPLETE; 72 } 73 74 *lifetime = out_cred.times.endtime; 75 krb5_free_cred_contents(context, &out_cred); 76 77 return GSS_S_COMPLETE; 78 } 79 80 81 82 83 static krb5_error_code 84 get_keytab(krb5_context context, krb5_keytab *keytab) 85 { 86 krb5_error_code kret; 87 88 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex); 89 90 if (_gsskrb5_keytab != NULL) { 91 char *name = NULL; 92 93 kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name); 94 if (kret == 0) { 95 kret = krb5_kt_resolve(context, name, keytab); 96 krb5_xfree(name); 97 } 98 } else 99 kret = krb5_kt_default(context, keytab); 100 101 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); 102 103 return (kret); 104 } 105 106 static OM_uint32 acquire_initiator_cred 107 (OM_uint32 * minor_status, 108 krb5_context context, 109 gss_const_OID credential_type, 110 const void *credential_data, 111 const gss_name_t desired_name, 112 OM_uint32 time_req, 113 gss_const_OID desired_mech, 114 gss_cred_usage_t cred_usage, 115 gsskrb5_cred handle 116 ) 117 { 118 OM_uint32 ret; 119 krb5_creds cred; 120 krb5_principal def_princ; 121 krb5_get_init_creds_opt *opt; 122 krb5_ccache ccache; 123 krb5_keytab keytab; 124 krb5_error_code kret; 125 126 keytab = NULL; 127 ccache = NULL; 128 def_princ = NULL; 129 ret = GSS_S_FAILURE; 130 memset(&cred, 0, sizeof(cred)); 131 132 /* 133 * If we have a preferred principal, lets try to find it in all 134 * caches, otherwise, fall back to default cache, ignore all 135 * errors while searching. 136 */ 137 138 if (credential_type != GSS_C_NO_OID && 139 !gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { 140 kret = KRB5_NOCREDS_SUPPLIED; /* XXX */ 141 goto end; 142 } 143 144 if (handle->principal) { 145 kret = krb5_cc_cache_match (context, 146 handle->principal, 147 &ccache); 148 if (kret == 0) { 149 ret = GSS_S_COMPLETE; 150 goto found; 151 } 152 } 153 154 if (ccache == NULL) { 155 kret = krb5_cc_default(context, &ccache); 156 if (kret) 157 goto end; 158 } 159 kret = krb5_cc_get_principal(context, ccache, &def_princ); 160 if (kret != 0) { 161 /* we'll try to use a keytab below */ 162 krb5_cc_close(context, ccache); 163 def_princ = NULL; 164 kret = 0; 165 } else if (handle->principal == NULL) { 166 kret = krb5_copy_principal(context, def_princ, &handle->principal); 167 if (kret) 168 goto end; 169 } else if (handle->principal != NULL && 170 krb5_principal_compare(context, handle->principal, 171 def_princ) == FALSE) { 172 krb5_free_principal(context, def_princ); 173 def_princ = NULL; 174 krb5_cc_close(context, ccache); 175 ccache = NULL; 176 } 177 if (def_princ == NULL) { 178 /* We have no existing credentials cache, 179 * so attempt to get a TGT using a keytab. 180 */ 181 if (handle->principal == NULL) { 182 kret = krb5_get_default_principal(context, &handle->principal); 183 if (kret) 184 goto end; 185 } 186 kret = krb5_get_init_creds_opt_alloc(context, &opt); 187 if (kret) 188 goto end; 189 if (credential_type != GSS_C_NO_OID && 190 gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { 191 gss_buffer_t password = (gss_buffer_t)credential_data; 192 193 /* XXX are we requiring password to be NUL terminated? */ 194 195 kret = krb5_get_init_creds_password(context, &cred, 196 handle->principal, 197 password->value, 198 NULL, NULL, 0, NULL, opt); 199 } else { 200 kret = get_keytab(context, &keytab); 201 if (kret) { 202 krb5_get_init_creds_opt_free(context, opt); 203 goto end; 204 } 205 kret = krb5_get_init_creds_keytab(context, &cred, 206 handle->principal, keytab, 207 0, NULL, opt); 208 } 209 krb5_get_init_creds_opt_free(context, opt); 210 if (kret) 211 goto end; 212 kret = krb5_cc_new_unique(context, krb5_cc_type_memory, 213 NULL, &ccache); 214 if (kret) 215 goto end; 216 kret = krb5_cc_initialize(context, ccache, cred.client); 217 if (kret) { 218 krb5_cc_destroy(context, ccache); 219 goto end; 220 } 221 kret = krb5_cc_store_cred(context, ccache, &cred); 222 if (kret) { 223 krb5_cc_destroy(context, ccache); 224 goto end; 225 } 226 handle->lifetime = cred.times.endtime; 227 handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 228 } else { 229 230 ret = __gsskrb5_ccache_lifetime(minor_status, 231 context, 232 ccache, 233 handle->principal, 234 &handle->lifetime); 235 if (ret != GSS_S_COMPLETE) { 236 krb5_cc_close(context, ccache); 237 goto end; 238 } 239 kret = 0; 240 } 241 found: 242 handle->ccache = ccache; 243 ret = GSS_S_COMPLETE; 244 245 end: 246 if (cred.client != NULL) 247 krb5_free_cred_contents(context, &cred); 248 if (def_princ != NULL) 249 krb5_free_principal(context, def_princ); 250 if (keytab != NULL) 251 krb5_kt_close(context, keytab); 252 if (ret != GSS_S_COMPLETE && kret != 0) 253 *minor_status = kret; 254 return (ret); 255 } 256 257 static OM_uint32 acquire_acceptor_cred 258 (OM_uint32 * minor_status, 259 krb5_context context, 260 gss_const_OID credential_type, 261 const void *credential_data, 262 const gss_name_t desired_name, 263 OM_uint32 time_req, 264 gss_const_OID desired_mech, 265 gss_cred_usage_t cred_usage, 266 gsskrb5_cred handle 267 ) 268 { 269 OM_uint32 ret; 270 krb5_error_code kret; 271 272 ret = GSS_S_FAILURE; 273 274 if (credential_type != GSS_C_NO_OID) { 275 kret = EINVAL; 276 goto end; 277 } 278 279 kret = get_keytab(context, &handle->keytab); 280 if (kret) 281 goto end; 282 283 /* check that the requested principal exists in the keytab */ 284 if (handle->principal) { 285 krb5_keytab_entry entry; 286 287 kret = krb5_kt_get_entry(context, handle->keytab, 288 handle->principal, 0, 0, &entry); 289 if (kret) 290 goto end; 291 krb5_kt_free_entry(context, &entry); 292 ret = GSS_S_COMPLETE; 293 } else { 294 /* 295 * Check if there is at least one entry in the keytab before 296 * declaring it as an useful keytab. 297 */ 298 krb5_keytab_entry tmp; 299 krb5_kt_cursor c; 300 301 kret = krb5_kt_start_seq_get (context, handle->keytab, &c); 302 if (kret) 303 goto end; 304 if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) { 305 krb5_kt_free_entry(context, &tmp); 306 ret = GSS_S_COMPLETE; /* ok found one entry */ 307 } 308 krb5_kt_end_seq_get (context, handle->keytab, &c); 309 } 310 end: 311 if (ret != GSS_S_COMPLETE) { 312 if (handle->keytab != NULL) 313 krb5_kt_close(context, handle->keytab); 314 if (kret != 0) { 315 *minor_status = kret; 316 } 317 } 318 return (ret); 319 } 320 321 OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred 322 (OM_uint32 * minor_status, 323 const gss_name_t desired_name, 324 OM_uint32 time_req, 325 const gss_OID_set desired_mechs, 326 gss_cred_usage_t cred_usage, 327 gss_cred_id_t * output_cred_handle, 328 gss_OID_set * actual_mechs, 329 OM_uint32 * time_rec 330 ) 331 { 332 OM_uint32 ret; 333 334 if (desired_mechs) { 335 int present = 0; 336 337 ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 338 desired_mechs, &present); 339 if (ret) 340 return ret; 341 if (!present) { 342 *minor_status = 0; 343 return GSS_S_BAD_MECH; 344 } 345 } 346 347 ret = _gsskrb5_acquire_cred_ext(minor_status, 348 desired_name, 349 GSS_C_NO_OID, 350 NULL, 351 time_req, 352 GSS_KRB5_MECHANISM, 353 cred_usage, 354 output_cred_handle); 355 if (ret) 356 return ret; 357 358 359 ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle, 360 NULL, time_rec, NULL, actual_mechs); 361 if (ret) { 362 OM_uint32 tmp; 363 _gsskrb5_release_cred(&tmp, output_cred_handle); 364 } 365 366 return ret; 367 } 368 369 OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext 370 (OM_uint32 * minor_status, 371 const gss_name_t desired_name, 372 gss_const_OID credential_type, 373 const void *credential_data, 374 OM_uint32 time_req, 375 gss_const_OID desired_mech, 376 gss_cred_usage_t cred_usage, 377 gss_cred_id_t * output_cred_handle 378 ) 379 { 380 krb5_context context; 381 gsskrb5_cred handle; 382 OM_uint32 ret; 383 384 cred_usage &= GSS_C_OPTION_MASK; 385 386 if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { 387 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 388 return GSS_S_FAILURE; 389 } 390 391 GSSAPI_KRB5_INIT(&context); 392 393 *output_cred_handle = NULL; 394 395 handle = calloc(1, sizeof(*handle)); 396 if (handle == NULL) { 397 *minor_status = ENOMEM; 398 return (GSS_S_FAILURE); 399 } 400 401 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 402 403 if (desired_name != GSS_C_NO_NAME) { 404 ret = _gsskrb5_canon_name(minor_status, context, 1, NULL, 405 desired_name, &handle->principal); 406 if (ret) { 407 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 408 free(handle); 409 return ret; 410 } 411 } 412 if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { 413 ret = acquire_initiator_cred(minor_status, context, 414 credential_type, credential_data, 415 desired_name, time_req, 416 desired_mech, cred_usage, handle); 417 if (ret != GSS_S_COMPLETE) { 418 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 419 krb5_free_principal(context, handle->principal); 420 free(handle); 421 return (ret); 422 } 423 } 424 if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { 425 ret = acquire_acceptor_cred(minor_status, context, 426 credential_type, credential_data, 427 desired_name, time_req, 428 desired_mech, cred_usage, handle); 429 if (ret != GSS_S_COMPLETE) { 430 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 431 krb5_free_principal(context, handle->principal); 432 free(handle); 433 return (ret); 434 } 435 } 436 ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms); 437 if (ret == GSS_S_COMPLETE) 438 ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 439 &handle->mechanisms); 440 if (ret != GSS_S_COMPLETE) { 441 if (handle->mechanisms != NULL) 442 gss_release_oid_set(NULL, &handle->mechanisms); 443 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 444 krb5_free_principal(context, handle->principal); 445 free(handle); 446 return (ret); 447 } 448 handle->usage = cred_usage; 449 *minor_status = 0; 450 *output_cred_handle = (gss_cred_id_t)handle; 451 return (GSS_S_COMPLETE); 452 } 453