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