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