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
__context_debug_set_next_seqno(OM_uint32 * minor,gss_ctx_id_t cntx,void * argp)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
__context_debug_get_next_seqno(OM_uint32 * minor,gss_ctx_id_t cntx,void * argp)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
__context_debug_set_last_seqno(OM_uint32 * minor,gss_ctx_id_t cntx,void * argp)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
__context_debug_get_last_seqno(OM_uint32 * minor,gss_ctx_id_t cntx,void * argp)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
rev(seq_word_t r)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
__context_debug_print_seq_hist(OM_uint32 * minor,gss_ctx_id_t cntx,void * argp)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
__context_debug_get_hist_size(OM_uint32 * minor,gss_ctx_id_t cntx,void * argp)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
__context_debug(OM_uint32 * minor,gss_ctx_id_t cntx,void * argp)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*
__context_debug_entry()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
clear_all_bits(seq_array_t sa)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
check_bit(seq_array_t sa,unsigned int bit)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
set_bit(seq_array_t sa,unsigned int bit)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
shift_bits(seq_array_t sa,unsigned int n)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
__dh_seq_detection(dh_gss_context_t ctx,OM_uint32 seqno)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
__dh_next_seqno(dh_gss_context_t ctx)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
__dh_init_seq_hist(dh_gss_context_t ctx)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
__dh_destroy_seq_hist(dh_gss_context_t ctx)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