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