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