xref: /freebsd/crypto/krb5/src/lib/krb5/krb/privsafe.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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