xref: /illumos-gate/usr/src/lib/gss_mechs/mech_dh/backend/mech/seq.c (revision a629ded1d7b2e67c2028ccbc5ba9099328cc4e1b)
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