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