1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/note.h> 30 #include "dh_gssapi.h" 31 32 /* 33 * This module implements the interfaces for replay and out-of-sequence 34 * detection. 35 */ 36 37 #define WBITS_DEF 8 * sizeof (seq_word_t) /* Bits in a seq_word_t */ 38 static const int WBITS = WBITS_DEF; /* Stored in a static int for debuging */ 39 static const int NBITS = SSIZE * WBITS_DEF; /* Total bits in the sequence */ 40 41 /* 42 * The following routines are for debuging: 43 * __context_debug_set_next_seqno 44 * __context_debug_get_next_seqno 45 * __context_debug_set_last_seqno 46 * __context_debug_get_last_seqno 47 * __context_debug_print_seq_hist 48 * __context_debug_get_hist_size 49 * __context_debug 50 * 51 * These routines are declared static and there addresses placed into a table. 52 * There is one publicly declare routine __context_debug_entry that is used 53 * to fetch these entries. This way other routines can be added with out 54 * changing the map-version file. This is being done for use with a libgss 55 * test driver. In particular this technique is being used to implement 56 * a pseudo libgss entry point gss_context_cntrl. Its declaration is 57 * OM_uint32 58 * gss_context_cntl(OM_uint32 *minor, gss_ctx_id_t ctx, int cmd, void *argp); 59 * 60 * Hence the declaratin of the debug routines below. 61 */ 62 63 /* Set the next sequence number to be sent */ 64 static OM_uint32 65 __context_debug_set_next_seqno(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) 66 { 67 dh_gss_context_t ctx = (dh_gss_context_t)cntx; 68 OM_uint32 seqno = (OM_uint32)(intptr_t)argp; 69 70 if (minor == 0) 71 return (GSS_S_CALL_INACCESSIBLE_WRITE); 72 73 *minor = DH_SUCCESS; 74 /* 75 * If context, set the sequence number. 76 * Locking should not be necessary since OM_uint32 should be atomic 77 * size. 78 */ 79 if (ctx) { 80 mutex_lock(&ctx->seqno_lock); 81 ctx->next_seqno = seqno; 82 mutex_unlock(&ctx->seqno_lock); 83 } 84 return (GSS_S_COMPLETE); 85 } 86 87 /* Get the next sequence number to be sent */ 88 static OM_uint32 89 __context_debug_get_next_seqno(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) 90 { 91 dh_gss_context_t ctx = (dh_gss_context_t)cntx; 92 93 if (minor == 0) 94 return (GSS_S_CALL_INACCESSIBLE_WRITE); 95 96 if (argp == 0) 97 return (GSS_S_CALL_INACCESSIBLE_WRITE); 98 99 *minor = DH_SUCCESS; 100 /* Grap the next sequence number */ 101 *(OM_uint32 *)argp = ctx->next_seqno; 102 103 return (GSS_S_COMPLETE); 104 } 105 106 /* Set the last sequence number to was seen */ 107 static OM_uint32 108 __context_debug_set_last_seqno(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) 109 { 110 dh_gss_context_t ctx = (dh_gss_context_t)cntx; 111 OM_uint32 seqno = (OM_uint32)(intptr_t)argp; 112 113 if (minor == 0) 114 return (GSS_S_CALL_INACCESSIBLE_WRITE); 115 116 *minor = DH_SUCCESS; 117 118 /* 119 * If context, set the sequence number. 120 * Locking should not be necessary since OM_uint32 should be atomic 121 * size. 122 */ 123 if (ctx) { 124 mutex_lock(&ctx->hist.seq_arr_lock); 125 ctx->hist.seqno = seqno; 126 mutex_unlock(&ctx->hist.seq_arr_lock); 127 } 128 return (GSS_S_COMPLETE); 129 } 130 131 /* Get the last sequence number seen */ 132 static OM_uint32 133 __context_debug_get_last_seqno(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) 134 { 135 dh_gss_context_t ctx = (dh_gss_context_t)cntx; 136 137 if (minor == 0) 138 return (GSS_S_CALL_INACCESSIBLE_WRITE); 139 140 if (argp == 0) 141 return (GSS_S_CALL_INACCESSIBLE_WRITE); 142 143 *minor = DH_SUCCESS; 144 /* Grap the next sequence number */ 145 *(OM_uint32 *)argp = ctx->hist.seqno; 146 147 return (GSS_S_COMPLETE); 148 } 149 150 static seq_word_t 151 rev(seq_word_t r) 152 { 153 int i; 154 seq_word_t t = 0; 155 156 for (i = 0; i < WBITS; i++) 157 if (r & ((seq_word_t)1 << i)) 158 t |= ((seq_word_t)1 << (WBITS - 1 - i)); 159 160 return (t); 161 } 162 163 /* Print out the sequence history to stderr */ 164 static OM_uint32 165 __context_debug_print_seq_hist(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) 166 { 167 _NOTE(ARGUNUSED(argp)) 168 dh_gss_context_t ctx = (dh_gss_context_t)cntx; 169 int i; 170 171 if (minor == 0) 172 return (GSS_S_CALL_INACCESSIBLE_WRITE); 173 174 *minor = DH_SUCCESS; 175 176 /* Print out the sequence history */ 177 fprintf(stderr, "%u: ", ctx->hist.seqno); 178 179 for (i = 0; i < SSIZE; i++) 180 fprintf(stderr, "%016.16llx", rev(ctx->hist.arr[i])); 181 fprintf(stderr, "\n"); 182 183 return (GSS_S_COMPLETE); 184 } 185 186 /* Fetch the size of the history */ 187 static OM_uint32 188 __context_debug_get_hist_size(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) 189 { 190 _NOTE(ARGUNUSED(cntx)) 191 192 if (minor == 0) 193 return (GSS_S_CALL_INACCESSIBLE_WRITE); 194 if (argp == 0) 195 return (GSS_S_CALL_INACCESSIBLE_WRITE); 196 197 *minor = DH_SUCCESS; 198 *(OM_uint32 *)argp = NBITS; 199 200 return (GSS_S_COMPLETE); 201 } 202 203 /* Set the debug flag on the context */ 204 static OM_uint32 205 __context_debug(OM_uint32 *minor, gss_ctx_id_t cntx, void *argp) 206 { 207 dh_gss_context_t ctx = (dh_gss_context_t)cntx; 208 209 if (minor == 0) 210 return (GSS_S_CALL_INACCESSIBLE_WRITE); 211 212 *minor = DH_SUCCESS; 213 ctx->debug = (OM_uint32)(intptr_t)argp; 214 215 return (GSS_S_COMPLETE); 216 } 217 218 /* Type to descript debug routines */ 219 typedef OM_uint32 (*fptr)(OM_uint32 *, gss_ctx_id_t, void *); 220 221 /* Array of debug entries defined above */ 222 static fptr __context_debug_entry_array[] = { 223 __context_debug, 224 __context_debug_set_next_seqno, 225 __context_debug_get_next_seqno, 226 __context_debug_print_seq_hist, 227 __context_debug_get_hist_size, 228 __context_debug_set_last_seqno, 229 __context_debug_get_last_seqno 230 }; 231 232 /* Structure to hold the debug entries */ 233 static struct { 234 int no_entries; 235 fptr *entrys; 236 } __context_debug_entry_points = { 237 sizeof (__context_debug_entry_array)/sizeof (fptr), 238 __context_debug_entry_array 239 }; 240 241 /* 242 * Exported entry point for debug routines. A call to this routine will 243 * return a pointer to the above structure. 244 */ 245 246 void* 247 __context_debug_entry() 248 { 249 return (&__context_debug_entry_points); 250 } 251 252 /* *************** End of Debug Section ***************** */ 253 254 /* Clear all the bits in a sequence array */ 255 static void 256 clear_all_bits(seq_array_t sa) 257 { 258 unsigned int i; 259 260 for (i = 0; i < SSIZE; i++) 261 sa->arr[i] = (seq_word_t)0; 262 } 263 264 /* Check that a bit is set in a sequence array */ 265 static unsigned int 266 check_bit(seq_array_t sa, unsigned int bit) 267 { 268 if (bit >= NBITS) 269 return (0); 270 271 return (sa->arr[bit/WBITS] & ((seq_word_t)1 << (bit % WBITS)) ? 1 : 0); 272 } 273 274 /* Set a bit in a sequence array */ 275 void 276 set_bit(seq_array_t sa, unsigned int bit) 277 { 278 if (bit < NBITS) 279 sa->arr[bit/WBITS] |= ((seq_word_t)1 << (bit % WBITS)); 280 } 281 282 /* Clear a bit in a sequence array */ 283 /* 284 * This function is not used, but is here as a comment for completeness. 285 * Lint will complain if it is not commented out. 286 * static void 287 * clear_bit(seq_array_t sa, unsigned int bit) 288 * { 289 * if (bit < NBITS) 290 * sa->arr[bit/WBITS] &= ~((seq_word_t)1 << (bit % WBITS)); 291 * } 292 */ 293 294 /* 295 * Sift the bits in a sequence array by n 296 * 297 * The seqeunece arrays are logically arranged least significant bit to 298 * most significant bit, where the LSB represents that last sequence 299 * number seen. Thus this routine shifts the entire array to the left by 300 * n. 301 * 302 * 0 NBITS-1 303 * +---------------------------------------------------------------+ 304 * | | 305 * +---------------------------------------------------------------+ 306 * ^ 307 * This bit corresponds to the last sequence number seen sa->seqno. 308 */ 309 static void 310 shift_bits(seq_array_t sa, unsigned int n) 311 { 312 int i, m; 313 seq_word_t in = 0, out; 314 315 /* How many words to shift */ 316 m = n / WBITS; 317 318 /* Do we need to shift by words */ 319 if (m) { 320 for (i = SSIZE - 1; i >= m; i--) 321 sa->arr[i] = sa->arr[i - m]; 322 for (; i >= 0; i--) 323 sa->arr[i] = (seq_word_t)0; 324 } 325 326 if (m >= SSIZE) 327 return; 328 329 /* The bits we need to shift */ 330 n %= WBITS; 331 if (n == 0) 332 return; 333 334 335 for (i = m; i < SSIZE; i++) { 336 /* The out going bits */ 337 out = (sa->arr[i] >> (WBITS - n)); 338 /* 339 * shift this part of the bit array and "add in" 340 * the most significant bits shifted out of the previous 341 * previous word. 342 */ 343 sa->arr[i] = (sa->arr[i] << n) | in; 344 /* The output of this word is the input to the next */ 345 in = out; 346 } 347 } 348 349 350 /* 351 * See if the given sequence number is out of sequence or is a replay 352 * on the given context. If the context is not interested in either 353 * just return GSS_S_COMPLETE 354 */ 355 OM_uint32 356 __dh_seq_detection(dh_gss_context_t ctx, OM_uint32 seqno) 357 { 358 OM_uint32 n; 359 OM_uint32 stat = GSS_S_COMPLETE; 360 OM_uint32 minor; 361 362 /* 363 * See if there is anything to do. If not return with no bits set. 364 */ 365 366 if (((ctx->flags & GSS_C_REPLAY_FLAG) == 0) && 367 ((ctx->flags & GSS_C_SEQUENCE_FLAG) == 0)) 368 return (stat); 369 370 /* lock the history why we check */ 371 mutex_lock(&ctx->hist.seq_arr_lock); 372 373 /* If debugging print out the current history */ 374 if (ctx->debug) 375 __context_debug_print_seq_hist(&minor, (gss_ctx_id_t)ctx, 0); 376 377 n = seqno - ctx->hist.seqno; 378 /* See if n is zero or that the high order bit is set or n = 0 */ 379 if ((n & ~((~((OM_uint32)0)) >> 1)) || n == 0) { 380 /* sequence number is in the past */ 381 382 /* 383 * We want the small piece of the pie, so take the 384 * 2s complement (-n). 385 */ 386 n = ~n + 1; 387 388 /* the sequence number is ancient history */ 389 if (n > NBITS - 1) 390 stat = GSS_S_OLD_TOKEN; 391 /* See if it has been seen before */ 392 else if (check_bit(&ctx->hist, n)) 393 stat = GSS_S_DUPLICATE_TOKEN; 394 else { 395 /* Otherwise we've seen it now, so recored the fact */ 396 set_bit(&ctx->hist, n); 397 398 /* If we care, report that we're out of sequence */ 399 if (ctx->flags & GSS_C_SEQUENCE_FLAG) 400 stat = GSS_S_UNSEQ_TOKEN; 401 } 402 } else { 403 /* sequence number is in the future so shift */ 404 shift_bits(&ctx->hist, n); 405 406 /* The sequence number is the most recent now */ 407 ctx->hist.seqno = seqno; 408 409 /* So set the most recent bit */ 410 set_bit(&ctx->hist, 0); 411 412 /* if n > 1 and we care report a gap in the sequence */ 413 if (n > 1 && (ctx->flags & GSS_C_SEQUENCE_FLAG)) 414 stat = GSS_S_GAP_TOKEN; 415 } 416 417 /* If we're debugging print out the new state */ 418 if (ctx->debug) 419 __context_debug_print_seq_hist(&minor, (gss_ctx_id_t)ctx, 0); 420 421 /* Let other threads in */ 422 mutex_unlock(&ctx->hist.seq_arr_lock); 423 424 /* return the status */ 425 return (stat); 426 } 427 428 /* 429 * Set the next sequence number to use on this context. 430 * Return that sequence number. 431 */ 432 OM_uint32 433 __dh_next_seqno(dh_gss_context_t ctx) 434 { 435 OM_uint32 t; 436 437 mutex_lock(&ctx->seqno_lock); 438 t = ctx->next_seqno++; 439 mutex_unlock(&ctx->seqno_lock); 440 441 return (t); 442 } 443 444 445 /* 446 * Initialize sequence history on a new context 447 */ 448 void 449 __dh_init_seq_hist(dh_gss_context_t ctx) 450 { 451 mutex_init(&ctx->seqno_lock, USYNC_THREAD, 0); 452 ctx->next_seqno = 1; 453 mutex_init(&ctx->hist.seq_arr_lock, USYNC_THREAD, 0); 454 ctx->hist.seqno = 0; 455 clear_all_bits(&ctx->hist); 456 } 457 458 /* 459 * Destroy sequence history on a context. 460 */ 461 void 462 __dh_destroy_seq_hist(dh_gss_context_t ctx) 463 { 464 if (ctx) { 465 mutex_destroy(&ctx->seqno_lock); 466 mutex_destroy(&ctx->hist.seq_arr_lock); 467 } 468 } 469