1 /*
2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 /*
5  * lib/krb5/krb/rd_priv.c
6  *
7  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
8  * All Rights Reserved.
9  *
10  * Export of this software from the United States of America may
11  *   require a specific license from the United States Government.
12  *   It is the responsibility of any person or organization contemplating
13  *   export to obtain such a license before exporting.
14  *
15  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
16  * distribute this software and its documentation for any purpose and
17  * without fee is hereby granted, provided that the above copyright
18  * notice appear in all copies and that both that copyright notice and
19  * this permission notice appear in supporting documentation, and that
20  * the name of M.I.T. not be used in advertising or publicity pertaining
21  * to distribution of the software without specific, written prior
22  * permission.  Furthermore if you modify this software you must label
23  * your software as modified software and not distribute it in such a
24  * fashion that it might be confused with the original M.I.T. software.
25  * M.I.T. makes no representations about the suitability of
26  * this software for any purpose.  It is provided "as is" without express
27  * or implied warranty.
28  *
29  *
30  * krb5_rd_priv()
31  */
32 
33 #include "k5-int.h"
34 #include "cleanup.h"
35 #include "auth_con.h"
36 
37 
38 /*
39 
40 Parses a KRB_PRIV message from inbuf, placing the confidential user
41 data in *outbuf.
42 
43 key specifies the key to be used for decryption of the message.
44 
45 remote_addr and local_addr specify the full
46 addresses (host and port) of the sender and receiver.
47 
48 outbuf points to allocated storage which the caller should
49 free when finished.
50 
51 i_vector is used as an initialization vector for the
52 encryption, and if non-NULL its contents are replaced with the last
53 block of the encrypted data upon exit.
54 
55 Returns system errors, integrity errors.
56 
57 */
58 
59 static krb5_error_code
krb5_rd_priv_basic(krb5_context context,const krb5_data * inbuf,const krb5_keyblock * keyblock,const krb5_address * local_addr,const krb5_address * remote_addr,krb5_pointer i_vector,krb5_replay_data * replaydata,krb5_data * outbuf)60 krb5_rd_priv_basic(krb5_context context, const krb5_data *inbuf, const krb5_keyblock *keyblock, const krb5_address *local_addr, const krb5_address *remote_addr, krb5_pointer i_vector, krb5_replay_data *replaydata, krb5_data *outbuf)
61 {
62     krb5_error_code 	  retval;
63     krb5_priv 		* privmsg;
64     krb5_data 		  scratch;
65     krb5_priv_enc_part  * privmsg_enc_part;
66     size_t		  blocksize;
67     krb5_data		  ivdata;
68 
69     if (!krb5_is_krb_priv(inbuf))
70 	return KRB5KRB_AP_ERR_MSG_TYPE;
71 
72     /* decode private message */
73     if ((retval = decode_krb5_priv(inbuf, &privmsg)))
74 	return retval;
75 
76     if (i_vector) {
77 	if ((retval = krb5_c_block_size(context, keyblock->enctype,
78 					&blocksize)))
79 	    goto cleanup_privmsg;
80 
81 	ivdata.length = blocksize;
82 	ivdata.data = i_vector;
83     }
84 
85     scratch.length = privmsg->enc_part.ciphertext.length;
86     if (!(scratch.data = malloc(scratch.length))) {
87 	retval = ENOMEM;
88 	goto cleanup_privmsg;
89     }
90 
91     if ((retval = krb5_c_decrypt(context, keyblock,
92 				 KRB5_KEYUSAGE_KRB_PRIV_ENCPART,
93 				 i_vector?&ivdata:0,
94 				 &privmsg->enc_part, &scratch)))
95 	goto cleanup_scratch;
96 
97     /*  now decode the decrypted stuff */
98     if ((retval = decode_krb5_enc_priv_part(&scratch, &privmsg_enc_part)))
99         goto cleanup_scratch;
100 
101     if (!krb5_address_compare(context,remote_addr,privmsg_enc_part->s_address)){
102 	retval = KRB5KRB_AP_ERR_BADADDR;
103 	goto cleanup_data;
104     }
105 
106     if (privmsg_enc_part->r_address) {
107 	if (local_addr) {
108 	    if (!krb5_address_compare(context, local_addr,
109 				      privmsg_enc_part->r_address)) {
110 		retval = KRB5KRB_AP_ERR_BADADDR;
111 		goto cleanup_data;
112 	    }
113 	} else {
114 	    krb5_address **our_addrs;
115 
116 	    if ((retval = krb5_os_localaddr(context, &our_addrs))) {
117 		goto cleanup_data;
118 	    }
119 	    if (!krb5_address_search(context, privmsg_enc_part->r_address,
120 				     our_addrs)) {
121 		krb5_free_addresses(context, our_addrs);
122 		retval =  KRB5KRB_AP_ERR_BADADDR;
123 		goto cleanup_data;
124 	    }
125 	    krb5_free_addresses(context, our_addrs);
126 	}
127     }
128 
129     replaydata->timestamp = privmsg_enc_part->timestamp;
130     replaydata->usec = privmsg_enc_part->usec;
131     replaydata->seq = privmsg_enc_part->seq_number;
132 
133     /* everything is ok - return data to the user */
134     *outbuf = privmsg_enc_part->user_data;
135     retval = 0;
136 
137 cleanup_data:;
138     if (retval == 0)
139 	privmsg_enc_part->user_data.data = 0;
140     krb5_free_priv_enc_part(context, privmsg_enc_part);
141 
142 cleanup_scratch:;
143     /* Solaris Kerberos */
144     (void) memset(scratch.data, 0, scratch.length);
145     krb5_xfree(scratch.data);
146 
147 cleanup_privmsg:;
148     krb5_xfree(privmsg->enc_part.ciphertext.data);
149     krb5_xfree(privmsg);
150 
151     return retval;
152 }
153 
154 krb5_error_code KRB5_CALLCONV
krb5_rd_priv(krb5_context context,krb5_auth_context auth_context,const krb5_data * inbuf,krb5_data * outbuf,krb5_replay_data * outdata)155 krb5_rd_priv(krb5_context context, krb5_auth_context auth_context, const krb5_data *inbuf, krb5_data *outbuf, krb5_replay_data *outdata)
156 {
157     krb5_error_code 	  retval;
158     krb5_keyblock       * keyblock;
159     krb5_replay_data	  replaydata;
160 
161     /* Get keyblock */
162     if ((keyblock = auth_context->recv_subkey) == NULL)
163 	keyblock = auth_context->keyblock;
164 
165     if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
166       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
167       (outdata == NULL))
168 	/* Need a better error */
169 	return KRB5_RC_REQUIRED;
170 
171     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
172       (auth_context->rcache == NULL))
173 	return KRB5_RC_REQUIRED;
174 
175 {
176     krb5_address * premote_fulladdr = NULL;
177     krb5_address * plocal_fulladdr = NULL;
178     krb5_address remote_fulladdr;
179     krb5_address local_fulladdr;
180     CLEANUP_INIT(2);
181 
182     if (auth_context->local_addr) {
183     	if (auth_context->local_port) {
184             if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr,
185                                  	      auth_context->local_port,
186 					      &local_fulladdr))){
187                 CLEANUP_PUSH(local_fulladdr.contents, free);
188 	        plocal_fulladdr = &local_fulladdr;
189             } else {
190 	        return retval;
191             }
192 	} else {
193             plocal_fulladdr = auth_context->local_addr;
194         }
195     }
196 
197     if (auth_context->remote_addr) {
198     	if (auth_context->remote_port) {
199             if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr,
200                                  	      auth_context->remote_port,
201 					      &remote_fulladdr))){
202                 CLEANUP_PUSH(remote_fulladdr.contents, free);
203 	        premote_fulladdr = &remote_fulladdr;
204             } else {
205                 CLEANUP_DONE();
206 	        return retval;
207             }
208 	} else {
209             premote_fulladdr = auth_context->remote_addr;
210         }
211     }
212 
213     if ((retval = krb5_rd_priv_basic(context, inbuf, keyblock,
214 				     plocal_fulladdr,
215 				     premote_fulladdr,
216 				     auth_context->i_vector,
217 				     &replaydata, outbuf))) {
218 	CLEANUP_DONE();
219 	return retval;
220     }
221 
222     CLEANUP_DONE();
223 }
224 
225     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
226 	krb5_donot_replay replay;
227 
228 	if ((retval = krb5int_check_clockskew(context, replaydata.timestamp)))
229 	    goto error;
230 
231 	if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
232 					   "_priv", &replay.client)))
233 	    goto error;
234 
235 	replay.server = "";		/* XXX */
236 	replay.cusec = replaydata.usec;
237 	replay.ctime = replaydata.timestamp;
238 	if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
239 	    krb5_xfree(replay.client);
240 	    goto error;
241 	}
242 	krb5_xfree(replay.client);
243     }
244 
245     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
246 	if (!krb5int_auth_con_chkseqnum(context, auth_context,
247 					replaydata.seq)) {
248 	    retval =  KRB5KRB_AP_ERR_BADORDER;
249 	    goto error;
250 	}
251 	auth_context->remote_seq_number++;
252     }
253 
254     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
255       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
256 	outdata->timestamp = replaydata.timestamp;
257 	outdata->usec = replaydata.usec;
258 	outdata->seq = replaydata.seq;
259     }
260 
261     /* everything is ok - return data to the user */
262     return 0;
263 
264 error:;
265     krb5_xfree(outbuf->data);
266     return retval;
267 
268 }
269 
270