1 /* 2 * Copyright (c) 1997 - 2007 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_locl.h> 35 36 RCSID("$Id: rd_cred.c 20304 2007-04-11 11:15:05Z lha $"); 37 38 static krb5_error_code 39 compare_addrs(krb5_context context, 40 krb5_address *a, 41 krb5_address *b, 42 const char *message) 43 { 44 char a_str[64], b_str[64]; 45 size_t len; 46 47 if(krb5_address_compare (context, a, b)) 48 return 0; 49 50 krb5_print_address (a, a_str, sizeof(a_str), &len); 51 krb5_print_address (b, b_str, sizeof(b_str), &len); 52 krb5_set_error_string(context, "%s: %s != %s", message, b_str, a_str); 53 return KRB5KRB_AP_ERR_BADADDR; 54 } 55 56 krb5_error_code KRB5_LIB_FUNCTION 57 krb5_rd_cred(krb5_context context, 58 krb5_auth_context auth_context, 59 krb5_data *in_data, 60 krb5_creds ***ret_creds, 61 krb5_replay_data *outdata) 62 { 63 krb5_error_code ret; 64 size_t len; 65 KRB_CRED cred; 66 EncKrbCredPart enc_krb_cred_part; 67 krb5_data enc_krb_cred_part_data; 68 krb5_crypto crypto; 69 int i; 70 71 memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); 72 73 if ((auth_context->flags & 74 (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && 75 outdata == NULL) 76 return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */ 77 78 *ret_creds = NULL; 79 80 ret = decode_KRB_CRED(in_data->data, in_data->length, 81 &cred, &len); 82 if(ret) { 83 krb5_clear_error_string(context); 84 return ret; 85 } 86 87 if (cred.pvno != 5) { 88 ret = KRB5KRB_AP_ERR_BADVERSION; 89 krb5_clear_error_string (context); 90 goto out; 91 } 92 93 if (cred.msg_type != krb_cred) { 94 ret = KRB5KRB_AP_ERR_MSG_TYPE; 95 krb5_clear_error_string (context); 96 goto out; 97 } 98 99 if (cred.enc_part.etype == ETYPE_NULL) { 100 /* DK: MIT GSS-API Compatibility */ 101 enc_krb_cred_part_data.length = cred.enc_part.cipher.length; 102 enc_krb_cred_part_data.data = cred.enc_part.cipher.data; 103 } else { 104 /* Try both subkey and session key. 105 * 106 * RFC4120 claims we should use the session key, but Heimdal 107 * before 0.8 used the remote subkey if it was send in the 108 * auth_context. 109 */ 110 111 if (auth_context->remote_subkey) { 112 ret = krb5_crypto_init(context, auth_context->remote_subkey, 113 0, &crypto); 114 if (ret) 115 goto out; 116 117 ret = krb5_decrypt_EncryptedData(context, 118 crypto, 119 KRB5_KU_KRB_CRED, 120 &cred.enc_part, 121 &enc_krb_cred_part_data); 122 123 krb5_crypto_destroy(context, crypto); 124 } 125 126 /* 127 * If there was not subkey, or we failed using subkey, 128 * retry using the session key 129 */ 130 if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) 131 { 132 133 ret = krb5_crypto_init(context, auth_context->keyblock, 134 0, &crypto); 135 136 if (ret) 137 goto out; 138 139 ret = krb5_decrypt_EncryptedData(context, 140 crypto, 141 KRB5_KU_KRB_CRED, 142 &cred.enc_part, 143 &enc_krb_cred_part_data); 144 145 krb5_crypto_destroy(context, crypto); 146 } 147 if (ret) 148 goto out; 149 } 150 151 ret = krb5_decode_EncKrbCredPart (context, 152 enc_krb_cred_part_data.data, 153 enc_krb_cred_part_data.length, 154 &enc_krb_cred_part, 155 &len); 156 if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data) 157 krb5_data_free(&enc_krb_cred_part_data); 158 if (ret) 159 goto out; 160 161 /* check sender address */ 162 163 if (enc_krb_cred_part.s_address 164 && auth_context->remote_address 165 && auth_context->remote_port) { 166 krb5_address *a; 167 168 ret = krb5_make_addrport (context, &a, 169 auth_context->remote_address, 170 auth_context->remote_port); 171 if (ret) 172 goto out; 173 174 175 ret = compare_addrs(context, a, enc_krb_cred_part.s_address, 176 "sender address is wrong in received creds"); 177 krb5_free_address(context, a); 178 free(a); 179 if(ret) 180 goto out; 181 } 182 183 /* check receiver address */ 184 185 if (enc_krb_cred_part.r_address 186 && auth_context->local_address) { 187 if(auth_context->local_port && 188 enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) { 189 krb5_address *a; 190 ret = krb5_make_addrport (context, &a, 191 auth_context->local_address, 192 auth_context->local_port); 193 if (ret) 194 goto out; 195 196 ret = compare_addrs(context, a, enc_krb_cred_part.r_address, 197 "receiver address is wrong in received creds"); 198 krb5_free_address(context, a); 199 free(a); 200 if(ret) 201 goto out; 202 } else { 203 ret = compare_addrs(context, auth_context->local_address, 204 enc_krb_cred_part.r_address, 205 "receiver address is wrong in received creds"); 206 if(ret) 207 goto out; 208 } 209 } 210 211 /* check timestamp */ 212 if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { 213 krb5_timestamp sec; 214 215 krb5_timeofday (context, &sec); 216 217 if (enc_krb_cred_part.timestamp == NULL || 218 enc_krb_cred_part.usec == NULL || 219 abs(*enc_krb_cred_part.timestamp - sec) 220 > context->max_skew) { 221 krb5_clear_error_string (context); 222 ret = KRB5KRB_AP_ERR_SKEW; 223 goto out; 224 } 225 } 226 227 if ((auth_context->flags & 228 (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) { 229 /* if these fields are not present in the cred-part, silently 230 return zero */ 231 memset(outdata, 0, sizeof(*outdata)); 232 if(enc_krb_cred_part.timestamp) 233 outdata->timestamp = *enc_krb_cred_part.timestamp; 234 if(enc_krb_cred_part.usec) 235 outdata->usec = *enc_krb_cred_part.usec; 236 if(enc_krb_cred_part.nonce) 237 outdata->seq = *enc_krb_cred_part.nonce; 238 } 239 240 /* Convert to NULL terminated list of creds */ 241 242 *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1, 243 sizeof(**ret_creds)); 244 245 if (*ret_creds == NULL) { 246 ret = ENOMEM; 247 krb5_set_error_string (context, "malloc: out of memory"); 248 goto out; 249 } 250 251 for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) { 252 KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i]; 253 krb5_creds *creds; 254 255 creds = calloc(1, sizeof(*creds)); 256 if(creds == NULL) { 257 ret = ENOMEM; 258 krb5_set_error_string (context, "malloc: out of memory"); 259 goto out; 260 } 261 262 ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, 263 &cred.tickets.val[i], &len, ret); 264 if (ret) { 265 free(creds); 266 goto out; 267 } 268 if(creds->ticket.length != len) 269 krb5_abortx(context, "internal error in ASN.1 encoder"); 270 copy_EncryptionKey (&kci->key, &creds->session); 271 if (kci->prealm && kci->pname) 272 _krb5_principalname2krb5_principal (context, 273 &creds->client, 274 *kci->pname, 275 *kci->prealm); 276 if (kci->flags) 277 creds->flags.b = *kci->flags; 278 if (kci->authtime) 279 creds->times.authtime = *kci->authtime; 280 if (kci->starttime) 281 creds->times.starttime = *kci->starttime; 282 if (kci->endtime) 283 creds->times.endtime = *kci->endtime; 284 if (kci->renew_till) 285 creds->times.renew_till = *kci->renew_till; 286 if (kci->srealm && kci->sname) 287 _krb5_principalname2krb5_principal (context, 288 &creds->server, 289 *kci->sname, 290 *kci->srealm); 291 if (kci->caddr) 292 krb5_copy_addresses (context, 293 kci->caddr, 294 &creds->addresses); 295 296 (*ret_creds)[i] = creds; 297 298 } 299 (*ret_creds)[i] = NULL; 300 301 free_KRB_CRED (&cred); 302 free_EncKrbCredPart(&enc_krb_cred_part); 303 304 return 0; 305 306 out: 307 free_EncKrbCredPart(&enc_krb_cred_part); 308 free_KRB_CRED (&cred); 309 if(*ret_creds) { 310 for(i = 0; (*ret_creds)[i]; i++) 311 krb5_free_creds(context, (*ret_creds)[i]); 312 free(*ret_creds); 313 *ret_creds = NULL; 314 } 315 return ret; 316 } 317 318 krb5_error_code KRB5_LIB_FUNCTION 319 krb5_rd_cred2 (krb5_context context, 320 krb5_auth_context auth_context, 321 krb5_ccache ccache, 322 krb5_data *in_data) 323 { 324 krb5_error_code ret; 325 krb5_creds **creds; 326 int i; 327 328 ret = krb5_rd_cred(context, auth_context, in_data, &creds, NULL); 329 if(ret) 330 return ret; 331 332 /* Store the creds in the ccache */ 333 334 for(i = 0; creds && creds[i]; i++) { 335 krb5_cc_store_cred(context, ccache, creds[i]); 336 krb5_free_creds(context, creds[i]); 337 } 338 free(creds); 339 return 0; 340 } 341