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