Windows global stuff:
[krb5.git] / src / lib / krb5 / krb / rd_safe.c
1 /*
2  * lib/krb5/krb/rd_safe.c
3  *
4  * Copyright 1990,1991 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.  M.I.T. makes no representations about the suitability of
20  * this software for any purpose.  It is provided "as is" without express
21  * or implied warranty.
22  * 
23  *
24  * krb5_rd_safe()
25  */
26
27 #include "k5-int.h"
28 #include "auth_con.h"
29
30 extern krb5_deltat krb5_clockskew;
31 #define in_clock_skew(date) (labs((date)-currenttime) < krb5_clockskew)
32
33 /*
34  parses a KRB_SAFE message from inbuf, placing the integrity-protected user
35  data in *outbuf.
36
37  key specifies the key to be used for decryption of the message.
38  
39  sender_addr and recv_addr specify the full addresses (host and port) of
40  the sender and receiver.
41
42  outbuf points to allocated storage which the caller should free when finished.
43
44  returns system errors, integrity errors
45  */
46 static krb5_error_code
47 krb5_rd_safe_basic(context, inbuf, keyblock, recv_addr, sender_addr, 
48                    replaydata, outbuf)
49     krb5_context          context;
50     const krb5_data     * inbuf;
51     const krb5_keyblock * keyblock;
52     const krb5_address  * recv_addr;
53     const krb5_address  * sender_addr;
54     krb5_replay_data    * replaydata; 
55     krb5_data           * outbuf;
56 {
57     krb5_error_code       retval;
58     krb5_safe           * message;
59     krb5_checksum our_cksum, *his_cksum;
60     krb5_octet zero_octet = 0;
61     krb5_data *scratch;
62
63     if (!krb5_is_krb_safe(inbuf))
64         return KRB5KRB_AP_ERR_MSG_TYPE;
65
66     if (retval = decode_krb5_safe(inbuf, &message))
67         return retval;
68
69     if (!valid_cksumtype(message->checksum->checksum_type)) {
70         retval = KRB5_PROG_SUMTYPE_NOSUPP;
71         goto cleanup;
72     }
73     if (!is_coll_proof_cksum(message->checksum->checksum_type) ||
74         !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( &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     /* to do the checksum stuff, we need to re-encode the message with a
107        zero-length zero-type checksum, then checksum the encoding, and verify.
108      */
109     his_cksum = message->checksum;
110
111     our_cksum.length = 0;
112     our_cksum.checksum_type = 0;
113     our_cksum.contents = &zero_octet;
114
115     message->checksum = &our_cksum;
116
117     if (retval = encode_krb5_safe(message, &scratch)) 
118         goto cleanup;
119
120     message->checksum = his_cksum;
121                          
122     if (!(our_cksum.contents = (krb5_octet *)
123           malloc(krb5_checksum_size(context, his_cksum->checksum_type)))) {
124         retval = ENOMEM;
125         goto cleanup;
126     }
127
128     retval = krb5_calculate_checksum(context, his_cksum->checksum_type,
129                                      scratch->data, scratch->length,
130                                      (krb5_pointer) keyblock->contents,
131                                      keyblock->length, &our_cksum);
132     (void) memset((char *)scratch->data, 0, scratch->length);
133     krb5_free_data(context, scratch);
134     
135     if (retval) {
136         goto cleanup_cksum;
137     }
138
139     if (our_cksum.length != his_cksum->length ||
140         memcmp((char *)our_cksum.contents, (char *)his_cksum->contents,
141                our_cksum.length)) {
142         retval = KRB5KRB_AP_ERR_MODIFIED;
143         goto cleanup_cksum;
144     }
145
146     replaydata->timestamp = message->timestamp;
147     replaydata->usec = message->usec;
148     replaydata->seq = message->seq_number;
149
150     *outbuf = message->user_data;
151     message->user_data.data = NULL;
152
153     krb5_free_checksum(context, his_cksum);
154     return 0;
155
156 cleanup_cksum:
157     krb5_xfree(our_cksum.contents);
158
159 cleanup:
160     krb5_free_safe(context, message);
161     return retval;
162 }
163
164 krb5_error_code
165 krb5_rd_safe(context, auth_context, inbuf, outbuf, outdata)
166     krb5_context        context;
167     krb5_auth_context * auth_context;
168     const krb5_data   * inbuf;
169     krb5_data         * outbuf;
170     krb5_replay_data  * outdata;
171 {
172     krb5_error_code     retval;
173     krb5_replay_data    replaydata;
174
175     if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
176       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
177       (outdata == NULL)) 
178         /* Need a better error */
179         return KRB5_RC_REQUIRED;
180
181     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
182       (auth_context->rcache == NULL)) 
183         return KRB5_RC_REQUIRED;
184
185     if (retval = krb5_rd_safe_basic(context, inbuf, auth_context->keyblock,
186       auth_context->local_addr, auth_context->remote_addr,
187       &replaydata, outbuf))
188         return retval;
189
190     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
191         krb5_donot_replay replay;
192         krb5_timestamp currenttime;
193
194         if (retval = krb5_timeofday(context, &currenttime)) 
195             goto error;
196
197         if (!in_clock_skew(replaydata.timestamp)) {
198             retval =  KRB5KRB_AP_ERR_SKEW;
199             goto error;
200         }
201
202         if (retval = krb5_gen_replay_name(context, auth_context->remote_addr, 
203                                           "_safe", &replay.client)) 
204             goto error;
205
206         replay.server = "";             /* XXX */
207         replay.cusec = replaydata.usec;
208         replay.ctime = replaydata.timestamp;
209         if (retval = krb5_rc_store(context, auth_context->rcache, &replay)) {
210             krb5_xfree(replay.client);
211             goto error;
212         }
213         krb5_xfree(replay.client);
214     }
215
216     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
217         if (auth_context->remote_seq_number != replaydata.seq) {
218             retval =  KRB5KRB_AP_ERR_BADORDER;
219             goto error;
220         }
221     }
222
223     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
224       (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
225         outdata->timestamp = replaydata.timestamp;
226         outdata->usec = replaydata.usec;
227         outdata->seq = replaydata.seq;
228     }
229         
230     /* everything is ok - return data to the user */
231     return 0;
232
233 error:;
234     krb5_xfree(outbuf->data);
235     return retval;
236
237 }
238