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 }