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 }