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