/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* lib/gssapi/generic/util_seqstate.c - sequence number checking */ /* * Copyright (C) 2014 by the Massachusetts Institute of Technology. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gssapiP_generic.h" #include struct g_seqnum_state_st { /* Flags to indicate whether we are supposed to check for replays or * enforce strict sequencing. */ int do_replay; int do_sequence; /* UINT32_MAX for 32-bit sequence numbers, UINT64_MAX for 64-bit. Mask * against this after arithmetic to stay within the correct range. */ uint64_t seqmask; /* The initial sequence number for this context. This value will be * subtracted from all received sequence numbers to simplify wraparound. */ uint64_t base; /* The expected next sequence number (one more than the highest previously * seen sequence number), relative to base. */ uint64_t next; /* * A bitmap for the 64 sequence numbers prior to next. If the 1<<(i-1) bit * is set, then we have seen seqnum next-i relative to base. The least * significant bit is always set if we have received any sequence numbers, * and indicates the highest sequence number we have seen (next-1). When * we advance next, we shift recvmap to the left. */ uint64_t recvmap; }; long g_seqstate_init(g_seqnum_state *state_out, uint64_t seqnum, int do_replay, int do_sequence, int wide) { g_seqnum_state state; *state_out = NULL; state = malloc(sizeof(*state)); if (state == NULL) return ENOMEM; state->do_replay = do_replay; state->do_sequence = do_sequence; state->seqmask = wide ? UINT64_MAX : UINT32_MAX; state->base = seqnum; state->next = state->recvmap = 0; *state_out = state; return 0; } OM_uint32 g_seqstate_check(g_seqnum_state state, uint64_t seqnum) { uint64_t rel_seqnum, offset, bit; if (!state->do_replay && !state->do_sequence) return GSS_S_COMPLETE; /* Use the difference from the base seqnum, to simplify wraparound. */ rel_seqnum = (seqnum - state->base) & state->seqmask; if (rel_seqnum >= state->next) { /* seqnum is the expected sequence number or in the future. Update the * received bitmap and expected next sequence number. */ offset = rel_seqnum - state->next; state->recvmap = (state->recvmap << (offset + 1)) | 1; state->next = (rel_seqnum + 1) & state->seqmask; return (offset > 0 && state->do_sequence) ? GSS_S_GAP_TOKEN : GSS_S_COMPLETE; } /* seqnum is in the past. Check if it's too old for replay detection. */ offset = state->next - rel_seqnum; if (offset > 64) return state->do_sequence ? GSS_S_UNSEQ_TOKEN : GSS_S_OLD_TOKEN; /* Check for replay and mark as received. */ bit = (uint64_t)1 << (offset - 1); if (state->do_replay && (state->recvmap & bit)) return GSS_S_DUPLICATE_TOKEN; state->recvmap |= bit; return state->do_sequence ? GSS_S_UNSEQ_TOKEN : GSS_S_COMPLETE; } void g_seqstate_free(g_seqnum_state state) { free(state); } /* * These support functions are for the serialization routines */ void g_seqstate_size(g_seqnum_state state, size_t *sizep) { *sizep += sizeof(*state); } long g_seqstate_externalize(g_seqnum_state state, unsigned char **buf, size_t *lenremain) { if (*lenremain < sizeof(*state)) return ENOMEM; memcpy(*buf, state, sizeof(*state)); *buf += sizeof(*state); *lenremain -= sizeof(*state); return 0; } long g_seqstate_internalize(g_seqnum_state *state_out, unsigned char **buf, size_t *lenremain) { g_seqnum_state state; *state_out = NULL; if (*lenremain < sizeof(*state)) return EINVAL; state = malloc(sizeof(*state)); if (state == NULL) return ENOMEM; memcpy(state, *buf, sizeof(*state)); *buf += sizeof(*state); *lenremain -= sizeof(*state); *state_out = state; return 0; }