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