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
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)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
krb5_rd_safe(krb5_context context,krb5_auth_context auth_context,const krb5_data * inbuf,krb5_data * outbuf,krb5_replay_data * outdata)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