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, &currenttime)))
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