/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include "dh_gssapi.h" /* * This module implements the interfaces for replay and out-of-sequence * detection. */ #define WBITS_DEF 8 * sizeof (seq_word_t) /* Bits in a seq_word_t */ static const int WBITS = WBITS_DEF; /* Stored in a static int for debuging */ static const int NBITS = SSIZE * WBITS_DEF; /* Total bits in the sequence */ /* * The following routines are for debuging: * __context_debug_set_next_seqno * __context_debug_get_next_seqno * __context_debug_set_last_seqno * __context_debug_get_last_seqno * __context_debug_print_seq_hist * __context_debug_get_hist_size * __context_debug * * These routines are declared static and there addresses placed into a table. * There is one publicly declare routine __context_debug_entry that is used * to fetch these entries. This way other routines can be added with out * changing the map-version file. This is being done for use with a libgss * test driver. In particular this technique is being used to implement * a pseudo libgss entry point gss_context_cntrl. Its declaration is * OM_uint32 * gss_context_cntl(OM_uint32 *minor, gss_ctx_id_t ctx, int cmd, void *argp); * * Hence the declaratin of the debug routines below. */ /* Set the next sequence number to be sent */ static OM_uint32 __context_debug_set_next_seqno(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) { dh_gss_context_t ctx = (dh_gss_context_t)cntx; OM_uint32 seqno = (OM_uint32)(intptr_t)argp; if (minor == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minor = DH_SUCCESS; /* * If context, set the sequence number. * Locking should not be necessary since OM_uint32 should be atomic * size. */ if (ctx) { mutex_lock(&ctx->seqno_lock); ctx->next_seqno = seqno; mutex_unlock(&ctx->seqno_lock); } return (GSS_S_COMPLETE); } /* Get the next sequence number to be sent */ static OM_uint32 __context_debug_get_next_seqno(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) { dh_gss_context_t ctx = (dh_gss_context_t)cntx; if (minor == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); if (argp == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minor = DH_SUCCESS; /* Grap the next sequence number */ *(OM_uint32 *)argp = ctx->next_seqno; return (GSS_S_COMPLETE); } /* Set the last sequence number to was seen */ static OM_uint32 __context_debug_set_last_seqno(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) { dh_gss_context_t ctx = (dh_gss_context_t)cntx; OM_uint32 seqno = (OM_uint32)(intptr_t)argp; if (minor == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minor = DH_SUCCESS; /* * If context, set the sequence number. * Locking should not be necessary since OM_uint32 should be atomic * size. */ if (ctx) { mutex_lock(&ctx->hist.seq_arr_lock); ctx->hist.seqno = seqno; mutex_unlock(&ctx->hist.seq_arr_lock); } return (GSS_S_COMPLETE); } /* Get the last sequence number seen */ static OM_uint32 __context_debug_get_last_seqno(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) { dh_gss_context_t ctx = (dh_gss_context_t)cntx; if (minor == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); if (argp == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minor = DH_SUCCESS; /* Grap the next sequence number */ *(OM_uint32 *)argp = ctx->hist.seqno; return (GSS_S_COMPLETE); } static seq_word_t rev(seq_word_t r) { int i; seq_word_t t = 0; for (i = 0; i < WBITS; i++) if (r & ((seq_word_t)1 << i)) t |= ((seq_word_t)1 << (WBITS - 1 - i)); return (t); } /* Print out the sequence history to stderr */ static OM_uint32 __context_debug_print_seq_hist(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) { _NOTE(ARGUNUSED(argp)) dh_gss_context_t ctx = (dh_gss_context_t)cntx; int i; if (minor == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minor = DH_SUCCESS; /* Print out the sequence history */ fprintf(stderr, "%u: ", ctx->hist.seqno); for (i = 0; i < SSIZE; i++) fprintf(stderr, "%016.16llx", rev(ctx->hist.arr[i])); fprintf(stderr, "\n"); return (GSS_S_COMPLETE); } /* Fetch the size of the history */ static OM_uint32 __context_debug_get_hist_size(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) { _NOTE(ARGUNUSED(cntx)) if (minor == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); if (argp == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minor = DH_SUCCESS; *(OM_uint32 *)argp = NBITS; return (GSS_S_COMPLETE); } /* Set the debug flag on the context */ static OM_uint32 __context_debug(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) { dh_gss_context_t ctx = (dh_gss_context_t)cntx; if (minor == 0) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minor = DH_SUCCESS; ctx->debug = (OM_uint32)(intptr_t)argp; return (GSS_S_COMPLETE); } /* Type to descript debug routines */ typedef OM_uint32 (*fptr)(OM_uint32 *, gss_ctx_id_t, void *); /* Array of debug entries defined above */ static fptr __context_debug_entry_array[] = { __context_debug, __context_debug_set_next_seqno, __context_debug_get_next_seqno, __context_debug_print_seq_hist, __context_debug_get_hist_size, __context_debug_set_last_seqno, __context_debug_get_last_seqno }; /* Structure to hold the debug entries */ static struct { int no_entries; fptr *entrys; } __context_debug_entry_points = { sizeof (__context_debug_entry_array)/sizeof (fptr), __context_debug_entry_array }; /* * Exported entry point for debug routines. A call to this routine will * return a pointer to the above structure. */ void* __context_debug_entry() { return (&__context_debug_entry_points); } /* *************** End of Debug Section ***************** */ /* Clear all the bits in a sequence array */ static void clear_all_bits(seq_array_t sa) { unsigned int i; for (i = 0; i < SSIZE; i++) sa->arr[i] = (seq_word_t)0; } /* Check that a bit is set in a sequence array */ static unsigned int check_bit(seq_array_t sa, unsigned int bit) { if (bit >= NBITS) return (0); return (sa->arr[bit/WBITS] & ((seq_word_t)1 << (bit % WBITS)) ? 1 : 0); } /* Set a bit in a sequence array */ void set_bit(seq_array_t sa, unsigned int bit) { if (bit < NBITS) sa->arr[bit/WBITS] |= ((seq_word_t)1 << (bit % WBITS)); } /* Clear a bit in a sequence array */ /* * This function is not used, but is here as a comment for completeness. * Lint will complain if it is not commented out. * static void * clear_bit(seq_array_t sa, unsigned int bit) * { * if (bit < NBITS) * sa->arr[bit/WBITS] &= ~((seq_word_t)1 << (bit % WBITS)); * } */ /* * Sift the bits in a sequence array by n * * The seqeunece arrays are logically arranged least significant bit to * most significant bit, where the LSB represents that last sequence * number seen. Thus this routine shifts the entire array to the left by * n. * * 0 NBITS-1 * +---------------------------------------------------------------+ * | | * +---------------------------------------------------------------+ * ^ * This bit corresponds to the last sequence number seen sa->seqno. */ static void shift_bits(seq_array_t sa, unsigned int n) { int i, m; seq_word_t in = 0, out; /* How many words to shift */ m = n / WBITS; /* Do we need to shift by words */ if (m) { for (i = SSIZE - 1; i >= m; i--) sa->arr[i] = sa->arr[i - m]; for (; i >= 0; i--) sa->arr[i] = (seq_word_t)0; } if (m >= SSIZE) return; /* The bits we need to shift */ n %= WBITS; if (n == 0) return; for (i = m; i < SSIZE; i++) { /* The out going bits */ out = (sa->arr[i] >> (WBITS - n)); /* * shift this part of the bit array and "add in" * the most significant bits shifted out of the previous * previous word. */ sa->arr[i] = (sa->arr[i] << n) | in; /* The output of this word is the input to the next */ in = out; } } /* * See if the given sequence number is out of sequence or is a replay * on the given context. If the context is not interested in either * just return GSS_S_COMPLETE */ OM_uint32 __dh_seq_detection(dh_gss_context_t ctx, OM_uint32 seqno) { OM_uint32 n; OM_uint32 stat = GSS_S_COMPLETE; OM_uint32 minor; /* * See if there is anything to do. If not return with no bits set. */ if (((ctx->flags & GSS_C_REPLAY_FLAG) == 0) && ((ctx->flags & GSS_C_SEQUENCE_FLAG) == 0)) return (stat); /* lock the history why we check */ mutex_lock(&ctx->hist.seq_arr_lock); /* If debugging print out the current history */ if (ctx->debug) __context_debug_print_seq_hist(&minor, (gss_ctx_id_t)ctx, 0); n = seqno - ctx->hist.seqno; /* See if n is zero or that the high order bit is set or n = 0 */ if ((n & ~((~((OM_uint32)0)) >> 1)) || n == 0) { /* sequence number is in the past */ /* * We want the small piece of the pie, so take the * 2s complement (-n). */ n = ~n + 1; /* the sequence number is ancient history */ if (n > NBITS - 1) stat = GSS_S_OLD_TOKEN; /* See if it has been seen before */ else if (check_bit(&ctx->hist, n)) stat = GSS_S_DUPLICATE_TOKEN; else { /* Otherwise we've seen it now, so recored the fact */ set_bit(&ctx->hist, n); /* If we care, report that we're out of sequence */ if (ctx->flags & GSS_C_SEQUENCE_FLAG) stat = GSS_S_UNSEQ_TOKEN; } } else { /* sequence number is in the future so shift */ shift_bits(&ctx->hist, n); /* The sequence number is the most recent now */ ctx->hist.seqno = seqno; /* So set the most recent bit */ set_bit(&ctx->hist, 0); /* if n > 1 and we care report a gap in the sequence */ if (n > 1 && (ctx->flags & GSS_C_SEQUENCE_FLAG)) stat = GSS_S_GAP_TOKEN; } /* If we're debugging print out the new state */ if (ctx->debug) __context_debug_print_seq_hist(&minor, (gss_ctx_id_t)ctx, 0); /* Let other threads in */ mutex_unlock(&ctx->hist.seq_arr_lock); /* return the status */ return (stat); } /* * Set the next sequence number to use on this context. * Return that sequence number. */ OM_uint32 __dh_next_seqno(dh_gss_context_t ctx) { OM_uint32 t; mutex_lock(&ctx->seqno_lock); t = ctx->next_seqno++; mutex_unlock(&ctx->seqno_lock); return (t); } /* * Initialize sequence history on a new context */ void __dh_init_seq_hist(dh_gss_context_t ctx) { mutex_init(&ctx->seqno_lock, USYNC_THREAD, 0); ctx->next_seqno = 1; mutex_init(&ctx->hist.seq_arr_lock, USYNC_THREAD, 0); ctx->hist.seqno = 0; clear_all_bits(&ctx->hist); } /* * Destroy sequence history on a context. */ void __dh_destroy_seq_hist(dh_gss_context_t ctx) { if (ctx) { mutex_destroy(&ctx->seqno_lock); mutex_destroy(&ctx->hist.seq_arr_lock); } }