1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/privsafe.c - Shared logic for KRB-SAFE and KRB-PRIV messages */
3 /*
4 * Copyright (C) 2011,2019 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "k5-int.h"
34 #include "int-proto.h"
35 #include "auth_con.h"
36
37 krb5_error_code
k5_privsafe_gen_rdata(krb5_context context,krb5_auth_context authcon,krb5_replay_data * rdata,krb5_replay_data * caller_rdata)38 k5_privsafe_gen_rdata(krb5_context context, krb5_auth_context authcon,
39 krb5_replay_data *rdata, krb5_replay_data *caller_rdata)
40 {
41 krb5_error_code ret;
42 krb5_int32 flags = authcon->auth_context_flags;
43 krb5_boolean do_time = !!(flags & KRB5_AUTH_CONTEXT_DO_TIME);
44 krb5_boolean do_sequence = !!(flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE);
45 krb5_boolean ret_time = !!(flags & KRB5_AUTH_CONTEXT_RET_TIME);
46 krb5_boolean ret_sequence = !!(flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE);
47
48 memset(rdata, 0, sizeof(*rdata));
49 if ((ret_time || ret_sequence) && caller_rdata == NULL)
50 return KRB5_RC_REQUIRED;
51
52 if (do_time || ret_time) {
53 ret = krb5_us_timeofday(context, &rdata->timestamp, &rdata->usec);
54 if (ret)
55 return ret;
56 if (ret_time) {
57 caller_rdata->timestamp = rdata->timestamp;
58 caller_rdata->usec = rdata->usec;
59 }
60 }
61 if (do_sequence || ret_sequence) {
62 rdata->seq = authcon->local_seq_number;
63 if (ret_sequence)
64 caller_rdata->seq = rdata->seq;
65 }
66
67 return 0;
68 }
69
70 krb5_error_code
k5_privsafe_gen_addrs(krb5_context context,krb5_auth_context authcon,krb5_address * lstorage,krb5_address * rstorage,krb5_address ** local_out,krb5_address ** remote_out)71 k5_privsafe_gen_addrs(krb5_context context, krb5_auth_context authcon,
72 krb5_address *lstorage, krb5_address *rstorage,
73 krb5_address **local_out, krb5_address **remote_out)
74 {
75 krb5_error_code ret;
76
77 *local_out = NULL;
78 *remote_out = NULL;
79
80 if (authcon->local_addr != NULL) {
81 if (authcon->local_port != NULL) {
82 ret = krb5_make_fulladdr(context, authcon->local_addr,
83 authcon->local_port, lstorage);
84 if (ret)
85 return ret;
86 *local_out = lstorage;
87 } else {
88 *local_out = authcon->local_addr;
89 }
90 }
91
92 if (authcon->remote_addr != NULL) {
93 if (authcon->remote_port != NULL) {
94 ret = krb5_make_fulladdr(context, authcon->remote_addr,
95 authcon->remote_port, rstorage);
96 if (ret)
97 return ret;
98 *remote_out = rstorage;
99 } else {
100 *remote_out = authcon->remote_addr;
101 }
102 }
103
104 return 0;
105 }
106
107 krb5_error_code
k5_privsafe_check_replay(krb5_context context,krb5_auth_context authcon,const krb5_replay_data * rdata,const krb5_enc_data * enc,const krb5_checksum * cksum)108 k5_privsafe_check_replay(krb5_context context, krb5_auth_context authcon,
109 const krb5_replay_data *rdata,
110 const krb5_enc_data *enc, const krb5_checksum *cksum)
111 {
112 krb5_error_code ret;
113 krb5_data tag;
114
115 assert(enc != NULL || cksum != NULL);
116
117 if (!(authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME))
118 return 0;
119
120 if (rdata != NULL) {
121 ret = krb5_check_clockskew(context, rdata->timestamp);
122 if (ret)
123 return ret;
124 }
125
126 if (enc != NULL) {
127 ret = k5_rc_tag_from_ciphertext(context, enc, &tag);
128 if (ret)
129 return ret;
130 } else {
131 tag = make_data(cksum->contents, cksum->length);
132 }
133
134 if (authcon->memrcache == NULL) {
135 ret = k5_memrcache_create(context, &authcon->memrcache);
136 if (ret)
137 return ret;
138 }
139
140 return k5_memrcache_store(context, authcon->memrcache, &tag);
141 }
142
143 /*
144 * k5_privsafe_check_seqnum
145 *
146 * We use a somewhat complex heuristic for validating received
147 * sequence numbers. We must accommodate both our older
148 * implementation, which sends negative sequence numbers, and the
149 * broken Heimdal implementation (at least as of 0.5.2), which
150 * violates X.690 BER for integer encodings. The requirement of
151 * handling negative sequence numbers removes one of easier means of
152 * detecting a Heimdal implementation, so we resort to this mess
153 * here.
154 *
155 * X.690 BER (and consequently DER, which are the required encoding
156 * rules in RFC1510) encode all integer types as signed integers.
157 * This means that the MSB being set on the first octet of the
158 * contents of the encoding indicates a negative value. Heimdal does
159 * not prepend the required zero octet to unsigned integer encodings
160 * which would otherwise have the MSB of the first octet of their
161 * encodings set.
162 *
163 * Our ASN.1 library implements a special decoder for sequence
164 * numbers, accepting both negative and positive 32-bit numbers but
165 * mapping them both into the space of positive unsigned 32-bit
166 * numbers in the obvious bit-pattern-preserving way. This maintains
167 * compatibility with our older implementations. This also means that
168 * encodings emitted by Heimdal are ambiguous.
169 *
170 * Heimdal counter value received uint32 value
171 *
172 * 0x00000080 0xFFFFFF80
173 * 0x000000FF 0xFFFFFFFF
174 * 0x00008000 0xFFFF8000
175 * 0x0000FFFF 0xFFFFFFFF
176 * 0x00800000 0xFF800000
177 * 0x00FFFFFF 0xFFFFFFFF
178 * 0xFF800000 0xFF800000
179 * 0xFFFFFFFF 0xFFFFFFFF
180 *
181 * We use two auth_context flags, SANE_SEQ and HEIMDAL_SEQ, which are
182 * only set after we can unambiguously determine the sanity of the
183 * sending implementation. Once one of these flags is set, we accept
184 * only the sequence numbers appropriate to the remote implementation
185 * type. We can make the determination in two different ways. The
186 * first is to note the receipt of a "negative" sequence number when a
187 * "positive" one was expected. The second is to note the receipt of
188 * a sequence number that wraps through "zero" in a weird way. The
189 * latter corresponds to the receipt of an initial sequence number in
190 * the ambiguous range.
191 *
192 * There are 2^7 + 2^15 + 2^23 + 2^23 = 16810112 total ambiguous
193 * initial Heimdal counter values, but we receive them as one of 2^23
194 * possible values. There is a ~1/256 chance of a Heimdal
195 * implementation sending an initial sequence number in the ambiguous
196 * range.
197 *
198 * We have to do special treatment when receiving sequence numbers
199 * between 0xFF800000..0xFFFFFFFF, or when wrapping through zero
200 * weirdly (due to ambiguous initial sequence number). If we are
201 * expecting a value corresponding to an ambiguous Heimdal counter
202 * value, and we receive an exact match, we can mark the remote end as
203 * sane.
204 */
205
206 static krb5_boolean
chk_heimdal_seqnum(krb5_ui_4 exp_seq,krb5_ui_4 in_seq)207 chk_heimdal_seqnum(krb5_ui_4 exp_seq, krb5_ui_4 in_seq)
208 {
209 if (( exp_seq & 0xFF800000) == 0x00800000
210 && (in_seq & 0xFF800000) == 0xFF800000
211 && (in_seq & 0x00FFFFFF) == exp_seq)
212 return 1;
213 else if (( exp_seq & 0xFFFF8000) == 0x00008000
214 && (in_seq & 0xFFFF8000) == 0xFFFF8000
215 && (in_seq & 0x0000FFFF) == exp_seq)
216 return 1;
217 else if (( exp_seq & 0xFFFFFF80) == 0x00000080
218 && (in_seq & 0xFFFFFF80) == 0xFFFFFF80
219 && (in_seq & 0x000000FF) == exp_seq)
220 return 1;
221 else
222 return 0;
223 }
224
225 krb5_boolean
k5_privsafe_check_seqnum(krb5_context ctx,krb5_auth_context ac,krb5_ui_4 in_seq)226 k5_privsafe_check_seqnum(krb5_context ctx, krb5_auth_context ac,
227 krb5_ui_4 in_seq)
228 {
229 krb5_ui_4 exp_seq;
230
231 exp_seq = ac->remote_seq_number;
232
233 /*
234 * If sender is known to be sane, accept _only_ exact matches.
235 */
236 if (ac->auth_context_flags & KRB5_AUTH_CONN_SANE_SEQ)
237 return in_seq == exp_seq;
238
239 /*
240 * If sender is not known to be sane, first check the ambiguous
241 * range of received values, 0xFF800000..0xFFFFFFFF.
242 */
243 if ((in_seq & 0xFF800000) == 0xFF800000) {
244 /*
245 * If expected sequence number is in the range
246 * 0xFF800000..0xFFFFFFFF, then we can't make any
247 * determinations about the sanity of the sending
248 * implementation.
249 */
250 if ((exp_seq & 0xFF800000) == 0xFF800000 && in_seq == exp_seq)
251 return 1;
252 /*
253 * If sender is not known for certain to be a broken Heimdal
254 * implementation, check for exact match.
255 */
256 if (!(ac->auth_context_flags & KRB5_AUTH_CONN_HEIMDAL_SEQ)
257 && in_seq == exp_seq)
258 return 1;
259 /*
260 * Now apply hairy algorithm for matching sequence numbers
261 * sent by broken Heimdal implementations. If it matches, we
262 * know for certain it's a broken Heimdal sender.
263 */
264 if (chk_heimdal_seqnum(exp_seq, in_seq)) {
265 ac->auth_context_flags |= KRB5_AUTH_CONN_HEIMDAL_SEQ;
266 return 1;
267 }
268 return 0;
269 }
270
271 /*
272 * Received value not in the ambiguous range? If the _expected_
273 * value is in the range of ambiguous Hemidal counter values, and
274 * it matches the received value, sender is known to be sane.
275 */
276 if (in_seq == exp_seq) {
277 if (( exp_seq & 0xFFFFFF80) == 0x00000080
278 || (exp_seq & 0xFFFF8000) == 0x00008000
279 || (exp_seq & 0xFF800000) == 0x00800000)
280 ac->auth_context_flags |= KRB5_AUTH_CONN_SANE_SEQ;
281 return 1;
282 }
283
284 /*
285 * Magic wraparound for the case where the initial sequence number
286 * is in the ambiguous range. This means that the sender's
287 * counter is at a different count than ours, so we correct ours,
288 * and mark the sender as being a broken Heimdal implementation.
289 */
290 if (exp_seq == 0
291 && !(ac->auth_context_flags & KRB5_AUTH_CONN_HEIMDAL_SEQ)) {
292 switch (in_seq) {
293 case 0x100:
294 case 0x10000:
295 case 0x1000000:
296 ac->auth_context_flags |= KRB5_AUTH_CONN_HEIMDAL_SEQ;
297 exp_seq = in_seq;
298 return 1;
299 default:
300 return 0;
301 }
302 }
303 return 0;
304 }
305
306 /*
307 * Verify the sender and receiver addresses from a KRB-SAFE or KRB-PRIV message
308 * against the auth context. msg_r_addr may be NULL, but msg_s_addr must not
309 * be. The auth context's remote addr must be set.
310 */
311 krb5_error_code
k5_privsafe_check_addrs(krb5_context context,krb5_auth_context ac,krb5_address * msg_s_addr,krb5_address * msg_r_addr)312 k5_privsafe_check_addrs(krb5_context context, krb5_auth_context ac,
313 krb5_address *msg_s_addr, krb5_address *msg_r_addr)
314 {
315 krb5_error_code ret = 0;
316 krb5_address **our_addrs = NULL;
317 const krb5_address *local_addr, *remote_addr;
318 krb5_address local_fulladdr, remote_fulladdr;
319
320 local_fulladdr.contents = remote_fulladdr.contents = NULL;
321
322 /* Determine the remote comparison address. */
323 if (ac->remote_addr != NULL) {
324 if (ac->remote_port != NULL) {
325 ret = krb5_make_fulladdr(context, ac->remote_addr, ac->remote_port,
326 &remote_fulladdr);
327 if (ret)
328 goto cleanup;
329 remote_addr = &remote_fulladdr;
330 } else
331 remote_addr = ac->remote_addr;
332 } else
333 remote_addr = NULL;
334
335 /* Determine the local comparison address (possibly NULL). */
336 if (ac->local_addr != NULL) {
337 if (ac->local_port != NULL) {
338 ret = krb5_make_fulladdr(context, ac->local_addr, ac->local_port,
339 &local_fulladdr);
340 if (ret)
341 goto cleanup;
342 local_addr = &local_fulladdr;
343 } else
344 local_addr = ac->local_addr;
345 } else
346 local_addr = NULL;
347
348 /* Check the remote address against the message's sender address. */
349 if (remote_addr != NULL &&
350 !krb5_address_compare(context, remote_addr, msg_s_addr)) {
351 ret = KRB5KRB_AP_ERR_BADADDR;
352 goto cleanup;
353 }
354
355 /* Receiver address is optional; only check it if supplied. */
356 if (msg_r_addr == NULL)
357 goto cleanup;
358
359 /* Check the message's receiver address against the local address, or
360 * against all local addresses if no specific local address is set. */
361 if (local_addr != NULL) {
362 if (!krb5_address_compare(context, local_addr, msg_r_addr)) {
363 ret = KRB5KRB_AP_ERR_BADADDR;
364 goto cleanup;
365 }
366 } else {
367 ret = krb5_os_localaddr(context, &our_addrs);
368 if (ret)
369 goto cleanup;
370
371 if (!krb5_address_search(context, msg_r_addr, our_addrs)) {
372 ret = KRB5KRB_AP_ERR_BADADDR;
373 goto cleanup;
374 }
375 }
376
377 cleanup:
378 free(local_fulladdr.contents);
379 free(remote_fulladdr.contents);
380 krb5_free_addresses(context, our_addrs);
381 return ret;
382 }
383