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 "krb5/gsskrb5_locl.h" 35 36 RCSID("$Id: acquire_cred.c 22124 2007-12-04 00:03:52Z lha $"); 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_get_credentials(context, 0, 67 id, &in_cred, &out_cred); 68 krb5_free_principal(context, in_cred.server); 69 if (kret) { 70 *minor_status = kret; 71 return GSS_S_FAILURE; 72 } 73 74 *lifetime = out_cred->times.endtime; 75 krb5_free_creds(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 char kt_name[256]; 87 krb5_error_code kret; 88 89 HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex); 90 91 if (_gsskrb5_keytab != NULL) { 92 kret = krb5_kt_get_name(context, 93 _gsskrb5_keytab, 94 kt_name, sizeof(kt_name)); 95 if (kret == 0) 96 kret = krb5_kt_resolve(context, kt_name, keytab); 97 } else 98 kret = krb5_kt_default(context, keytab); 99 100 HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex); 101 102 return (kret); 103 } 104 105 static OM_uint32 acquire_initiator_cred 106 (OM_uint32 * minor_status, 107 krb5_context context, 108 const gss_name_t desired_name, 109 OM_uint32 time_req, 110 const gss_OID_set desired_mechs, 111 gss_cred_usage_t cred_usage, 112 gsskrb5_cred handle, 113 gss_OID_set * actual_mechs, 114 OM_uint32 * time_rec 115 ) 116 { 117 OM_uint32 ret; 118 krb5_creds cred; 119 krb5_principal def_princ; 120 krb5_get_init_creds_opt *opt; 121 krb5_ccache ccache; 122 krb5_keytab keytab; 123 krb5_error_code kret; 124 125 keytab = NULL; 126 ccache = NULL; 127 def_princ = NULL; 128 ret = GSS_S_FAILURE; 129 memset(&cred, 0, sizeof(cred)); 130 131 /* If we have a preferred principal, lets try to find it in all 132 * caches, otherwise, fall back to default cache. Ignore 133 * errors. */ 134 if (handle->principal) 135 kret = krb5_cc_cache_match (context, 136 handle->principal, 137 NULL, 138 &ccache); 139 140 if (ccache == NULL) { 141 kret = krb5_cc_default(context, &ccache); 142 if (kret) 143 goto end; 144 } 145 kret = krb5_cc_get_principal(context, ccache, 146 &def_princ); 147 if (kret != 0) { 148 /* we'll try to use a keytab below */ 149 krb5_cc_destroy(context, ccache); 150 ccache = NULL; 151 kret = 0; 152 } else if (handle->principal == NULL) { 153 kret = krb5_copy_principal(context, def_princ, 154 &handle->principal); 155 if (kret) 156 goto end; 157 } else if (handle->principal != NULL && 158 krb5_principal_compare(context, handle->principal, 159 def_princ) == FALSE) { 160 /* Before failing, lets check the keytab */ 161 krb5_free_principal(context, def_princ); 162 def_princ = NULL; 163 } 164 if (def_princ == NULL) { 165 /* We have no existing credentials cache, 166 * so attempt to get a TGT using a keytab. 167 */ 168 if (handle->principal == NULL) { 169 kret = krb5_get_default_principal(context, 170 &handle->principal); 171 if (kret) 172 goto end; 173 } 174 kret = get_keytab(context, &keytab); 175 if (kret) 176 goto end; 177 kret = krb5_get_init_creds_opt_alloc(context, &opt); 178 if (kret) 179 goto end; 180 kret = krb5_get_init_creds_keytab(context, &cred, 181 handle->principal, keytab, 0, NULL, opt); 182 krb5_get_init_creds_opt_free(context, opt); 183 if (kret) 184 goto end; 185 kret = krb5_cc_gen_new(context, &krb5_mcc_ops, 186 &ccache); 187 if (kret) 188 goto end; 189 kret = krb5_cc_initialize(context, ccache, cred.client); 190 if (kret) 191 goto end; 192 kret = krb5_cc_store_cred(context, ccache, &cred); 193 if (kret) 194 goto end; 195 handle->lifetime = cred.times.endtime; 196 handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 197 } else { 198 199 ret = __gsskrb5_ccache_lifetime(minor_status, 200 context, 201 ccache, 202 handle->principal, 203 &handle->lifetime); 204 if (ret != GSS_S_COMPLETE) 205 goto end; 206 kret = 0; 207 } 208 209 handle->ccache = ccache; 210 ret = GSS_S_COMPLETE; 211 212 end: 213 if (cred.client != NULL) 214 krb5_free_cred_contents(context, &cred); 215 if (def_princ != NULL) 216 krb5_free_principal(context, def_princ); 217 if (keytab != NULL) 218 krb5_kt_close(context, keytab); 219 if (ret != GSS_S_COMPLETE) { 220 if (ccache != NULL) 221 krb5_cc_close(context, ccache); 222 if (kret != 0) { 223 *minor_status = kret; 224 } 225 } 226 return (ret); 227 } 228 229 static OM_uint32 acquire_acceptor_cred 230 (OM_uint32 * minor_status, 231 krb5_context context, 232 const gss_name_t desired_name, 233 OM_uint32 time_req, 234 const gss_OID_set desired_mechs, 235 gss_cred_usage_t cred_usage, 236 gsskrb5_cred handle, 237 gss_OID_set * actual_mechs, 238 OM_uint32 * time_rec 239 ) 240 { 241 OM_uint32 ret; 242 krb5_error_code kret; 243 244 kret = 0; 245 ret = GSS_S_FAILURE; 246 kret = get_keytab(context, &handle->keytab); 247 if (kret) 248 goto end; 249 250 /* check that the requested principal exists in the keytab */ 251 if (handle->principal) { 252 krb5_keytab_entry entry; 253 254 kret = krb5_kt_get_entry(context, handle->keytab, 255 handle->principal, 0, 0, &entry); 256 if (kret) 257 goto end; 258 krb5_kt_free_entry(context, &entry); 259 ret = GSS_S_COMPLETE; 260 } else { 261 /* 262 * Check if there is at least one entry in the keytab before 263 * declaring it as an useful keytab. 264 */ 265 krb5_keytab_entry tmp; 266 krb5_kt_cursor c; 267 268 kret = krb5_kt_start_seq_get (context, handle->keytab, &c); 269 if (kret) 270 goto end; 271 if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) { 272 krb5_kt_free_entry(context, &tmp); 273 ret = GSS_S_COMPLETE; /* ok found one entry */ 274 } 275 krb5_kt_end_seq_get (context, handle->keytab, &c); 276 } 277 end: 278 if (ret != GSS_S_COMPLETE) { 279 if (handle->keytab != NULL) 280 krb5_kt_close(context, handle->keytab); 281 if (kret != 0) { 282 *minor_status = kret; 283 } 284 } 285 return (ret); 286 } 287 288 OM_uint32 _gsskrb5_acquire_cred 289 (OM_uint32 * minor_status, 290 const gss_name_t desired_name, 291 OM_uint32 time_req, 292 const gss_OID_set desired_mechs, 293 gss_cred_usage_t cred_usage, 294 gss_cred_id_t * output_cred_handle, 295 gss_OID_set * actual_mechs, 296 OM_uint32 * time_rec 297 ) 298 { 299 krb5_context context; 300 gsskrb5_cred handle; 301 OM_uint32 ret; 302 303 if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { 304 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 305 return GSS_S_FAILURE; 306 } 307 308 GSSAPI_KRB5_INIT(&context); 309 310 *output_cred_handle = NULL; 311 if (time_rec) 312 *time_rec = 0; 313 if (actual_mechs) 314 *actual_mechs = GSS_C_NO_OID_SET; 315 316 if (desired_mechs) { 317 int present = 0; 318 319 ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 320 desired_mechs, &present); 321 if (ret) 322 return ret; 323 if (!present) { 324 *minor_status = 0; 325 return GSS_S_BAD_MECH; 326 } 327 } 328 329 handle = calloc(1, sizeof(*handle)); 330 if (handle == NULL) { 331 *minor_status = ENOMEM; 332 return (GSS_S_FAILURE); 333 } 334 335 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 336 337 if (desired_name != GSS_C_NO_NAME) { 338 krb5_principal name = (krb5_principal)desired_name; 339 ret = krb5_copy_principal(context, name, &handle->principal); 340 if (ret) { 341 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 342 *minor_status = ret; 343 free(handle); 344 return GSS_S_FAILURE; 345 } 346 } 347 if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { 348 ret = acquire_initiator_cred(minor_status, context, 349 desired_name, time_req, 350 desired_mechs, cred_usage, handle, 351 actual_mechs, time_rec); 352 if (ret != GSS_S_COMPLETE) { 353 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 354 krb5_free_principal(context, handle->principal); 355 free(handle); 356 return (ret); 357 } 358 } 359 if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { 360 ret = acquire_acceptor_cred(minor_status, context, 361 desired_name, time_req, 362 desired_mechs, cred_usage, handle, actual_mechs, time_rec); 363 if (ret != GSS_S_COMPLETE) { 364 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 365 krb5_free_principal(context, handle->principal); 366 free(handle); 367 return (ret); 368 } 369 } 370 ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms); 371 if (ret == GSS_S_COMPLETE) 372 ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 373 &handle->mechanisms); 374 if (ret == GSS_S_COMPLETE) 375 ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)handle, 376 NULL, time_rec, NULL, actual_mechs); 377 if (ret != GSS_S_COMPLETE) { 378 if (handle->mechanisms != NULL) 379 gss_release_oid_set(NULL, &handle->mechanisms); 380 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 381 krb5_free_principal(context, handle->principal); 382 free(handle); 383 return (ret); 384 } 385 *minor_status = 0; 386 if (time_rec) { 387 ret = _gsskrb5_lifetime_left(minor_status, 388 context, 389 handle->lifetime, 390 time_rec); 391 392 if (ret) 393 return ret; 394 } 395 handle->usage = cred_usage; 396 *output_cred_handle = (gss_cred_id_t)handle; 397 return (GSS_S_COMPLETE); 398 } 399