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