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 }