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 }