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 */
47static krb5_error_code
48krb5_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
156cleanup:
157    krb5_free_safe(context, message);
158    return retval;
159}
160
161krb5_error_code KRB5_CALLCONV
162krb5_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
274error:
275    free(outbuf->data);
276    return retval;
277
278}
279
280