xref: /freebsd/crypto/krb5/src/lib/gssapi/generic/util_seqstate.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/generic/util_seqstate.c - sequence number checking */
3 /*
4  * Copyright (C) 2014 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 "gssapiP_generic.h"
34 #include <string.h>
35 
36 struct g_seqnum_state_st {
37     /* Flags to indicate whether we are supposed to check for replays or
38      * enforce strict sequencing. */
39     int do_replay;
40     int do_sequence;
41 
42     /* UINT32_MAX for 32-bit sequence numbers, UINT64_MAX for 64-bit.  Mask
43      * against this after arithmetic to stay within the correct range. */
44     uint64_t seqmask;
45 
46     /* The initial sequence number for this context.  This value will be
47      * subtracted from all received sequence numbers to simplify wraparound. */
48     uint64_t base;
49 
50     /* The expected next sequence number (one more than the highest previously
51      * seen sequence number), relative to base. */
52     uint64_t next;
53 
54     /*
55      * A bitmap for the 64 sequence numbers prior to next.  If the 1<<(i-1) bit
56      * is set, then we have seen seqnum next-i relative to base.  The least
57      * significant bit is always set if we have received any sequence numbers,
58      * and indicates the highest sequence number we have seen (next-1).  When
59      * we advance next, we shift recvmap to the left.
60      */
61     uint64_t recvmap;
62 };
63 
64 long
g_seqstate_init(g_seqnum_state * state_out,uint64_t seqnum,int do_replay,int do_sequence,int wide)65 g_seqstate_init(g_seqnum_state *state_out, uint64_t seqnum, int do_replay,
66                 int do_sequence, int wide)
67 {
68     g_seqnum_state state;
69 
70     *state_out = NULL;
71     state = malloc(sizeof(*state));
72     if (state == NULL)
73         return ENOMEM;
74     state->do_replay = do_replay;
75     state->do_sequence = do_sequence;
76     state->seqmask = wide ? UINT64_MAX : UINT32_MAX;
77     state->base = seqnum;
78     state->next = state->recvmap = 0;
79     *state_out = state;
80     return 0;
81 }
82 
83 OM_uint32
g_seqstate_check(g_seqnum_state state,uint64_t seqnum)84 g_seqstate_check(g_seqnum_state state, uint64_t seqnum)
85 {
86     uint64_t rel_seqnum, offset, bit;
87 
88     if (!state->do_replay && !state->do_sequence)
89         return GSS_S_COMPLETE;
90 
91     /* Use the difference from the base seqnum, to simplify wraparound. */
92     rel_seqnum = (seqnum - state->base) & state->seqmask;
93 
94     if (rel_seqnum >= state->next) {
95         /* seqnum is the expected sequence number or in the future.  Update the
96          * received bitmap and expected next sequence number. */
97         offset = rel_seqnum - state->next;
98         state->recvmap = (state->recvmap << (offset + 1)) | 1;
99         state->next = (rel_seqnum + 1) & state->seqmask;
100 
101         return (offset > 0 && state->do_sequence) ? GSS_S_GAP_TOKEN :
102             GSS_S_COMPLETE;
103     }
104 
105     /* seqnum is in the past.  Check if it's too old for replay detection. */
106     offset = state->next - rel_seqnum;
107     if (offset > 64)
108         return state->do_sequence ? GSS_S_UNSEQ_TOKEN : GSS_S_OLD_TOKEN;
109 
110     /* Check for replay and mark as received. */
111     bit = (uint64_t)1 << (offset - 1);
112     if (state->do_replay && (state->recvmap & bit))
113         return GSS_S_DUPLICATE_TOKEN;
114     state->recvmap |= bit;
115 
116     return state->do_sequence ? GSS_S_UNSEQ_TOKEN : GSS_S_COMPLETE;
117 }
118 
119 void
g_seqstate_free(g_seqnum_state state)120 g_seqstate_free(g_seqnum_state state)
121 {
122     free(state);
123 }
124 
125 /*
126  * These support functions are for the serialization routines
127  */
128 void
g_seqstate_size(g_seqnum_state state,size_t * sizep)129 g_seqstate_size(g_seqnum_state state, size_t *sizep)
130 {
131     *sizep += sizeof(*state);
132 }
133 
134 long
g_seqstate_externalize(g_seqnum_state state,unsigned char ** buf,size_t * lenremain)135 g_seqstate_externalize(g_seqnum_state state, unsigned char **buf,
136                        size_t *lenremain)
137 {
138     if (*lenremain < sizeof(*state))
139         return ENOMEM;
140     memcpy(*buf, state, sizeof(*state));
141     *buf += sizeof(*state);
142     *lenremain -= sizeof(*state);
143     return 0;
144 }
145 
146 long
g_seqstate_internalize(g_seqnum_state * state_out,unsigned char ** buf,size_t * lenremain)147 g_seqstate_internalize(g_seqnum_state *state_out, unsigned char **buf,
148                        size_t *lenremain)
149 {
150     g_seqnum_state state;
151 
152     *state_out = NULL;
153     if (*lenremain < sizeof(*state))
154         return EINVAL;
155     state = malloc(sizeof(*state));
156     if (state == NULL)
157         return ENOMEM;
158     memcpy(state, *buf, sizeof(*state));
159     *buf += sizeof(*state);
160     *lenremain -= sizeof(*state);
161     *state_out = state;
162     return 0;
163 }
164