Factor out the address checks in krb5_rd_safe and krb5_rd_priv into
[krb5.git] / src / lib / krb5 / krb / rd_safe.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/rd_safe.c - definition of krb5_rd_safe() */
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 #include "k5-int.h"
28 #include "int-proto.h"
29 #include "cleanup.h"
30 #include "auth_con.h"
31
32 /*
33   parses a KRB_SAFE message from inbuf, placing the integrity-protected user
34   data in *outbuf.
35
36   key specifies the key to be used for decryption of the message.
37
38   outbuf points to allocated storage which the caller should free when finished.
39
40   returns system errors, integrity errors
41 */
42 static krb5_error_code
43 rd_safe_basic(krb5_context context, krb5_auth_context ac,
44               const krb5_data *inbuf, krb5_key key,
45               krb5_replay_data *replaydata, krb5_data *outbuf)
46 {
47     krb5_error_code       retval;
48     krb5_safe           * message;
49     krb5_data safe_body;
50     krb5_checksum our_cksum, *his_cksum;
51     krb5_octet zero_octet = 0;
52     krb5_data *scratch;
53     krb5_boolean valid;
54     struct krb5_safe_with_body swb;
55
56     if (!krb5_is_krb_safe(inbuf))
57         return KRB5KRB_AP_ERR_MSG_TYPE;
58
59     if ((retval = decode_krb5_safe_with_body(inbuf, &message, &safe_body)))
60         return retval;
61
62     if (!krb5_c_valid_cksumtype(message->checksum->checksum_type)) {
63         retval = KRB5_PROG_SUMTYPE_NOSUPP;
64         goto cleanup;
65     }
66     if (!krb5_c_is_coll_proof_cksum(message->checksum->checksum_type) ||
67         !krb5_c_is_keyed_cksum(message->checksum->checksum_type)) {
68         retval = KRB5KRB_AP_ERR_INAPP_CKSUM;
69         goto cleanup;
70     }
71
72     retval = k5_privsafe_check_addrs(context, ac, message->s_address,
73                                      message->r_address);
74     if (retval)
75         goto cleanup;
76
77     /* verify the checksum */
78     /*
79      * In order to recreate what was checksummed, we regenerate the message
80      * without checksum and then have the cryptographic subsystem verify
81      * the checksum for us.  This is because some checksum methods have
82      * a confounder encrypted as part of the checksum.
83      */
84     his_cksum = message->checksum;
85
86     our_cksum.length = 0;
87     our_cksum.checksum_type = 0;
88     our_cksum.contents = &zero_octet;
89
90     message->checksum = &our_cksum;
91
92     swb.body = &safe_body;
93     swb.safe = message;
94     retval = encode_krb5_safe_with_body(&swb, &scratch);
95     message->checksum = his_cksum;
96     if (retval)
97         goto cleanup;
98
99     retval = krb5_k_verify_checksum(context, key,
100                                     KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
101                                     scratch, his_cksum, &valid);
102
103     (void) memset(scratch->data, 0, scratch->length);
104     krb5_free_data(context, scratch);
105
106     if (!valid) {
107         /*
108          * Checksum over only the KRB-SAFE-BODY, like RFC 1510 says, in
109          * case someone actually implements it correctly.
110          */
111         retval = krb5_k_verify_checksum(context, key,
112                                         KRB5_KEYUSAGE_KRB_SAFE_CKSUM,
113                                         &safe_body, his_cksum, &valid);
114         if (!valid) {
115             retval = KRB5KRB_AP_ERR_MODIFIED;
116             goto cleanup;
117         }
118     }
119
120     replaydata->timestamp = message->timestamp;
121     replaydata->usec = message->usec;
122     replaydata->seq = message->seq_number;
123
124     *outbuf = message->user_data;
125     message->user_data.data = NULL;
126     retval = 0;
127
128 cleanup:
129     krb5_free_safe(context, message);
130     return retval;
131 }
132
133 krb5_error_code KRB5_CALLCONV
134 krb5_rd_safe(krb5_context context, krb5_auth_context auth_context,
135              const krb5_data *inbuf, krb5_data *outbuf,
136              krb5_replay_data *outdata)
137 {
138     krb5_error_code       retval;
139     krb5_key              key;
140     krb5_replay_data      replaydata;
141
142     if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
143          (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
144         (outdata == NULL))
145         /* Need a better error */
146         return KRB5_RC_REQUIRED;
147
148     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
149         (auth_context->rcache == NULL))
150         return KRB5_RC_REQUIRED;
151
152     if (!auth_context->remote_addr)
153         return KRB5_REMOTE_ADDR_REQUIRED;
154
155     /* Get key */
156     if ((key = auth_context->recv_subkey) == NULL)
157         key = auth_context->key;
158
159     memset(&replaydata, 0, sizeof(replaydata));
160     retval = rd_safe_basic(context, auth_context, inbuf, key, &replaydata,
161                            outbuf);
162     if (retval)
163         return retval;
164
165     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
166         krb5_donot_replay replay;
167
168         if ((retval = krb5int_check_clockskew(context, replaydata.timestamp)))
169             goto error;
170
171         if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
172                                            "_safe", &replay.client)))
173             goto error;
174
175         replay.server = "";             /* XXX */
176         replay.msghash = NULL;
177         replay.cusec = replaydata.usec;
178         replay.ctime = replaydata.timestamp;
179         if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
180             free(replay.client);
181             goto error;
182         }
183         free(replay.client);
184     }
185
186     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
187         if (!k5_privsafe_check_seqnum(context, auth_context, replaydata.seq)) {
188             retval =  KRB5KRB_AP_ERR_BADORDER;
189             goto error;
190         }
191         auth_context->remote_seq_number++;
192     }
193
194     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
195         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
196         outdata->timestamp = replaydata.timestamp;
197         outdata->usec = replaydata.usec;
198         outdata->seq = replaydata.seq;
199     }
200
201     /* everything is ok - return data to the user */
202     return 0;
203
204 error:
205     free(outbuf->data);
206     return retval;
207
208 }