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 * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
26 */
27
28 /*
29 * A module that implements a dummy security mechanism.
30 * It's mainly used to test GSS-API application. Multiple tokens
31 * exchanged during security context establishment can be
32 * specified through dummy_mech.conf located in /etc.
33 */
34
35 #include <sys/types.h>
36 #include <sys/modctl.h>
37 #include <sys/errno.h>
38 #include <gssapiP_dummy.h>
39 #include <gssapi_err_generic.h>
40 #include <mechglueP.h>
41 #include <gssapi/kgssapi_defs.h>
42 #include <sys/debug.h>
43
44 #ifdef DUMMY_MECH_DEBUG
45 /*
46 * Kernel kgssd module debugging aid. The global variable "dummy_mech_log"
47 * is a bit mask which allows various types of debugging messages
48 * to be printed out.
49 *
50 * dummy_mech_log & 1 will cause actual failures to be printed.
51 * dummy_mech_log & 2 will cause informational messages to be
52 * printed on the client side of kgssd.
53 * dummy_mech_log & 4 will cause informational messages to be
54 * printed on the server side of kgssd.
55 * dummy_mech_log & 8 will cause informational messages to be
56 * printed on both client and server side of kgssd.
57 */
58
59 uint_t dummy_mech_log = 1;
60 #endif
61
62 /* Local defines */
63 #define MAGIC_TOKEN_NUMBER 12345
64 /* private routines for dummy_mechanism */
65 static gss_buffer_desc make_dummy_token_msg(void *data, int datalen);
66
67 static int der_length_size(int);
68
69 static void der_write_length(unsigned char **, int);
70 static int der_read_length(unsigned char **, int *);
71 static int g_token_size(gss_OID mech, unsigned int body_size);
72 static void g_make_token_header(gss_OID mech, int body_size,
73 unsigned char **buf, int tok_type);
74 static int g_verify_token_header(gss_OID mech, int *body_size,
75 unsigned char **buf_in, int tok_type,
76 int toksize);
77
78 /* private global variables */
79 static int dummy_token_nums;
80
81 /*
82 * This OID:
83 * { iso(1) org(3) internet(6) dod(1) private(4) enterprises(1) sun(42)
84 * products(2) gssapi(26) mechtypes(1) dummy(2) }
85 */
86
87 static struct gss_config dummy_mechanism =
88 {{10, "\053\006\001\004\001\052\002\032\001\002"},
89 NULL, /* context */
90 NULL, /* next */
91 TRUE, /* uses_kmod */
92 dummy_gss_unseal,
93 dummy_gss_delete_sec_context,
94 dummy_gss_seal,
95 dummy_gss_import_sec_context,
96 dummy_gss_sign,
97 dummy_gss_verify
98 };
99
100 static gss_mechanism
101 gss_mech_initialize()
102 {
103 dprintf("Entering gss_mech_initialize\n");
104
105 if (dummy_token_nums == 0)
106 dummy_token_nums = 1;
107
108 dprintf("Leaving gss_mech_initialize\n");
109 return (&dummy_mechanism);
110 }
111
112 /*
113 * Clean up after a failed mod_install()
114 */
115 static void
116 gss_mech_fini()
117 {
118 /* Nothing to do */
119 }
120
121
122 /*
123 * Module linkage information for the kernel.
124 */
125 extern struct mod_ops mod_miscops;
126
127 static struct modlmisc modlmisc = {
128 &mod_miscops, "in-kernel dummy GSS mechanism"
129 };
130
131 static struct modlinkage modlinkage = {
132 MODREV_1,
133 { (void *)&modlmisc, NULL }
134 };
135
136 static int dummy_fini_code = EBUSY;
137
138 int
139 _init()
140 {
141 int retval;
142 gss_mechanism mech, tmp;
143
144 mech = gss_mech_initialize();
145
146 mutex_enter(&__kgss_mech_lock);
147 tmp = __kgss_get_mechanism(&mech->mech_type);
148 if (tmp != NULL) {
149 DUMMY_MECH_LOG0(8,
150 "dummy GSS mechanism: mechanism already in table.\n");
151 if (tmp->uses_kmod == TRUE) {
152 DUMMY_MECH_LOG0(8, "dummy GSS mechanism: mechanism "
153 "table supports kernel operations!\n");
154 }
155 /*
156 * keep us loaded, but let us be unloadable. This
157 * will give the developer time to trouble shoot
158 */
159 dummy_fini_code = 0;
160 } else {
161 __kgss_add_mechanism(mech);
162 ASSERT(__kgss_get_mechanism(&mech->mech_type) == mech);
163 }
164 mutex_exit(&__kgss_mech_lock);
165
166 if ((retval = mod_install(&modlinkage)) != 0)
167 gss_mech_fini(); /* clean up */
168
169 return (retval);
170 }
171
172 int
173 _fini()
174 {
175 int ret = dummy_fini_code;
176
177 if (ret == 0) {
178 ret = (mod_remove(&modlinkage));
179 }
180 return (ret);
181 }
182
183 int
184 _info(struct modinfo *modinfop)
185 {
186 return (mod_info(&modlinkage, modinfop));
187 }
188
189
190 /*ARGSUSED*/
191 static OM_uint32
192 dummy_gss_sign(context, minor_status, context_handle,
193 qop_req, message_buffer, message_token,
194 gssd_ctx_verifier)
195 void *context;
196 OM_uint32 *minor_status;
197 gss_ctx_id_t context_handle;
198 int qop_req;
199 gss_buffer_t message_buffer;
200 gss_buffer_t message_token;
201 OM_uint32 gssd_ctx_verifier;
202 {
203 dummy_gss_ctx_id_rec *ctx;
204 char token_string[] = "dummy_gss_sign";
205
206 dprintf("Entering gss_sign\n");
207
208 if (context_handle == GSS_C_NO_CONTEXT)
209 return (GSS_S_NO_CONTEXT);
210 ctx = (dummy_gss_ctx_id_rec *) context_handle;
211 ASSERT(ctx->established == 1);
212 ASSERT(ctx->token_number == MAGIC_TOKEN_NUMBER);
213
214 *message_token = make_dummy_token_msg(
215 token_string, strlen(token_string));
216
217 dprintf("Leaving gss_sign\n");
218 return (GSS_S_COMPLETE);
219 }
220
221 /*ARGSUSED*/
222 static OM_uint32
223 dummy_gss_verify(context, minor_status, context_handle,
224 message_buffer, token_buffer, qop_state,
225 gssd_ctx_verifier)
226 void *context;
227 OM_uint32 *minor_status;
228 gss_ctx_id_t context_handle;
229 gss_buffer_t message_buffer;
230 gss_buffer_t token_buffer;
231 int *qop_state;
232 OM_uint32 gssd_ctx_verifier;
233 {
234 unsigned char *ptr;
235 int bodysize;
236 int err;
237 dummy_gss_ctx_id_rec *ctx;
238
239 dprintf("Entering gss_verify\n");
240
241 if (context_handle == GSS_C_NO_CONTEXT)
242 return (GSS_S_NO_CONTEXT);
243
244 ctx = (dummy_gss_ctx_id_rec *) context_handle;
245 ASSERT(ctx->established == 1);
246 ASSERT(ctx->token_number == MAGIC_TOKEN_NUMBER);
247 /* Check for defective input token. */
248
249 ptr = (unsigned char *) token_buffer->value;
250 if (err = g_verify_token_header((gss_OID)gss_mech_dummy, &bodysize,
251 &ptr, 0,
252 token_buffer->length)) {
253 *minor_status = err;
254 return (GSS_S_DEFECTIVE_TOKEN);
255 }
256
257 *qop_state = GSS_C_QOP_DEFAULT;
258
259 dprintf("Leaving gss_verify\n");
260 return (GSS_S_COMPLETE);
261 }
262
263 /*ARGSUSED*/
264 static OM_uint32
265 dummy_gss_seal(context, minor_status, context_handle, conf_req_flag,
266 qop_req, input_message_buffer, conf_state,
267 output_message_buffer, gssd_ctx_verifier)
268 void *context;
269 OM_uint32 *minor_status;
270 gss_ctx_id_t context_handle;
271 int conf_req_flag;
272 int qop_req;
273 gss_buffer_t input_message_buffer;
274 int *conf_state;
275 gss_buffer_t output_message_buffer;
276 OM_uint32 gssd_ctx_verifier;
277 {
278 gss_buffer_desc output;
279 dummy_gss_ctx_id_rec *ctx;
280 dprintf("Entering gss_seal\n");
281
282 if (context_handle == GSS_C_NO_CONTEXT)
283 return (GSS_S_NO_CONTEXT);
284 ctx = (dummy_gss_ctx_id_rec *) context_handle;
285 ASSERT(ctx->established == 1);
286 ASSERT(ctx->token_number == MAGIC_TOKEN_NUMBER);
287 /* Copy the input message to output message */
288 output = make_dummy_token_msg(
289 input_message_buffer->value, input_message_buffer->length);
290
291 if (conf_state)
292 *conf_state = 1;
293
294 *output_message_buffer = output;
295
296 dprintf("Leaving gss_seal\n");
297 return (GSS_S_COMPLETE);
298 }
299
300 /*ARGSUSED*/
301 static OM_uint32
302 dummy_gss_unseal(context, minor_status, context_handle,
303 input_message_buffer, output_message_buffer,
304 conf_state, qop_state, gssd_ctx_verifier)
305 void *context;
306 OM_uint32 *minor_status;
307 gss_ctx_id_t context_handle;
308 gss_buffer_t input_message_buffer;
309 gss_buffer_t output_message_buffer;
310 int *conf_state;
311 int *qop_state;
312 OM_uint32 gssd_ctx_verifier;
313 {
314 gss_buffer_desc output;
315 dummy_gss_ctx_id_rec *ctx;
316 unsigned char *ptr;
317 int bodysize;
318 int err;
319
320 dprintf("Entering gss_unseal\n");
321
322 if (context_handle == GSS_C_NO_CONTEXT)
323 return (GSS_S_NO_CONTEXT);
324
325 ctx = (dummy_gss_ctx_id_rec *) context_handle;
326 ASSERT(ctx->established == 1);
327 ASSERT(ctx->token_number == MAGIC_TOKEN_NUMBER);
328
329 ptr = (unsigned char *) input_message_buffer->value;
330 if (err = g_verify_token_header((gss_OID)gss_mech_dummy, &bodysize,
331 &ptr, 0,
332 input_message_buffer->length)) {
333 *minor_status = err;
334 return (GSS_S_DEFECTIVE_TOKEN);
335 }
336 output.length = bodysize;
337 output.value = (void *)MALLOC(output.length);
338 (void) memcpy(output.value, ptr, output.length);
339
340 *output_message_buffer = output;
341 *qop_state = GSS_C_QOP_DEFAULT;
342
343 if (conf_state)
344 *conf_state = 1;
345
346 dprintf("Leaving gss_unseal\n");
347 return (GSS_S_COMPLETE);
348 }
349
350 /*ARGSUSED*/
351 OM_uint32
352 dummy_gss_import_sec_context(ct, minor_status, interprocess_token,
353 context_handle)
354 void *ct;
355 OM_uint32 *minor_status;
356 gss_buffer_t interprocess_token;
357 gss_ctx_id_t *context_handle;
358 {
359 unsigned char *ptr;
360 int bodysize;
361 int err;
362
363 /* Assume that we got ctx from the interprocess token. */
364 dummy_gss_ctx_id_t ctx;
365
366 dprintf("Entering import_sec_context\n");
367 ptr = (unsigned char *) interprocess_token->value;
368 if (err = g_verify_token_header((gss_OID)gss_mech_dummy, &bodysize,
369 &ptr, 0,
370 interprocess_token->length)) {
371 *minor_status = err;
372 return (GSS_S_DEFECTIVE_TOKEN);
373 }
374 ctx = (dummy_gss_ctx_id_t)MALLOC(sizeof (dummy_gss_ctx_id_rec));
375 ctx->token_number = MAGIC_TOKEN_NUMBER;
376 ctx->established = 1;
377
378 *context_handle = (gss_ctx_id_t)ctx;
379
380 dprintf("Leaving import_sec_context\n");
381 return (GSS_S_COMPLETE);
382 }
383
384 /*ARGSUSED*/
385 static OM_uint32
386 dummy_gss_delete_sec_context(ct, minor_status,
387 context_handle, output_token,
388 gssd_ctx_verifier)
389 void *ct;
390 OM_uint32 *minor_status;
391 gss_ctx_id_t *context_handle;
392 gss_buffer_t output_token;
393 OM_uint32 gssd_ctx_verifier;
394 {
395 dummy_gss_ctx_id_t ctx;
396
397 dprintf("Entering delete_sec_context\n");
398
399 /* Make the length to 0, so the output token is not sent to peer */
400 if (output_token) {
401 output_token->length = 0;
402 output_token->value = NULL;
403 }
404
405 if (*context_handle == GSS_C_NO_CONTEXT) {
406 *minor_status = 0;
407 return (GSS_S_COMPLETE);
408 }
409
410 ctx = (dummy_gss_ctx_id_rec *) *context_handle;
411 ASSERT(ctx->established == 1);
412 ASSERT(ctx->token_number == MAGIC_TOKEN_NUMBER);
413
414 FREE(ctx, sizeof (dummy_gss_ctx_id_rec));
415 *context_handle = GSS_C_NO_CONTEXT;
416
417 dprintf("Leaving delete_sec_context\n");
418 return (GSS_S_COMPLETE);
419 }
420
421 static int
422 der_length_size(int length)
423 {
424 if (length < (1<<7))
425 return (1);
426 else if (length < (1<<8))
427 return (2);
428 else if (length < (1<<16))
429 return (3);
430 else if (length < (1<<24))
431 return (4);
432 else
433 return (5);
434 }
435
436 static void
437 der_write_length(unsigned char ** buf, int length)
438 {
439 if (length < (1<<7)) {
440 *(*buf)++ = (unsigned char) length;
441 } else {
442 *(*buf)++ = (unsigned char) (der_length_size(length)+127);
443 if (length >= (1<<24))
444 *(*buf)++ = (unsigned char) (length>>24);
445 if (length >= (1<<16))
446 *(*buf)++ = (unsigned char) ((length>>16)&0xff);
447 if (length >= (1<<8))
448 *(*buf)++ = (unsigned char) ((length>>8)&0xff);
449 *(*buf)++ = (unsigned char) (length&0xff);
450 }
451 }
452
453 static int
454 der_read_length(buf, bufsize)
455 unsigned char **buf;
456 int *bufsize;
457 {
458 unsigned char sf;
459 int ret;
460
461 if (*bufsize < 1)
462 return (-1);
463 sf = *(*buf)++;
464 (*bufsize)--;
465 if (sf & 0x80) {
466 if ((sf &= 0x7f) > ((*bufsize)-1))
467 return (-1);
468 if (sf > DUMMY_SIZE_OF_INT)
469 return (-1);
470 ret = 0;
471 for (; sf; sf--) {
472 ret = (ret<<8) + (*(*buf)++);
473 (*bufsize)--;
474 }
475 } else {
476 ret = sf;
477 }
478
479 return (ret);
480 }
481
482 static int
483 g_token_size(mech, body_size)
484 gss_OID mech;
485 unsigned int body_size;
486 {
487 /* set body_size to sequence contents size */
488 body_size += 4 + (int)mech->length; /* NEED overflow check */
489 return (1 + der_length_size(body_size) + body_size);
490 }
491
492 static void
493 g_make_token_header(mech, body_size, buf, tok_type)
494 gss_OID mech;
495 int body_size;
496 unsigned char **buf;
497 int tok_type;
498 {
499 *(*buf)++ = 0x60;
500 der_write_length(buf, 4 + mech->length + body_size);
501 *(*buf)++ = 0x06;
502 *(*buf)++ = (unsigned char) mech->length;
503 TWRITE_STR(*buf, mech->elements, ((int)mech->length));
504 *(*buf)++ = (unsigned char) ((tok_type>>8)&0xff);
505 *(*buf)++ = (unsigned char) (tok_type&0xff);
506 }
507
508 static int
509 g_verify_token_header(mech, body_size, buf_in, tok_type, toksize)
510 gss_OID mech;
511 int *body_size;
512 unsigned char **buf_in;
513 int tok_type;
514 int toksize;
515 {
516 unsigned char *buf = *buf_in;
517 int seqsize;
518 gss_OID_desc toid;
519 int ret = 0;
520
521 if ((toksize -= 1) < 0)
522 return (G_BAD_TOK_HEADER);
523 if (*buf++ != 0x60)
524 return (G_BAD_TOK_HEADER);
525
526 if ((seqsize = der_read_length(&buf, &toksize)) < 0)
527 return (G_BAD_TOK_HEADER);
528
529 if (seqsize != toksize)
530 return (G_BAD_TOK_HEADER);
531
532 if ((toksize -= 1) < 0)
533 return (G_BAD_TOK_HEADER);
534 if (*buf++ != 0x06)
535 return (G_BAD_TOK_HEADER);
536
537 if ((toksize -= 1) < 0)
538 return (G_BAD_TOK_HEADER);
539 toid.length = *buf++;
540
541 if ((toksize -= toid.length) < 0)
542 return (G_BAD_TOK_HEADER);
543 toid.elements = buf;
544 buf += toid.length;
545
546 if (! g_OID_equal(&toid, mech))
547 ret = G_WRONG_MECH;
548
549 /*
550 * G_WRONG_MECH is not returned immediately because it's more important
551 * to return G_BAD_TOK_HEADER if the token header is in fact bad
552 */
553
554 if ((toksize -= 2) < 0)
555 return (G_BAD_TOK_HEADER);
556
557 if ((*buf++ != ((tok_type>>8)&0xff)) ||
558 (*buf++ != (tok_type&0xff)))
559 return (G_BAD_TOK_HEADER);
560
561 if (!ret) {
562 *buf_in = buf;
563 *body_size = toksize;
564 }
565
566 return (ret);
567 }
568
569 static gss_buffer_desc
570 make_dummy_token_msg(void *data, int dataLen)
571 {
572 gss_buffer_desc buffer;
573 int tlen;
574 unsigned char *t;
575 unsigned char *ptr;
576
577 if (data == NULL) {
578 buffer.length = 0;
579 buffer.value = NULL;
580 return (buffer);
581 }
582
583 tlen = g_token_size((gss_OID)gss_mech_dummy, dataLen);
584 t = (unsigned char *) MALLOC(tlen);
585 ptr = t;
586
587 g_make_token_header((gss_OID)gss_mech_dummy, dataLen, &ptr, 0);
588 (void) memcpy(ptr, data, dataLen);
589
590 buffer.length = tlen;
591 buffer.value = (void *) t;
592 return (buffer);
593 }