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
40Parses a KRB_PRIV message from inbuf, placing the confidential user
41data in *outbuf.
42
43key specifies the key to be used for decryption of the message.
44
45remote_addr and local_addr specify the full
46addresses (host and port) of the sender and receiver.
47
48outbuf points to allocated storage which the caller should
49free when finished.
50
51i_vector is used as an initialization vector for the
52encryption, and if non-NULL its contents are replaced with the last
53block of the encrypted data upon exit.
54
55Returns system errors, integrity errors.
56
57*/
58
59static krb5_error_code
60krb5_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
137cleanup_data:;
138    if (retval == 0)
139	privmsg_enc_part->user_data.data = 0;
140    krb5_free_priv_enc_part(context, privmsg_enc_part);
141
142cleanup_scratch:;
143    /* Solaris Kerberos */
144    (void) memset(scratch.data, 0, scratch.length);
145    krb5_xfree(scratch.data);
146
147cleanup_privmsg:;
148    krb5_xfree(privmsg->enc_part.ciphertext.data);
149    krb5_xfree(privmsg);
150
151    return retval;
152}
153
154krb5_error_code KRB5_CALLCONV
155krb5_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
264error:;
265    krb5_xfree(outbuf->data);
266    return retval;
267
268}
269
270