5d2cee232bd0b24bd500a9ee65f2c5659fe1ec4b
[platform/upstream/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 = NULL;
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     krb5_free_data(context, safe_body);
131     return retval;
132 }
133
134 krb5_error_code KRB5_CALLCONV
135 krb5_rd_safe(krb5_context context, krb5_auth_context auth_context,
136              const krb5_data *inbuf, krb5_data *outbuf,
137              krb5_replay_data *outdata)
138 {
139     krb5_error_code       retval;
140     krb5_key              key;
141     krb5_replay_data      replaydata;
142
143     if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
144          (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
145         (outdata == NULL))
146         /* Need a better error */
147         return KRB5_RC_REQUIRED;
148
149     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
150         (auth_context->remote_addr == NULL))
151         return KRB5_REMOTE_ADDR_REQUIRED;
152
153     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
154         (auth_context->rcache == NULL))
155         return KRB5_RC_REQUIRED;
156
157     /* Get key */
158     if ((key = auth_context->recv_subkey) == NULL)
159         key = auth_context->key;
160
161     memset(&replaydata, 0, sizeof(replaydata));
162     retval = rd_safe_basic(context, auth_context, inbuf, key, &replaydata,
163                            outbuf);
164     if (retval)
165         return retval;
166
167     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
168         krb5_donot_replay replay;
169
170         if ((retval = krb5_check_clockskew(context, replaydata.timestamp)))
171             goto error;
172
173         if ((retval = krb5_gen_replay_name(context, auth_context->remote_addr,
174                                            "_safe", &replay.client)))
175             goto error;
176
177         replay.server = "";             /* XXX */
178         replay.msghash = NULL;
179         replay.cusec = replaydata.usec;
180         replay.ctime = replaydata.timestamp;
181         if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
182             free(replay.client);
183             goto error;
184         }
185         free(replay.client);
186     }
187
188     if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
189         if (!k5_privsafe_check_seqnum(context, auth_context, replaydata.seq)) {
190             retval =  KRB5KRB_AP_ERR_BADORDER;
191             goto error;
192         }
193         auth_context->remote_seq_number++;
194     }
195
196     if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
197         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
198         outdata->timestamp = replaydata.timestamp;
199         outdata->usec = replaydata.usec;
200         outdata->seq = replaydata.seq;
201     }
202
203     /* everything is ok - return data to the user */
204     return 0;
205
206 error:
207     free(outbuf->data);
208     return retval;
209
210 }