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