1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * lib/krb5/krb/rd_safe.c 9 * 10 * Copyright 1990,1991 by the Massachusetts Institute of Technology. 11 * All Rights Reserved. 12 * 13 * Export of this software from the United States of America may 14 * require a specific license from the United States Government. 15 * It is the responsibility of any person or organization contemplating 16 * export to obtain such a license before exporting. 17 * 18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19 * distribute this software and its documentation for any purpose and 20 * without fee is hereby granted, provided that the above copyright 21 * notice appear in all copies and that both that copyright notice and 22 * this permission notice appear in supporting documentation, and that 23 * the name of M.I.T. not be used in advertising or publicity pertaining 24 * to distribution of the software without specific, written prior 25 * permission. Furthermore if you modify this software you must label 26 * your software as modified software and not distribute it in such a 27 * fashion that it might be confused with the original M.I.T. software. 28 * M.I.T. makes no representations about the suitability of 29 * this software for any purpose. It is provided "as is" without express 30 * or implied warranty. 31 * 32 * 33 * krb5_rd_safe() 34 */ 35 36 #include "k5-int.h" 37 #include "cleanup.h" 38 #include "auth_con.h" 39 40 #define in_clock_skew(date) (labs((date)-currenttime) < context->clockskew) 41 42 /* 43 parses a KRB_SAFE message from inbuf, placing the integrity-protected user 44 data in *outbuf. 45 46 key specifies the key to be used for decryption of the message. 47 48 sender_addr and recv_addr specify the full addresses (host and port) of 49 the sender and receiver. 50 51 outbuf points to allocated storage which the caller should free when finished. 52 53 returns system errors, integrity errors 54 */ 55 static krb5_error_code 56 krb5_rd_safe_basic(krb5_context context, const krb5_data *inbuf, const krb5_keyblock *keyblock, const krb5_address *recv_addr, const krb5_address *sender_addr, krb5_replay_data *replaydata, krb5_data *outbuf) 57 { 58 krb5_error_code retval; 59 krb5_safe * message; 60 krb5_data safe_body; 61 krb5_checksum our_cksum, *his_cksum; 62 krb5_octet zero_octet = 0; 63 krb5_data *scratch; 64 krb5_boolean valid; 65 66 /* Solaris Kerberos */ 67 KRB5_LOG0(KRB5_INFO, "krb5_rd_safe_basic() start"); 68 69 if (!krb5_is_krb_safe(inbuf)) { 70 /* Solaris Kerberos */ 71 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() end, error retval=%d", 72 KRB5KRB_AP_ERR_MSG_TYPE); 73 return KRB5KRB_AP_ERR_MSG_TYPE; 74 } 75 76 if ((retval = decode_krb5_safe_with_body(inbuf, &message, &safe_body))) { 77 /* Solaris Kerberos */ 78 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() end, error retval=%d", 79 retval); 80 return retval; 81 } 82 83 if (!krb5_c_valid_cksumtype(message->checksum->checksum_type)) { 84 retval = KRB5_PROG_SUMTYPE_NOSUPP; 85 /* Solaris Kerberos */ 86 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() error retval=%d", 87 retval); 88 goto cleanup; 89 } 90 if (!krb5_c_is_coll_proof_cksum(message->checksum->checksum_type) || 91 !krb5_c_is_keyed_cksum(message->checksum->checksum_type)) { 92 retval = KRB5KRB_AP_ERR_INAPP_CKSUM; 93 /* Solaris Kerberos */ 94 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() error retval=%d", 95 retval); 96 goto cleanup; 97 } 98 99 if (!krb5_address_compare(context, sender_addr, message->s_address)) { 100 retval = KRB5KRB_AP_ERR_BADADDR; 101 /* Solaris Kerberos */ 102 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() error retval=%d", 103 retval); 104 goto cleanup; 105 } 106 107 if (message->r_address) { 108 if (recv_addr) { 109 if (!krb5_address_compare(context, recv_addr, message->r_address)) { 110 retval = KRB5KRB_AP_ERR_BADADDR; 111 /* Solaris Kerberos */ 112 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() error retval=%d", 113 retval); 114 goto cleanup; 115 } 116 } else { 117 krb5_address **our_addrs; 118 119 if ((retval = krb5_os_localaddr(context, &our_addrs))) { 120 /* Solaris Kerberos */ 121 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() error retval=%d", 122 retval); 123 goto cleanup; 124 } 125 if (!krb5_address_search(context, message->r_address, our_addrs)) { 126 krb5_free_addresses(context, our_addrs); 127 retval = KRB5KRB_AP_ERR_BADADDR; 128 /* Solaris Kerberos */ 129 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() error retval=%d", 130 retval); 131 goto cleanup; 132 } 133 krb5_free_addresses(context, our_addrs); 134 } 135 } 136 137 /* verify the checksum */ 138 /* 139 * In order to recreate what was checksummed, we regenerate the message 140 * without checksum and then have the cryptographic subsystem verify 141 * the checksum for us. This is because some checksum methods have 142 * a confounder encrypted as part of the checksum. 143 */ 144 his_cksum = message->checksum; 145 146 our_cksum.length = 0; 147 our_cksum.checksum_type = 0; 148 our_cksum.contents = &zero_octet; 149 150 message->checksum = &our_cksum; 151 152 if ((retval = encode_krb5_safe_with_body(message, &safe_body, &scratch))) { 153 /* Solaris Kerberos */ 154 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() error retval=%d", 155 retval); 156 goto cleanup; 157 } 158 159 message->checksum = his_cksum; 160 161 retval = krb5_c_verify_checksum(context, keyblock, 162 KRB5_KEYUSAGE_KRB_SAFE_CKSUM, 163 scratch, his_cksum, &valid); 164 165 (void) memset((char *)scratch->data, 0, scratch->length); 166 krb5_free_data(context, scratch); 167 168 if (!valid) { 169 /* 170 * Checksum over only the KRB-SAFE-BODY, like RFC 1510 says, in 171 * case someone actually implements it correctly. 172 */ 173 retval = krb5_c_verify_checksum(context, keyblock, 174 KRB5_KEYUSAGE_KRB_SAFE_CKSUM, 175 &safe_body, his_cksum, &valid); 176 if (!valid) { 177 retval = KRB5KRB_AP_ERR_MODIFIED; 178 /* Solaris Kerberos */ 179 KRB5_LOG(KRB5_ERR, "krb5_rd_safe_basic() error retval=%d", 180 retval); 181 goto cleanup; 182 } 183 } 184 185 replaydata->timestamp = message->timestamp; 186 replaydata->usec = message->usec; 187 replaydata->seq = message->seq_number; 188 189 *outbuf = message->user_data; 190 message->user_data.data = NULL; 191 retval = 0; 192 193 cleanup: 194 krb5_free_safe(context, message); 195 /* Solaris Kerberos */ 196 KRB5_LOG(KRB5_INFO, "krb5_rd_safe_basic() end, retval=%d", 197 retval); 198 return retval; 199 } 200 201 krb5_error_code KRB5_CALLCONV 202 krb5_rd_safe(krb5_context context, krb5_auth_context auth_context, const krb5_data *inbuf, krb5_data *outbuf, krb5_replay_data *outdata) 203 { 204 krb5_error_code retval; 205 krb5_keyblock * keyblock; 206 krb5_replay_data replaydata; 207 208 if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || 209 (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) && 210 (outdata == NULL)) 211 /* Need a better error */ 212 return KRB5_RC_REQUIRED; 213 214 if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) && 215 (auth_context->rcache == NULL)) 216 return KRB5_RC_REQUIRED; 217 218 /* Get keyblock */ 219 if ((keyblock = auth_context->recv_subkey) == NULL) 220 keyblock = auth_context->keyblock; 221 222 { 223 krb5_address * premote_fulladdr = NULL; 224 krb5_address * plocal_fulladdr = NULL; 225 krb5_address remote_fulladdr; 226 krb5_address local_fulladdr; 227 CLEANUP_INIT(2); 228 229 if (auth_context->local_addr) { 230 if (auth_context->local_port) { 231 if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr, 232 auth_context->local_port, 233 &local_fulladdr))){ 234 CLEANUP_PUSH(local_fulladdr.contents, free); 235 plocal_fulladdr = &local_fulladdr; 236 } else { 237 return retval; 238 } 239 } else { 240 plocal_fulladdr = auth_context->local_addr; 241 } 242 } 243 244 if (auth_context->remote_addr) { 245 if (auth_context->remote_port) { 246 if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr, 247 auth_context->remote_port, 248 &remote_fulladdr))){ 249 CLEANUP_PUSH(remote_fulladdr.contents, free); 250 premote_fulladdr = &remote_fulladdr; 251 } else { 252 return retval; 253 } 254 } else { 255 premote_fulladdr = auth_context->remote_addr; 256 } 257 } 258 259 if ((retval = krb5_rd_safe_basic(context, inbuf, keyblock, 260 plocal_fulladdr, premote_fulladdr, 261 &replaydata, outbuf))) { 262 CLEANUP_DONE(); 263 return retval; 264 } 265 266 CLEANUP_DONE(); 267 } 268 269 270 if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) { 271 krb5_donot_replay replay; 272 krb5_timestamp currenttime; 273 274 if ((retval = krb5_timeofday(context, ¤ttime))) 275 goto error; 276 277 if (!in_clock_skew(replaydata.timestamp)) { 278 retval = KRB5KRB_AP_ERR_SKEW; 279 goto error; 280 } 281 282 if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr, 283 "_safe", &replay.client))) 284 goto error; 285 286 replay.server = ""; /* XXX */ 287 replay.cusec = replaydata.usec; 288 replay.ctime = replaydata.timestamp; 289 if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) { 290 krb5_xfree(replay.client); 291 goto error; 292 } 293 krb5_xfree(replay.client); 294 } 295 296 if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { 297 if (!krb5int_auth_con_chkseqnum(context, auth_context, 298 replaydata.seq)) { 299 retval = KRB5KRB_AP_ERR_BADORDER; 300 goto error; 301 } 302 auth_context->remote_seq_number++; 303 } 304 305 if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) || 306 (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) { 307 outdata->timestamp = replaydata.timestamp; 308 outdata->usec = replaydata.usec; 309 outdata->seq = replaydata.seq; 310 } 311 312 /* everything is ok - return data to the user */ 313 return 0; 314 315 error: 316 krb5_xfree(outbuf->data); 317 return retval; 318 319 } 320 321