1 /* $NetBSD: add_cred.c,v 1.2 2017/01/28 21:31:46 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2003 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 GSSAPI_CALLCONV _gsskrb5_add_cred ( 39 OM_uint32 *minor_status, 40 gss_const_cred_id_t input_cred_handle, 41 gss_const_name_t desired_name, 42 const gss_OID desired_mech, 43 gss_cred_usage_t cred_usage, 44 OM_uint32 initiator_time_req, 45 OM_uint32 acceptor_time_req, 46 gss_cred_id_t *output_cred_handle, 47 gss_OID_set *actual_mechs, 48 OM_uint32 *initiator_time_rec, 49 OM_uint32 *acceptor_time_rec) 50 { 51 krb5_context context; 52 OM_uint32 major, lifetime; 53 gsskrb5_cred cred, handle; 54 krb5_const_principal dname; 55 56 handle = NULL; 57 cred = (gsskrb5_cred)input_cred_handle; 58 dname = (krb5_const_principal)desired_name; 59 60 if (cred == NULL && output_cred_handle == NULL) { 61 *minor_status = EINVAL; 62 return GSS_S_CALL_INACCESSIBLE_WRITE; 63 } 64 65 GSSAPI_KRB5_INIT (&context); 66 67 if (desired_mech != GSS_C_NO_OID && 68 gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0) { 69 *minor_status = 0; 70 return GSS_S_BAD_MECH; 71 } 72 73 if (cred == NULL) { 74 /* 75 * Acquire a credential; output_cred_handle can't be NULL, see above. 76 */ 77 heim_assert(output_cred_handle != NULL, 78 "internal error in _gsskrb5_add_cred()"); 79 80 major = _gsskrb5_acquire_cred(minor_status, desired_name, 81 min(initiator_time_req, 82 acceptor_time_req), 83 GSS_C_NO_OID_SET, 84 cred_usage, 85 output_cred_handle, 86 actual_mechs, &lifetime); 87 if (major != GSS_S_COMPLETE) 88 goto failure; 89 90 } else { 91 /* 92 * Check that we're done or copy input to output if 93 * output_cred_handle != NULL. 94 */ 95 96 HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); 97 98 /* Check if requested output usage is compatible with output usage */ 99 if (cred->usage != cred_usage && cred->usage != GSS_C_BOTH) { 100 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 101 *minor_status = GSS_KRB5_S_G_BAD_USAGE; 102 return(GSS_S_FAILURE); 103 } 104 105 /* Check that we have the same name */ 106 if (dname != NULL && 107 krb5_principal_compare(context, dname, 108 cred->principal) != FALSE) { 109 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 110 *minor_status = 0; 111 return GSS_S_BAD_NAME; 112 } 113 114 if (output_cred_handle == NULL) { 115 /* 116 * This case is basically useless as we implement a single 117 * mechanism here, so we can't add elements to the 118 * input_cred_handle. 119 */ 120 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 121 *minor_status = 0; 122 return GSS_S_COMPLETE; 123 } 124 125 /* 126 * Copy input to output -- this works as if we were a 127 * GSS_Duplicate_cred() for one mechanism element. 128 */ 129 130 handle = calloc(1, sizeof(*handle)); 131 if (handle == NULL) { 132 if (cred != NULL) 133 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 134 *minor_status = ENOMEM; 135 return (GSS_S_FAILURE); 136 } 137 138 handle->usage = cred_usage; 139 handle->endtime = cred->endtime; 140 handle->principal = NULL; 141 handle->keytab = NULL; 142 handle->ccache = NULL; 143 handle->mechanisms = NULL; 144 HEIMDAL_MUTEX_init(&handle->cred_id_mutex); 145 146 major = GSS_S_FAILURE; 147 148 *minor_status = krb5_copy_principal(context, cred->principal, 149 &handle->principal); 150 if (*minor_status) { 151 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 152 free(handle); 153 return GSS_S_FAILURE; 154 } 155 156 if (cred->keytab) { 157 char *name = NULL; 158 159 *minor_status = krb5_kt_get_full_name(context, cred->keytab, 160 &name); 161 if (*minor_status) 162 goto failure; 163 164 *minor_status = krb5_kt_resolve(context, name, &handle->keytab); 165 krb5_xfree(name); 166 if (*minor_status) 167 goto failure; 168 } 169 170 if (cred->ccache) { 171 const char *type, *name; 172 char *type_name = NULL; 173 174 type = krb5_cc_get_type(context, cred->ccache); 175 if (type == NULL){ 176 *minor_status = ENOMEM; 177 goto failure; 178 } 179 180 if (strcmp(type, "MEMORY") == 0) { 181 *minor_status = krb5_cc_new_unique(context, type, 182 NULL, &handle->ccache); 183 if (*minor_status) 184 goto failure; 185 186 *minor_status = krb5_cc_copy_cache(context, cred->ccache, 187 handle->ccache); 188 if (*minor_status) 189 goto failure; 190 191 } else { 192 name = krb5_cc_get_name(context, cred->ccache); 193 if (name == NULL) { 194 *minor_status = ENOMEM; 195 goto failure; 196 } 197 198 if (asprintf(&type_name, "%s:%s", type, name) == -1 || 199 type_name == NULL) { 200 *minor_status = ENOMEM; 201 goto failure; 202 } 203 204 *minor_status = krb5_cc_resolve(context, type_name, 205 &handle->ccache); 206 free(type_name); 207 if (*minor_status) 208 goto failure; 209 } 210 } 211 major = gss_create_empty_oid_set(minor_status, &handle->mechanisms); 212 if (major != GSS_S_COMPLETE) 213 goto failure; 214 215 major = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM, 216 &handle->mechanisms); 217 if (major != GSS_S_COMPLETE) 218 goto failure; 219 220 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 221 222 major = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)cred, 223 NULL, &lifetime, NULL, actual_mechs); 224 if (major != GSS_S_COMPLETE) 225 goto failure; 226 227 *output_cred_handle = (gss_cred_id_t)handle; 228 } 229 230 if (initiator_time_rec) 231 *initiator_time_rec = lifetime; 232 if (acceptor_time_rec) 233 *acceptor_time_rec = lifetime; 234 235 *minor_status = 0; 236 return major; 237 238 failure: 239 if (handle) { 240 if (handle->principal) 241 krb5_free_principal(context, handle->principal); 242 if (handle->keytab) 243 krb5_kt_close(context, handle->keytab); 244 if (handle->ccache) 245 krb5_cc_destroy(context, handle->ccache); 246 if (handle->mechanisms) 247 gss_release_oid_set(NULL, &handle->mechanisms); 248 free(handle); 249 } 250 if (cred && output_cred_handle) 251 HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); 252 return major; 253 } 254