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_priv.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_priv()
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 
45 Parses a KRB_PRIV message from inbuf, placing the confidential user
46 data in *outbuf.
47 
48 key specifies the key to be used for decryption of the message.
49 
50 remote_addr and local_addr specify the full
51 addresses (host and port) of the sender and receiver.
52 
53 outbuf points to allocated storage which the caller should
54 free when finished.
55 
56 i_vector is used as an initialization vector for the
57 encryption, and if non-NULL its contents are replaced with the last
58 block of the encrypted data upon exit.
59 
60 Returns system errors, integrity errors.
61 
62 */
63 
64 static krb5_error_code
65 krb5_rd_priv_basic(
66     krb5_context 	  context,
67     const krb5_data     * inbuf,
68     const krb5_keyblock * keyblock,
69     const krb5_address  * local_addr,
70     const krb5_address  * remote_addr,
71     krb5_pointer 	  i_vector,
72     krb5_replay_data    * replaydata,
73     krb5_data 		* outbuf)
74 {
75     krb5_error_code 	  retval;
76     krb5_priv 		* privmsg;
77     krb5_data 		  scratch;
78     krb5_priv_enc_part  * privmsg_enc_part;
79     size_t		  blocksize;
80     krb5_data		  ivdata;
81 
82     if (!krb5_is_krb_priv(inbuf))
83 	return KRB5KRB_AP_ERR_MSG_TYPE;
84 
85     /* decode private message */
86     if ((retval = decode_krb5_priv(inbuf, &privmsg)))
87 	return retval;
88 
89     if (i_vector) {
90 	if ((retval = krb5_c_block_size(context, keyblock->enctype,
91 					&blocksize)))
92 	    goto cleanup_privmsg;
93 
94 	ivdata.length = blocksize;
95 	ivdata.data = i_vector;
96     }
97 
98     scratch.length = privmsg->enc_part.ciphertext.length;
99     if (!(scratch.data = malloc(scratch.length))) {
100 	retval = ENOMEM;
101 	goto cleanup_privmsg;
102     }
103 
104     if ((retval = krb5_c_decrypt(context, keyblock,
105 				 KRB5_KEYUSAGE_KRB_PRIV_ENCPART,
106 				 i_vector?&ivdata:0,
107 				 &privmsg->enc_part, &scratch)))
108 	goto cleanup_scratch;
109 
110     /*  now decode the decrypted stuff */
111     if ((retval = decode_krb5_enc_priv_part(&scratch, &privmsg_enc_part)))
112         goto cleanup_scratch;
113 
114     if (!krb5_address_compare(context,remote_addr,privmsg_enc_part->s_address)){
115 	retval = KRB5KRB_AP_ERR_BADADDR;
116 	goto cleanup_data;
117     }
118 
119     if (privmsg_enc_part->r_address) {
120 	if (local_addr) {
121 	    if (!krb5_address_compare(context, local_addr,
122 				      privmsg_enc_part->r_address)) {
123 		retval = KRB5KRB_AP_ERR_BADADDR;
124 		goto cleanup_data;
125 	    }
126 	} else {
127 	    krb5_address **our_addrs;
128 
129 	    if ((retval = krb5_os_localaddr(context, &our_addrs))) {
130 		goto cleanup_data;
131 	    }
132 	    if (!krb5_address_search(context, privmsg_enc_part->r_address,
133 				     our_addrs)) {
134 		krb5_free_addresses(context, our_addrs);
135 		retval =  KRB5KRB_AP_ERR_BADADDR;
136 		goto cleanup_data;
137 	    }
138 	    krb5_free_addresses(context, our_addrs);
139 	}
140     }
141 
142     replaydata->timestamp = privmsg_enc_part->timestamp;
143     replaydata->usec = privmsg_enc_part->usec;
144     replaydata->seq = privmsg_enc_part->seq_number;
145 
146     /* everything is ok - return data to the user */
147     *outbuf = privmsg_enc_part->user_data;
148     retval = 0;
149 
150 cleanup_data:;
151     if (retval == 0)
152 	privmsg_enc_part->user_data.data = 0;
153     krb5_free_priv_enc_part(context, privmsg_enc_part);
154 
155 cleanup_scratch:;
156     (void) memset(scratch.data, 0, scratch.length);
157     krb5_xfree(scratch.data);
158 
159 cleanup_privmsg:;
160     krb5_xfree(privmsg->enc_part.ciphertext.data);
161     krb5_xfree(privmsg);
162 
163     return retval;
164 }
165 
166 krb5_error_code KRB5_CALLCONV
167 krb5_rd_priv(
168     krb5_context 	  context,
169     krb5_auth_context 	  auth_context,
170     const krb5_data   	* inbuf,
171     krb5_data 	      	* outbuf,
172     krb5_replay_data  	* outdata)
173 {
174     krb5_error_code 	  retval;
175     krb5_keyblock       * keyblock;
176     krb5_replay_data	  replaydata;
177 
178     /* Get keyblock */
179     if ((keyblock = auth_context->recv_subkey) == NULL)
180 	keyblock = auth_context->keyblock;
181 
182     if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
183       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
184       (outdata == NULL))
185 	/* Need a better error */
186 	return KRB5_RC_REQUIRED;
187 
188     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
189       (auth_context->rcache == NULL))
190 	return KRB5_RC_REQUIRED;
191 
192 {
193     krb5_address * premote_fulladdr = NULL;
194     krb5_address * plocal_fulladdr = NULL;
195     krb5_address remote_fulladdr;
196     krb5_address local_fulladdr;
197     CLEANUP_INIT(2);
198 
199     if (auth_context->local_addr) {
200     	if (auth_context->local_port) {
201             if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr,
202                                  	      auth_context->local_port,
203 					      &local_fulladdr))){
204                 CLEANUP_PUSH(local_fulladdr.contents, free);
205 	        plocal_fulladdr = &local_fulladdr;
206             } else {
207 	        return retval;
208             }
209 	} else {
210             plocal_fulladdr = auth_context->local_addr;
211         }
212     }
213 
214     if (auth_context->remote_addr) {
215     	if (auth_context->remote_port) {
216             if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr,
217                                  	      auth_context->remote_port,
218 					      &remote_fulladdr))){
219                 CLEANUP_PUSH(remote_fulladdr.contents, free);
220 	        premote_fulladdr = &remote_fulladdr;
221             } else {
222                 CLEANUP_DONE();
223 	        return retval;
224             }
225 	} else {
226             premote_fulladdr = auth_context->remote_addr;
227         }
228     }
229 
230     if ((retval = krb5_rd_priv_basic(context, inbuf, keyblock,
231 				     plocal_fulladdr,
232 				     premote_fulladdr,
233 				     auth_context->i_vector,
234 				     &replaydata, outbuf))) {
235 	CLEANUP_DONE();
236 	return retval;
237     }
238 
239     CLEANUP_DONE();
240 }
241 
242     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
243 	krb5_donot_replay replay;
244     	krb5_timestamp currenttime;
245 
246 	if ((retval = krb5_timeofday(context, &currenttime)))
247 	    goto error;
248 
249 	if (!in_clock_skew(replaydata.timestamp)) {
250 	    retval =  KRB5KRB_AP_ERR_SKEW;
251 	    goto error;
252 	}
253 
254 	if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
255 					   "_priv", &replay.client)))
256 	    goto error;
257 
258 	replay.server = "";		/* XXX */
259 	replay.cusec = replaydata.usec;
260 	replay.ctime = replaydata.timestamp;
261 	retval = krb5_rc_store(context, auth_context->rcache, &replay);
262 	if (retval) {
263 	    krb5_xfree(replay.client);
264 	    goto error;
265 	}
266 	krb5_xfree(replay.client);
267     }
268 
269     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
270 	if (!krb5int_auth_con_chkseqnum(context, auth_context,
271 					replaydata.seq)) {
272 	    retval =  KRB5KRB_AP_ERR_BADORDER;
273 	    goto error;
274 	}
275 	auth_context->remote_seq_number++;
276     }
277 
278     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
279       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
280 	outdata->timestamp = replaydata.timestamp;
281 	outdata->usec = replaydata.usec;
282 	outdata->seq = replaydata.seq;
283     }
284 
285     /* everything is ok - return data to the user */
286     return 0;
287 
288 error:;
289     krb5_xfree(outbuf->data);
290     return retval;
291 
292 }
293