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 static krb5_error_code 37 compare_addrs(krb5_context context, 38 krb5_address *a, 39 krb5_address *b, 40 const char *message) 41 { 42 char a_str[64], b_str[64]; 43 size_t len; 44 45 if(krb5_address_compare (context, a, b)) 46 return 0; 47 48 krb5_print_address (a, a_str, sizeof(a_str), &len); 49 krb5_print_address (b, b_str, sizeof(b_str), &len); 50 krb5_set_error_message(context, KRB5KRB_AP_ERR_BADADDR, 51 "%s: %s != %s", message, b_str, a_str); 52 return KRB5KRB_AP_ERR_BADADDR; 53 } 54 55 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 56 krb5_rd_cred(krb5_context context, 57 krb5_auth_context auth_context, 58 krb5_data *in_data, 59 krb5_creds ***ret_creds, 60 krb5_replay_data *outdata) 61 { 62 krb5_error_code ret; 63 size_t len; 64 KRB_CRED cred; 65 EncKrbCredPart enc_krb_cred_part; 66 krb5_data enc_krb_cred_part_data; 67 krb5_crypto crypto; 68 size_t i; 69 70 memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); 71 krb5_data_zero(&enc_krb_cred_part_data); 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_message(context); 84 return ret; 85 } 86 87 if (cred.pvno != 5) { 88 ret = KRB5KRB_AP_ERR_BADVERSION; 89 krb5_clear_error_message (context); 90 goto out; 91 } 92 93 if (cred.msg_type != krb_cred) { 94 ret = KRB5KRB_AP_ERR_MSG_TYPE; 95 krb5_clear_error_message (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 = decode_EncKrbCredPart(enc_krb_cred_part_data.data, 152 enc_krb_cred_part_data.length, 153 &enc_krb_cred_part, 154 &len); 155 if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data) 156 krb5_data_free(&enc_krb_cred_part_data); 157 if (ret) { 158 krb5_set_error_message(context, ret, 159 N_("Failed to decode " 160 "encrypte credential part", "")); 161 goto out; 162 } 163 164 /* check sender address */ 165 166 if (enc_krb_cred_part.s_address 167 && auth_context->remote_address 168 && auth_context->remote_port) { 169 krb5_address *a; 170 171 ret = krb5_make_addrport (context, &a, 172 auth_context->remote_address, 173 auth_context->remote_port); 174 if (ret) 175 goto out; 176 177 178 ret = compare_addrs(context, a, enc_krb_cred_part.s_address, 179 N_("sender address is wrong " 180 "in received creds", "")); 181 krb5_free_address(context, a); 182 free(a); 183 if(ret) 184 goto out; 185 } 186 187 /* check receiver address */ 188 189 if (enc_krb_cred_part.r_address 190 && auth_context->local_address) { 191 if(auth_context->local_port && 192 enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) { 193 krb5_address *a; 194 ret = krb5_make_addrport (context, &a, 195 auth_context->local_address, 196 auth_context->local_port); 197 if (ret) 198 goto out; 199 200 ret = compare_addrs(context, a, enc_krb_cred_part.r_address, 201 N_("receiver address is wrong " 202 "in received creds", "")); 203 krb5_free_address(context, a); 204 free(a); 205 if(ret) 206 goto out; 207 } else { 208 ret = compare_addrs(context, auth_context->local_address, 209 enc_krb_cred_part.r_address, 210 N_("receiver address is wrong " 211 "in received creds", "")); 212 if(ret) 213 goto out; 214 } 215 } 216 217 /* check timestamp */ 218 if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { 219 krb5_timestamp sec; 220 221 krb5_timeofday (context, &sec); 222 223 if (enc_krb_cred_part.timestamp == NULL || 224 enc_krb_cred_part.usec == NULL || 225 abs(*enc_krb_cred_part.timestamp - sec) 226 > context->max_skew) { 227 krb5_clear_error_message (context); 228 ret = KRB5KRB_AP_ERR_SKEW; 229 goto out; 230 } 231 } 232 233 if ((auth_context->flags & 234 (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) { 235 /* if these fields are not present in the cred-part, silently 236 return zero */ 237 memset(outdata, 0, sizeof(*outdata)); 238 if(enc_krb_cred_part.timestamp) 239 outdata->timestamp = *enc_krb_cred_part.timestamp; 240 if(enc_krb_cred_part.usec) 241 outdata->usec = *enc_krb_cred_part.usec; 242 if(enc_krb_cred_part.nonce) 243 outdata->seq = *enc_krb_cred_part.nonce; 244 } 245 246 /* Convert to NULL terminated list of creds */ 247 248 *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1, 249 sizeof(**ret_creds)); 250 251 if (*ret_creds == NULL) { 252 ret = ENOMEM; 253 krb5_set_error_message(context, ret, 254 N_("malloc: out of memory", "")); 255 goto out; 256 } 257 258 for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) { 259 KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i]; 260 krb5_creds *creds; 261 262 creds = calloc(1, sizeof(*creds)); 263 if(creds == NULL) { 264 ret = ENOMEM; 265 krb5_set_error_message(context, ret, 266 N_("malloc: out of memory", "")); 267 goto out; 268 } 269 270 ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length, 271 &cred.tickets.val[i], &len, ret); 272 if (ret) { 273 free(creds); 274 goto out; 275 } 276 if(creds->ticket.length != len) 277 krb5_abortx(context, "internal error in ASN.1 encoder"); 278 copy_EncryptionKey (&kci->key, &creds->session); 279 if (kci->prealm && kci->pname) 280 _krb5_principalname2krb5_principal (context, 281 &creds->client, 282 *kci->pname, 283 *kci->prealm); 284 if (kci->flags) 285 creds->flags.b = *kci->flags; 286 if (kci->authtime) 287 creds->times.authtime = *kci->authtime; 288 if (kci->starttime) 289 creds->times.starttime = *kci->starttime; 290 if (kci->endtime) 291 creds->times.endtime = *kci->endtime; 292 if (kci->renew_till) 293 creds->times.renew_till = *kci->renew_till; 294 if (kci->srealm && kci->sname) 295 _krb5_principalname2krb5_principal (context, 296 &creds->server, 297 *kci->sname, 298 *kci->srealm); 299 if (kci->caddr) 300 krb5_copy_addresses (context, 301 kci->caddr, 302 &creds->addresses); 303 304 (*ret_creds)[i] = creds; 305 306 } 307 (*ret_creds)[i] = NULL; 308 309 free_KRB_CRED (&cred); 310 free_EncKrbCredPart(&enc_krb_cred_part); 311 312 return 0; 313 314 out: 315 free_EncKrbCredPart(&enc_krb_cred_part); 316 free_KRB_CRED (&cred); 317 if(*ret_creds) { 318 for(i = 0; (*ret_creds)[i]; i++) 319 krb5_free_creds(context, (*ret_creds)[i]); 320 free(*ret_creds); 321 *ret_creds = NULL; 322 } 323 return ret; 324 } 325 326 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 327 krb5_rd_cred2 (krb5_context context, 328 krb5_auth_context auth_context, 329 krb5_ccache ccache, 330 krb5_data *in_data) 331 { 332 krb5_error_code ret; 333 krb5_creds **creds; 334 int i; 335 336 ret = krb5_rd_cred(context, auth_context, in_data, &creds, NULL); 337 if(ret) 338 return ret; 339 340 /* Store the creds in the ccache */ 341 342 for(i = 0; creds && creds[i]; i++) { 343 krb5_cc_store_cred(context, ccache, creds[i]); 344 krb5_free_creds(context, creds[i]); 345 } 346 free(creds); 347 return 0; 348 } 349