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