1/* $NetBSD$ */ 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 38OM_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 83static krb5_error_code 84get_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 106static OM_uint32 acquire_initiator_cred 107 (OM_uint32 * minor_status, 108 krb5_context context, 109 const gss_name_t desired_name, 110 OM_uint32 time_req, 111 const gss_OID_set desired_mechs, 112 gss_cred_usage_t cred_usage, 113 gsskrb5_cred handle, 114 gss_OID_set * actual_mechs, 115 OM_uint32 * time_rec 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 (handle->principal) { 139 kret = krb5_cc_cache_match (context, 140 handle->principal, 141 &ccache); 142 if (kret == 0) { 143 ret = GSS_S_COMPLETE; 144 goto found; 145 } 146 } 147 148 if (ccache == NULL) { 149 kret = krb5_cc_default(context, &ccache); 150 if (kret) 151 goto end; 152 } 153 kret = krb5_cc_get_principal(context, ccache, &def_princ); 154 if (kret != 0) { 155 /* we'll try to use a keytab below */ 156 krb5_cc_close(context, ccache); 157 def_princ = NULL; 158 kret = 0; 159 } else if (handle->principal == NULL) { 160 kret = krb5_copy_principal(context, def_princ, &handle->principal); 161 if (kret) 162 goto end; 163 } else if (handle->principal != NULL && 164 krb5_principal_compare(context, handle->principal, 165 def_princ) == FALSE) { 166 krb5_free_principal(context, def_princ); 167 def_princ = NULL; 168 krb5_cc_close(context, ccache); 169 ccache = NULL; 170 } 171 if (def_princ == NULL) { 172 /* We have no existing credentials cache, 173 * so attempt to get a TGT using a keytab. 174 */ 175 if (handle->principal == NULL) { 176 kret = krb5_get_default_principal(context, &handle->principal); 177 if (kret) 178 goto end; 179 } 180 kret = get_keytab(context, &keytab); 181 if (kret) 182 goto end; 183 kret = krb5_get_init_creds_opt_alloc(context, &opt); 184 if (kret) 185 goto end; 186 kret = krb5_get_init_creds_keytab(context, &cred, 187 handle->principal, keytab, 0, NULL, opt); 188 krb5_get_init_creds_opt_free(context, opt); 189 if (kret) 190 goto end; 191 kret = krb5_cc_new_unique(context, krb5_cc_type_memory, 192 NULL, &ccache); 193 if (kret) 194 goto end; 195 kret = krb5_cc_initialize(context, ccache, cred.client); 196 if (kret) { 197 krb5_cc_destroy(context, ccache); 198 goto end; 199 } 200 kret = krb5_cc_store_cred(context, ccache, &cred); 201 if (kret) { 202 krb5_cc_destroy(context, ccache); 203 goto end; 204 } 205 handle->lifetime = cred.times.endtime; 206 handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; 207 } else { 208 209 ret = __gsskrb5_ccache_lifetime(minor_status, 210 context, 211 ccache, 212 handle->principal, 213 &handle->lifetime); 214 if (ret != GSS_S_COMPLETE) { 215 krb5_cc_close(context, ccache); 216 goto end; 217 } 218 kret = 0; 219 } 220 found: 221 handle->ccache = ccache; 222 ret = GSS_S_COMPLETE; 223 224end: 225 if (cred.client != NULL) 226 krb5_free_cred_contents(context, &cred); 227 if (def_princ != NULL) 228 krb5_free_principal(context, def_princ); 229 if (keytab != NULL) 230 krb5_kt_close(context, keytab); 231 if (ret != GSS_S_COMPLETE && kret != 0) 232 *minor_status = kret; 233 return (ret); 234} 235 236static OM_uint32 acquire_acceptor_cred 237 (OM_uint32 * minor_status, 238 krb5_context context, 239 const gss_name_t desired_name, 240 OM_uint32 time_req, 241 const gss_OID_set desired_mechs, 242 gss_cred_usage_t cred_usage, 243 gsskrb5_cred handle, 244 gss_OID_set * actual_mechs, 245 OM_uint32 * time_rec 246 ) 247{ 248 OM_uint32 ret; 249 krb5_error_code kret; 250 251 ret = GSS_S_FAILURE; 252 kret = get_keytab(context, &handle->keytab); 253 if (kret) 254 goto end; 255 256 /* check that the requested principal exists in the keytab */ 257 if (handle->principal) { 258 krb5_keytab_entry entry; 259 260 kret = krb5_kt_get_entry(context, handle->keytab, 261 handle->principal, 0, 0, &entry); 262 if (kret) 263 goto end; 264 krb5_kt_free_entry(context, &entry); 265 ret = GSS_S_COMPLETE; 266 } else { 267 /* 268 * Check if there is at least one entry in the keytab before 269 * declaring it as an useful keytab. 270 */ 271 krb5_keytab_entry tmp; 272 krb5_kt_cursor c; 273 274 kret = krb5_kt_start_seq_get (context, handle->keytab, &c); 275 if (kret) 276 goto end; 277 if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) { 278 krb5_kt_free_entry(context, &tmp); 279 ret = GSS_S_COMPLETE; /* ok found one entry */ 280 } 281 krb5_kt_end_seq_get (context, handle->keytab, &c); 282 } 283end: 284 if (ret != GSS_S_COMPLETE) { 285 if (handle->keytab != NULL) 286 krb5_kt_close(context, handle->keytab); 287 if (kret != 0) { 288 *minor_status = kret; 289 } 290 } 291 return (ret); 292} 293 294OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred 295(OM_uint32 * minor_status, 296 const gss_name_t desired_name, 297 OM_uint32 time_req, 298 const gss_OID_set desired_mechs, 299 gss_cred_usage_t cred_usage, 300 gss_cred_id_t * output_cred_handle, 301 gss_OID_set * actual_mechs, 302 OM_uint32 * time_rec 303 ) 304{ 305 krb5_context context; 306 gsskrb5_cred handle; 307 OM_uint32 ret; 308 309 if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { 310 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 311 return GSS_S_FAILURE; 312 } 313 314 GSSAPI_KRB5_INIT(&context); 315 316 *output_cred_handle = NULL; 317 if (time_rec) 318 *time_rec = 0; 319 if (actual_mechs) 320 *actual_mechs = GSS_C_NO_OID_SET; 321 322 if (desired_mechs) { 323 int present = 0; 324 325 ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 326 desired_mechs, &present); 327 if (ret) 328 return ret; 329 if (!present) { 330 *minor_status = 0; 331 return GSS_S_BAD_MECH; 332 } 333 } 334 335 handle = calloc(1, sizeof(*handle)); 336 if (handle == NULL) { 337 *minor_status = ENOMEM; 338 return (GSS_S_FAILURE); 339 } 340 341 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 342 343 if (desired_name != GSS_C_NO_NAME) { 344 345 ret = _gsskrb5_canon_name(minor_status, context, 1, NULL, 346 desired_name, &handle->principal); 347 if (ret) { 348 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 349 free(handle); 350 return ret; 351 } 352 } 353 if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { 354 ret = acquire_initiator_cred(minor_status, context, 355 desired_name, time_req, 356 desired_mechs, cred_usage, handle, 357 actual_mechs, time_rec); 358 if (ret != GSS_S_COMPLETE) { 359 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 360 krb5_free_principal(context, handle->principal); 361 free(handle); 362 return (ret); 363 } 364 } 365 if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { 366 ret = acquire_acceptor_cred(minor_status, context, 367 desired_name, time_req, 368 desired_mechs, cred_usage, handle, actual_mechs, time_rec); 369 if (ret != GSS_S_COMPLETE) { 370 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 371 krb5_free_principal(context, handle->principal); 372 free(handle); 373 return (ret); 374 } 375 } 376 ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms); 377 if (ret == GSS_S_COMPLETE) 378 ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 379 &handle->mechanisms); 380 if (ret == GSS_S_COMPLETE) 381 ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)handle, 382 NULL, time_rec, NULL, actual_mechs); 383 if (ret != GSS_S_COMPLETE) { 384 if (handle->mechanisms != NULL) 385 gss_release_oid_set(NULL, &handle->mechanisms); 386 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); 387 krb5_free_principal(context, handle->principal); 388 free(handle); 389 return (ret); 390 } 391 *minor_status = 0; 392 if (time_rec) { 393 ret = _gsskrb5_lifetime_left(minor_status, 394 context, 395 handle->lifetime, 396 time_rec); 397 398 if (ret) 399 return ret; 400 } 401 handle->usage = cred_usage; 402 *output_cred_handle = (gss_cred_id_t)handle; 403 return (GSS_S_COMPLETE); 404} 405