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