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