1 /*
   2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 #pragma ident   "%Z%%M% %I%     %E% SMI"
   7 
   8 /* GSSAPI SASL plugin
   9  * Leif Johansson
  10  * Rob Siemborski (SASL v2 Conversion)
  11  * $Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $
  12  */
  13 /* 
  14  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
  15  *
  16  * Redistribution and use in source and binary forms, with or without
  17  * modification, are permitted provided that the following conditions
  18  * are met:
  19  *
  20  * 1. Redistributions of source code must retain the above copyright
  21  *    notice, this list of conditions and the following disclaimer. 
  22  *
  23  * 2. Redistributions in binary form must reproduce the above copyright
  24  *    notice, this list of conditions and the following disclaimer in
  25  *    the documentation and/or other materials provided with the
  26  *    distribution.
  27  *
  28  * 3. The name "Carnegie Mellon University" must not be used to
  29  *    endorse or promote products derived from this software without
  30  *    prior written permission. For permission or any other legal
  31  *    details, please contact  
  32  *      Office of Technology Transfer
  33  *      Carnegie Mellon University
  34  *      5000 Forbes Avenue
  35  *      Pittsburgh, PA  15213-3890
  36  *      (412) 268-4387, fax: (412) 268-7395
  37  *      tech-transfer@andrew.cmu.edu
  38  *
  39  * 4. Redistributions of any form whatsoever must retain the following
  40  *    acknowledgment:
  41  *    "This product includes software developed by Computing Services
  42  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
  43  *
  44  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
  45  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  46  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
  47  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  48  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  49  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  50  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  51  */
  52 
  53 #include <config.h>
  54 
  55 #ifdef HAVE_GSSAPI_H
  56 #include <gssapi.h>
  57 #else
  58 #include <gssapi/gssapi.h>
  59 #endif
  60 
  61 #ifdef WIN32
  62 #  include <winsock.h>
  63 
  64 #  ifndef R_OK
  65 #    define R_OK 04
  66 #  endif
  67 /* we also need io.h for access() prototype */
  68 #  include <io.h>
  69 #else
  70 #  include <sys/param.h>
  71 #  include <sys/socket.h>
  72 #  include <netinet/in.h>
  73 #  include <arpa/inet.h>
  74 #  include <netdb.h>
  75 #endif /* WIN32 */
  76 #include <fcntl.h>
  77 #include <stdio.h>
  78 #include <sasl.h>
  79 #include <saslutil.h>
  80 #include <saslplug.h>
  81 
  82 #include "plugin_common.h"
  83 
  84 #ifdef HAVE_UNISTD_H
  85 #include <unistd.h>
  86 #endif
  87 
  88 #include <errno.h>
  89 
  90 #ifdef WIN32
  91 /* This must be after sasl.h */
  92 # include "saslgssapi.h"
  93 #endif /* WIN32 */
  94 
  95 /*****************************  Common Section  *****************************/
  96 
  97 #ifndef _SUN_SDK_
  98 static const char plugin_id[] = "$Id: gssapi.c,v 1.75 2003/07/02 13:13:42 rjs3 Exp $";
  99 #endif /* !_SUN_SDK_ */
 100 
 101 static const char * GSSAPI_BLANK_STRING = "";
 102 
 103 #ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
 104 extern gss_OID gss_nt_service_name;
 105 #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
 106 #endif
 107 
 108 #ifdef _SUN_SDK_
 109 static int
 110 get_oid(const sasl_utils_t *utils, gss_OID *oid);
 111 #ifdef GSSAPI_PROTECT
 112 DEFINE_STATIC_MUTEX(global_mutex);
 113 #endif /* GSSAPI_PROTECT */
 114 #endif /* _SUN_SDK_ */
 115 
 116 /* GSSAPI SASL Mechanism by Leif Johansson <leifj@matematik.su.se>
 117  * inspired by the kerberos mechanism and the gssapi_server and
 118  * gssapi_client from the heimdal distribution by Assar Westerlund
 119  * <assar@sics.se> and Johan Danielsson <joda@pdc.kth.se>. 
 120  * See the configure.in file for details on dependencies.
 121  * Heimdal can be obtained from http://www.pdc.kth.se/heimdal
 122  *
 123  * Important contributions from Sam Hartman <hartmans@fundsxpress.com>.
 124  */
 125 
 126 typedef struct context {
 127     int state;
 128     
 129     gss_ctx_id_t gss_ctx;
 130     gss_name_t   client_name;
 131     gss_name_t   server_name;
 132     gss_cred_id_t server_creds;
 133     sasl_ssf_t limitssf, requiressf; /* application defined bounds, for the
 134                                         server */
 135 #ifdef _SUN_SDK_
 136     gss_cred_id_t client_creds;
 137     gss_OID     mech_oid;
 138     int         use_authid;
 139 #endif /* _SUN_SDK_ */
 140     const sasl_utils_t *utils;
 141     
 142     /* layers buffering */
 143     char *buffer;
 144 #ifdef _SUN_SDK_
 145     unsigned bufsize;
 146 #else
 147     int bufsize;
 148 #endif /* _SUN_SDK_ */
 149     char sizebuf[4];
 150 #ifdef _SUN_SDK_
 151     unsigned cursize;
 152     unsigned size;
 153 #else
 154     int cursize;
 155     int size;
 156 #endif /* _SUN_SDK_ */
 157     unsigned needsize;
 158     
 159     char *encode_buf;                /* For encoding/decoding mem management */
 160     char *decode_buf;
 161     char *decode_once_buf;
 162     unsigned encode_buf_len;
 163     unsigned decode_buf_len;
 164     unsigned decode_once_buf_len;
 165     buffer_info_t *enc_in_buf;
 166     
 167     char *out_buf;                   /* per-step mem management */
 168     unsigned out_buf_len;    
 169     
 170     char *authid; /* hold the authid between steps - server */
 171     const char *user;   /* hold the userid between steps - client */
 172 #ifdef _SUN_SDK_
 173     const char *client_authid;
 174 #endif /* _SUN_SDK_ */
 175 #ifdef _INTEGRATED_SOLARIS_
 176     void *h;
 177 #endif /* _INTEGRATED_SOLARIS_ */
 178 } context_t;
 179 
 180 enum {
 181     SASL_GSSAPI_STATE_AUTHNEG = 1,
 182     SASL_GSSAPI_STATE_SSFCAP = 2,
 183     SASL_GSSAPI_STATE_SSFREQ = 3,
 184     SASL_GSSAPI_STATE_AUTHENTICATED = 4
 185 };
 186 
 187 #ifdef _SUN_SDK_
 188 /* sasl_gss_log only logs gss_display_status() error string */
 189 #define sasl_gss_log(x,y,z) sasl_gss_seterror_(text,y,z,1)
 190 #define sasl_gss_seterror(x,y,z) sasl_gss_seterror_(text,y,z,0)
 191 static void
 192 sasl_gss_seterror_(const context_t *text, OM_uint32 maj, OM_uint32 min, 
 193         int logonly)
 194 #else
 195 static void
 196 sasl_gss_seterror(const sasl_utils_t *utils, OM_uint32 maj, OM_uint32 min)
 197 #endif /* _SUN_SDK_ */
 198 {
 199     OM_uint32 maj_stat, min_stat;
 200     gss_buffer_desc msg;
 201     OM_uint32 msg_ctx;
 202     int ret;
 203     char *out = NULL;
 204 #ifdef _SUN_SDK_
 205     unsigned len, curlen = 0;
 206     const sasl_utils_t *utils = text->utils;
 207     char *prefix = dgettext(TEXT_DOMAIN, "GSSAPI Error: ");
 208 #else
 209     size_t len, curlen = 0;
 210     const char prefix[] = "GSSAPI Error: ";
 211 #endif /* _SUN_SDK_ */
 212     
 213     if(!utils) return;
 214     
 215     len = sizeof(prefix);
 216     ret = _plug_buf_alloc(utils, &out, &curlen, 256);
 217     if(ret != SASL_OK) return;
 218     
 219     strcpy(out, prefix);
 220     
 221     msg_ctx = 0;
 222     while (1) {
 223         maj_stat = gss_display_status(&min_stat, maj,
 224 #ifdef _SUN_SDK_
 225                                       GSS_C_GSS_CODE, text->mech_oid,
 226 #else
 227                                       GSS_C_GSS_CODE, GSS_C_NULL_OID,
 228 #endif /* _SUN_SDK_ */
 229                                       &msg_ctx, &msg);
 230         if(GSS_ERROR(maj_stat)) {
 231 #ifdef _SUN_SDK_
 232             if (logonly) {
 233                 utils->log(text->utils->conn, SASL_LOG_FAIL,
 234                     "GSSAPI Failure: (could not get major error message)");
 235             } else {
 236 #endif /* _SUN_SDK_ */
 237 #ifdef _INTEGRATED_SOLARIS_
 238                 utils->seterror(utils->conn, 0,
 239                                 gettext("GSSAPI Failure "
 240                                 "(could not get major error message)"));
 241 #ifdef _SUN_SDK_
 242             }
 243 #endif /* _SUN_SDK_ */
 244 #else
 245             utils->seterror(utils->conn, 0,
 246                             "GSSAPI Failure "
 247                             "(could not get major error message)");
 248 #ifdef _SUN_SDK_
 249             }
 250 #endif /* _SUN_SDK_ */
 251 #endif /* _INTEGRATED_SOLARIS_ */
 252             utils->free(out);
 253             return;
 254         }
 255         
 256         len += len + msg.length;
 257         ret = _plug_buf_alloc(utils, &out, &curlen, len);
 258         
 259         if(ret != SASL_OK) {
 260             utils->free(out);
 261             return;
 262         }
 263         
 264         strcat(out, msg.value);
 265         
 266         gss_release_buffer(&min_stat, &msg);
 267         
 268         if (!msg_ctx)
 269             break;
 270     }
 271     
 272     /* Now get the minor status */
 273     
 274     len += 2;
 275     ret = _plug_buf_alloc(utils, &out, &curlen, len);
 276     if(ret != SASL_OK) {
 277         utils->free(out);
 278         return;
 279     }
 280     
 281     strcat(out, " (");
 282     
 283     msg_ctx = 0;
 284     while (1) {
 285         maj_stat = gss_display_status(&min_stat, min,
 286 #ifdef _SUN_SDK_
 287                                       GSS_C_MECH_CODE, text->mech_oid,
 288 #else
 289                                       GSS_C_MECH_CODE, GSS_C_NULL_OID,
 290 #endif /* _SUN_SDK_ */
 291                                       &msg_ctx, &msg);
 292         if(GSS_ERROR(maj_stat)) {
 293 #ifdef _SUN_SDK_
 294             if (logonly) {
 295                 utils->log(text->utils->conn, SASL_LOG_FAIL,
 296                     "GSSAPI Failure: (could not get minor error message)");
 297             } else {
 298 #endif /* _SUN_SDK_ */
 299 #ifdef _INTEGRATED_SOLARIS_
 300                 utils->seterror(utils->conn, 0,
 301                                 gettext("GSSAPI Failure "
 302                                 "(could not get minor error message)"));
 303 #ifdef _SUN_SDK_
 304             }
 305 #endif /* _SUN_SDK_ */
 306 #else
 307             utils->seterror(utils->conn, 0,
 308                             "GSSAPI Failure "
 309                             "(could not get minor error message)");
 310 #ifdef _SUN_SDK_
 311             }
 312 #endif /* _SUN_SDK_ */
 313 #endif /* _INTEGRATED_SOLARIS_ */
 314             utils->free(out);
 315             return;
 316         }
 317         
 318         len += len + msg.length;
 319         ret = _plug_buf_alloc(utils, &out, &curlen, len);
 320         
 321         if(ret != SASL_OK) {
 322             utils->free(out);
 323             return;
 324         }
 325         
 326         strcat(out, msg.value);
 327         
 328         gss_release_buffer(&min_stat, &msg);
 329         
 330         if (!msg_ctx)
 331             break;
 332     }
 333     
 334     len += 1;
 335     ret = _plug_buf_alloc(utils, &out, &curlen, len);
 336     if(ret != SASL_OK) {
 337         utils->free(out);
 338         return;
 339     }
 340     
 341     strcat(out, ")");
 342     
 343 #ifdef _SUN_SDK_
 344     if (logonly) {
 345         utils->log(text->utils->conn, SASL_LOG_FAIL, out);
 346     } else {
 347         utils->seterror(utils->conn, 0, out);
 348     }
 349 #else
 350     utils->seterror(utils->conn, 0, out);
 351 #endif /* _SUN_SDK_ */
 352     utils->free(out);
 353 }
 354 
 355 static int 
 356 sasl_gss_encode(void *context, const struct iovec *invec, unsigned numiov,
 357                 const char **output, unsigned *outputlen, int privacy)
 358 {
 359     context_t *text = (context_t *)context;
 360     OM_uint32 maj_stat, min_stat;
 361     gss_buffer_t input_token, output_token;
 362     gss_buffer_desc real_input_token, real_output_token;
 363     int ret;
 364     struct buffer_info *inblob, bufinfo;
 365     
 366     if(!output) return SASL_BADPARAM;
 367     
 368     if(numiov > 1) {
 369         ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf);
 370         if(ret != SASL_OK) return ret;
 371         inblob = text->enc_in_buf;
 372     } else {
 373         bufinfo.data = invec[0].iov_base;
 374         bufinfo.curlen = invec[0].iov_len;
 375         inblob = &bufinfo;
 376     }
 377     
 378     if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) return SASL_NOTDONE;
 379     
 380     input_token = &real_input_token;
 381     
 382     real_input_token.value  = inblob->data;
 383     real_input_token.length = inblob->curlen;
 384     
 385     output_token = &real_output_token;
 386     output_token->value = NULL;
 387     output_token->length = 0;
 388     
 389 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 390     if (LOCK_MUTEX(&global_mutex) < 0)
 391         return (SASL_FAIL);
 392 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 393     maj_stat = gss_wrap (&min_stat,
 394                          text->gss_ctx,
 395                          privacy,
 396                          GSS_C_QOP_DEFAULT,
 397                          input_token,
 398                          NULL,
 399                          output_token);
 400     
 401     if (GSS_ERROR(maj_stat))
 402         {
 403             sasl_gss_seterror(text->utils, maj_stat, min_stat);
 404             if (output_token->value)
 405                 gss_release_buffer(&min_stat, output_token);
 406 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 407             UNLOCK_MUTEX(&global_mutex);
 408 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 409             return SASL_FAIL;
 410         }
 411     
 412     if (output_token->value && output) {
 413         int len;
 414         
 415         ret = _plug_buf_alloc(text->utils, &(text->encode_buf),
 416                               &(text->encode_buf_len), output_token->length + 4);
 417         
 418         if (ret != SASL_OK) {
 419             gss_release_buffer(&min_stat, output_token);
 420 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 421             UNLOCK_MUTEX(&global_mutex);
 422 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 423             return ret;
 424         }
 425         
 426         len = htonl(output_token->length);
 427         memcpy(text->encode_buf, &len, 4);
 428         memcpy(text->encode_buf + 4, output_token->value, output_token->length);
 429     }
 430     
 431     if (outputlen) {
 432         *outputlen = output_token->length + 4;
 433     }
 434     
 435     *output = text->encode_buf;
 436     
 437     if (output_token->value)
 438         gss_release_buffer(&min_stat, output_token);
 439     
 440 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 441     UNLOCK_MUTEX(&global_mutex);
 442 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 443 
 444     return SASL_OK;
 445 }
 446 
 447 static int gssapi_privacy_encode(void *context, const struct iovec *invec,
 448                                  unsigned numiov, const char **output,
 449                                  unsigned *outputlen)
 450 {
 451     return sasl_gss_encode(context,invec,numiov,output,outputlen,1);
 452 }
 453 
 454 static int gssapi_integrity_encode(void *context, const struct iovec *invec,
 455                                    unsigned numiov, const char **output,
 456                                    unsigned *outputlen) 
 457 {
 458     return sasl_gss_encode(context,invec,numiov,output,outputlen,0);
 459 }
 460 
 461 #define myMIN(a,b) (((a) < (b)) ? (a) : (b))
 462 
 463 static int gssapi_decode_once(void *context,
 464                               const char **input, unsigned *inputlen,
 465                               char **output, unsigned *outputlen)
 466 {
 467     context_t *text = (context_t *) context;
 468     OM_uint32 maj_stat, min_stat;
 469     gss_buffer_t input_token, output_token;
 470     gss_buffer_desc real_input_token, real_output_token;
 471     int result;
 472     unsigned diff;
 473     
 474     if (text->state != SASL_GSSAPI_STATE_AUTHENTICATED) {
 475 #ifdef _INTEGRATED_SOLARIS_
 476         SETERROR(text->utils, gettext("GSSAPI Failure"));
 477 #else
 478         SETERROR(text->utils, "GSSAPI Failure");
 479 #endif /* _INTEGRATED_SOLARIS_ */
 480         return SASL_NOTDONE;
 481     }
 482     
 483     /* first we need to extract a packet */
 484     if (text->needsize > 0) {
 485         /* how long is it? */
 486         int tocopy = myMIN(text->needsize, *inputlen);
 487         
 488         memcpy(text->sizebuf + 4 - text->needsize, *input, tocopy);
 489         text->needsize -= tocopy;
 490         *input += tocopy;
 491         *inputlen -= tocopy;
 492         
 493         if (text->needsize == 0) {
 494             /* got the entire size */
 495             memcpy(&text->size, text->sizebuf, 4);
 496             text->size = ntohl(text->size);
 497             text->cursize = 0;
 498             
 499 #ifdef _SUN_SDK_
 500             if (text->size > 0xFFFFFF) {
 501                 text->utils->log(text->utils->conn, SASL_LOG_ERR,
 502                                  "Illegal size in sasl_gss_decode_once");
 503 #else
 504             if (text->size > 0xFFFFFF || text->size <= 0) {
 505                 SETERROR(text->utils, "Illegal size in sasl_gss_decode_once");
 506 #endif /* _SUN_SDK_ */
 507                 return SASL_FAIL;
 508             }
 509             
 510             if (text->bufsize < text->size + 5) {
 511                 result = _plug_buf_alloc(text->utils, &text->buffer,
 512                                          &(text->bufsize), text->size+5);
 513                 if(result != SASL_OK) return result;
 514             }
 515         }
 516         if (*inputlen == 0) {
 517             /* need more data ! */
 518             *outputlen = 0;
 519             *output = NULL;
 520             
 521             return SASL_OK;
 522         }
 523     }
 524     
 525     diff = text->size - text->cursize;
 526     
 527     if (*inputlen < diff) {
 528         /* ok, let's queue it up; not enough data */
 529         memcpy(text->buffer + text->cursize, *input, *inputlen);
 530         text->cursize += *inputlen;
 531         *inputlen = 0;
 532         *outputlen = 0;
 533         *output = NULL;
 534         return SASL_OK;
 535     } else {
 536         memcpy(text->buffer + text->cursize, *input, diff);
 537         *input += diff;
 538         *inputlen -= diff;
 539     }
 540     
 541     input_token = &real_input_token; 
 542     real_input_token.value = text->buffer;
 543     real_input_token.length = text->size;
 544     
 545     output_token = &real_output_token;
 546     output_token->value = NULL;
 547     output_token->length = 0;
 548     
 549 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 550     if (LOCK_MUTEX(&global_mutex) < 0)
 551         return (SASL_FAIL);
 552 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 553 
 554     maj_stat = gss_unwrap (&min_stat,
 555                            text->gss_ctx,
 556                            input_token,
 557                            output_token,
 558                            NULL,
 559                            NULL);
 560     
 561     if (GSS_ERROR(maj_stat))
 562         {
 563             sasl_gss_seterror(text->utils, maj_stat, min_stat);
 564             if (output_token->value)
 565                 gss_release_buffer(&min_stat, output_token);
 566 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 567             UNLOCK_MUTEX(&global_mutex);
 568 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 569             return SASL_FAIL;
 570         }
 571     
 572     if (outputlen)
 573         *outputlen = output_token->length;
 574     
 575     if (output_token->value) {
 576         if (output) {
 577             result = _plug_buf_alloc(text->utils, &text->decode_once_buf,
 578                                      &text->decode_once_buf_len,
 579                                      *outputlen);
 580             if(result != SASL_OK) {
 581                 gss_release_buffer(&min_stat, output_token);
 582 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 583             UNLOCK_MUTEX(&global_mutex);
 584 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 585                 return result;
 586             }
 587             *output = text->decode_once_buf;
 588             memcpy(*output, output_token->value, *outputlen);
 589         }
 590         gss_release_buffer(&min_stat, output_token);
 591     }
 592 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 593             UNLOCK_MUTEX(&global_mutex);
 594 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 595     
 596     /* reset for the next packet */
 597 #ifndef _SUN_SDK_
 598     text->size = -1;
 599 #endif /* !_SUN_SDK_ */
 600     text->needsize = 4;
 601     
 602     return SASL_OK;
 603 }
 604 
 605 static int gssapi_decode(void *context,
 606                          const char *input, unsigned inputlen,
 607                          const char **output, unsigned *outputlen)
 608 {
 609     context_t *text = (context_t *) context;
 610     int ret;
 611     
 612     ret = _plug_decode(text->utils, context, input, inputlen,
 613                        &text->decode_buf, &text->decode_buf_len, outputlen,
 614                        gssapi_decode_once);
 615     
 616     *output = text->decode_buf;
 617     
 618     return ret;
 619 }
 620 
 621 static context_t *gss_new_context(const sasl_utils_t *utils)
 622 {
 623     context_t *ret;
 624     
 625     ret = utils->malloc(sizeof(context_t));
 626     if(!ret) return NULL;
 627     
 628     memset(ret,0,sizeof(context_t));
 629     ret->utils = utils;
 630 #ifdef _SUN_SDK_
 631     ret->gss_ctx = GSS_C_NO_CONTEXT;
 632     ret->client_name = GSS_C_NO_NAME;
 633     ret->server_name = GSS_C_NO_NAME;
 634     ret->server_creds = GSS_C_NO_CREDENTIAL;
 635     ret->client_creds = GSS_C_NO_CREDENTIAL;
 636     if (get_oid(utils, &ret->mech_oid) != SASL_OK) {
 637         utils->free(ret);
 638         return (NULL);
 639     }
 640 #endif /* _SUN_SDK_ */
 641     
 642     ret->needsize = 4;
 643     
 644     return ret;
 645 }
 646 
 647 static void sasl_gss_free_context_contents(context_t *text)
 648 {
 649     OM_uint32 maj_stat, min_stat;
 650     
 651     if (!text) return;
 652     
 653     if (text->gss_ctx != GSS_C_NO_CONTEXT) {
 654         maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER);
 655         text->gss_ctx = GSS_C_NO_CONTEXT;
 656     }
 657     
 658     if (text->client_name != GSS_C_NO_NAME) {
 659         maj_stat = gss_release_name(&min_stat,&text->client_name);
 660         text->client_name = GSS_C_NO_NAME;
 661     }
 662     
 663     if (text->server_name != GSS_C_NO_NAME) {
 664         maj_stat = gss_release_name(&min_stat,&text->server_name);
 665         text->server_name = GSS_C_NO_NAME;
 666     }
 667     
 668     if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
 669         maj_stat = gss_release_cred(&min_stat, &text->server_creds);
 670         text->server_creds = GSS_C_NO_CREDENTIAL;
 671     }
 672 
 673 #ifdef _SUN_SDK_
 674     if ( text->client_creds != GSS_C_NO_CREDENTIAL) {
 675         maj_stat = gss_release_cred(&min_stat, &text->client_creds);
 676         text->client_creds = GSS_C_NO_CREDENTIAL;
 677     }
 678 
 679     /*
 680      * Note that the oid returned by rpc_gss_mech_to_oid should not
 681      * be released
 682      */
 683 #endif /* _SUN_SDK_ */
 684     
 685     if (text->out_buf) {
 686         text->utils->free(text->out_buf);
 687         text->out_buf = NULL;
 688     }
 689     
 690     if (text->encode_buf) {
 691         text->utils->free(text->encode_buf);
 692         text->encode_buf = NULL;
 693     }
 694     
 695     if (text->decode_buf) {
 696         text->utils->free(text->decode_buf);
 697         text->decode_buf = NULL;
 698     }
 699     
 700     if (text->decode_once_buf) {
 701         text->utils->free(text->decode_once_buf);
 702         text->decode_once_buf = NULL;
 703     }
 704     
 705     if (text->enc_in_buf) {
 706         if(text->enc_in_buf->data) text->utils->free(text->enc_in_buf->data);
 707         text->utils->free(text->enc_in_buf);
 708         text->enc_in_buf = NULL;
 709     }
 710     
 711     if (text->buffer) {
 712         text->utils->free(text->buffer);
 713         text->buffer = NULL;
 714     }
 715     
 716     if (text->authid) { /* works for both client and server */
 717         text->utils->free(text->authid);
 718         text->authid = NULL;
 719     }
 720 }
 721 
 722 #ifdef _SUN_SDK_
 723 
 724 #ifdef HAVE_RPC_GSS_MECH_TO_OID
 725 #include <rpc/rpcsec_gss.h>
 726 #endif /* HAVE_RPC_GSS_MECH_TO_OID */
 727 
 728 static int
 729 get_oid(const sasl_utils_t *utils, gss_OID *oid)
 730 {
 731 #ifdef HAVE_RPC_GSS_MECH_TO_OID
 732     static gss_OID_desc kerb_v5 =
 733         {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
 734         /* 1.2.840.113554.1.2.2 */
 735     *oid = &kerb_v5;
 736 #endif /* HAVE_RPC_GSS_MECH_TO_OID */
 737     return (SASL_OK);
 738 }
 739 
 740 static int
 741 add_mech_to_set(context_t *text, gss_OID_set *desired_mechs)
 742 {
 743     OM_uint32 maj_stat, min_stat;
 744 
 745     maj_stat = gss_create_empty_oid_set(&min_stat, desired_mechs);
 746 
 747     if (GSS_ERROR(maj_stat)) {
 748         sasl_gss_seterror(text->utils, maj_stat, min_stat);
 749         sasl_gss_free_context_contents(text);
 750         return SASL_FAIL;
 751     }
 752 
 753     maj_stat = gss_add_oid_set_member(&min_stat, text->mech_oid, desired_mechs);
 754     if (GSS_ERROR(maj_stat)) {
 755         sasl_gss_seterror(text->utils, maj_stat, min_stat);
 756         sasl_gss_free_context_contents(text);
 757         (void) gss_release_oid_set(&min_stat, desired_mechs);
 758         return SASL_FAIL;
 759     }
 760     return SASL_OK;
 761 }
 762 #endif /* _SUN_SDK_ */
 763 
 764 static void gssapi_common_mech_dispose(void *conn_context,
 765                                        const sasl_utils_t *utils)
 766 {
 767 #ifdef _SUN_SDK_
 768     if (conn_context == NULL)
 769         return;
 770 #ifdef _INTEGRATED_SOLARIS_
 771     convert_prompt(utils, &((context_t *)conn_context)->h, NULL);
 772 #endif /* _INTEGRATED_SOLARIS_ */
 773 #endif /* _SUN_SDK_ */
 774 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 775     (void) LOCK_MUTEX(&global_mutex);
 776 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 777     sasl_gss_free_context_contents((context_t *)(conn_context));
 778 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 779     UNLOCK_MUTEX(&global_mutex);
 780 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 781     utils->free(conn_context);
 782 }
 783 
 784 /*****************************  Server Section  *****************************/
 785 
 786 static int 
 787 gssapi_server_mech_new(void *glob_context __attribute__((unused)), 
 788                        sasl_server_params_t *params,
 789                        const char *challenge __attribute__((unused)), 
 790                        unsigned challen __attribute__((unused)),
 791                        void **conn_context)
 792 {
 793     context_t *text;
 794     
 795 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 796     if (LOCK_MUTEX(&global_mutex) < 0)
 797         return (SASL_FAIL);
 798 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 799     text = gss_new_context(params->utils);
 800 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
 801     UNLOCK_MUTEX(&global_mutex);
 802 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
 803     if (text == NULL) {
 804 #ifndef _SUN_SDK_
 805         MEMERROR(params->utils);
 806 #endif /* !_SUN_SDK_ */
 807         return SASL_NOMEM;
 808     }
 809     
 810     text->gss_ctx = GSS_C_NO_CONTEXT;
 811     text->client_name = GSS_C_NO_NAME;
 812     text->server_name = GSS_C_NO_NAME;
 813     text->server_creds = GSS_C_NO_CREDENTIAL;
 814     text->state = SASL_GSSAPI_STATE_AUTHNEG;
 815     
 816     *conn_context = text;
 817     
 818     return SASL_OK;
 819 }
 820 
 821 static int 
 822 gssapi_server_mech_step(void *conn_context,
 823                         sasl_server_params_t *params,
 824                         const char *clientin,
 825                         unsigned clientinlen,
 826                         const char **serverout,
 827                         unsigned *serveroutlen,
 828                         sasl_out_params_t *oparams)
 829 {
 830     context_t *text = (context_t *)conn_context;
 831     gss_buffer_t input_token, output_token;
 832     gss_buffer_desc real_input_token, real_output_token;
 833     OM_uint32 maj_stat, min_stat;
 834 #ifdef _SUN_SDK_
 835     OM_uint32 max_input_size;
 836     gss_OID_set desired_mechs = GSS_C_NULL_OID_SET;
 837 #endif /* _SUN_SDK_ */
 838     gss_buffer_desc name_token;
 839     int ret;
 840     
 841     input_token = &real_input_token;
 842     output_token = &real_output_token;
 843     output_token->value = NULL; output_token->length = 0;
 844     input_token->value = NULL; input_token->length = 0;
 845     
 846     if(!serverout) {
 847         PARAMERROR(text->utils);
 848         return SASL_BADPARAM;
 849     }
 850     
 851     *serverout = NULL;
 852     *serveroutlen = 0;  
 853             
 854     switch (text->state) {
 855 
 856     case SASL_GSSAPI_STATE_AUTHNEG:
 857         if (text->server_name == GSS_C_NO_NAME) { /* only once */
 858             name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
 859             name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char));
 860             if (name_token.value == NULL) {
 861                 MEMERROR(text->utils);
 862                 sasl_gss_free_context_contents(text);
 863                 return SASL_NOMEM;
 864             }
 865 #ifdef _SUN_SDK_
 866             snprintf(name_token.value, name_token.length + 1,
 867                 "%s@%s", params->service, params->serverFQDN);
 868 #else
 869             sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN);
 870 #endif /* _SUN_SDK_ */
 871             
 872             maj_stat = gss_import_name (&min_stat,
 873                                         &name_token,
 874                                         GSS_C_NT_HOSTBASED_SERVICE,
 875                                         &text->server_name);
 876             
 877             params->utils->free(name_token.value);
 878             name_token.value = NULL;
 879             
 880             if (GSS_ERROR(maj_stat)) {
 881                 sasl_gss_seterror(text->utils, maj_stat, min_stat);
 882                 sasl_gss_free_context_contents(text);
 883                 return SASL_FAIL;
 884             }
 885             
 886             if ( text->server_creds != GSS_C_NO_CREDENTIAL) {
 887                 maj_stat = gss_release_cred(&min_stat, &text->server_creds);
 888                 text->server_creds = GSS_C_NO_CREDENTIAL;
 889             }
 890             
 891 #ifdef _SUN_SDK_
 892             if (text->mech_oid != GSS_C_NULL_OID) {
 893                 ret = add_mech_to_set(text, &desired_mechs);
 894                 if (ret != SASL_OK)
 895                     return (ret);
 896             }
 897 #endif /* _SUN_SDK_ */
 898 
 899             maj_stat = gss_acquire_cred(&min_stat, 
 900                                         text->server_name,
 901                                         GSS_C_INDEFINITE, 
 902 #ifdef _SUN_SDK_
 903                                         desired_mechs,
 904 #else
 905                                         GSS_C_NO_OID_SET,
 906 #endif /* _SUN_SDK_ */
 907                                         GSS_C_ACCEPT,
 908                                         &text->server_creds, 
 909                                         NULL, 
 910                                         NULL);
 911             
 912 #ifdef _SUN_SDK_
 913             if (desired_mechs != GSS_C_NULL_OID_SET) {
 914                 OM_uint32 min_stat2;
 915                 (void) gss_release_oid_set(&min_stat2, &desired_mechs);
 916             }
 917 #endif /* _SUN_SDK_ */
 918 
 919             if (GSS_ERROR(maj_stat)) {
 920                 sasl_gss_seterror(text->utils, maj_stat, min_stat);
 921                 sasl_gss_free_context_contents(text);
 922                 return SASL_FAIL;
 923             }
 924         }
 925         
 926         if (clientinlen) {
 927             real_input_token.value = (void *)clientin;
 928             real_input_token.length = clientinlen;
 929         }
 930         
 931         maj_stat =
 932             gss_accept_sec_context(&min_stat,
 933                                    &(text->gss_ctx),
 934                                    text->server_creds,
 935                                    input_token,
 936                                    GSS_C_NO_CHANNEL_BINDINGS,
 937                                    &text->client_name,
 938                                    NULL,
 939                                    output_token,
 940                                    NULL,
 941                                    NULL,
 942                                    NULL);
 943         
 944         if (GSS_ERROR(maj_stat)) {
 945 #ifdef _SUN_SDK_
 946             /* log the local error info, set a more generic error */
 947             sasl_gss_log(text->utils, maj_stat, min_stat);
 948             text->utils->seterror(text->utils->conn, SASL_NOLOG, 
 949                     gettext("GSSAPI Failure: accept security context error"));
 950             if (output_token->value) {
 951                 gss_release_buffer(&min_stat, output_token);
 952             }
 953 #else
 954             if (output_token->value) {
 955                 gss_release_buffer(&min_stat, output_token);
 956             }
 957             text->utils->seterror(text->utils->conn, SASL_NOLOG, "GSSAPI Failure: gss_accept_sec_context");
 958             text->utils->log(NULL, SASL_LOG_DEBUG, "GSSAPI Failure: gss_accept_sec_context");
 959 #endif /* _SUN_SDK_ */
 960             sasl_gss_free_context_contents(text);
 961             return SASL_BADAUTH;
 962         }
 963             
 964         if (serveroutlen)
 965             *serveroutlen = output_token->length;
 966         if (output_token->value) {
 967             if (serverout) {
 968                 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
 969                                       &(text->out_buf_len), *serveroutlen);
 970                 if(ret != SASL_OK) {
 971                     gss_release_buffer(&min_stat, output_token);
 972                     return ret;
 973                 }
 974                 memcpy(text->out_buf, output_token->value, *serveroutlen);
 975                 *serverout = text->out_buf;
 976             }
 977             
 978             gss_release_buffer(&min_stat, output_token);
 979         } else {
 980             /* No output token, send an empty string */
 981             *serverout = GSSAPI_BLANK_STRING;
 982 #ifndef _SUN_SDK_
 983             serveroutlen = 0;
 984 #endif /* !_SUN_SDK_ */
 985         }
 986         
 987         
 988         if (maj_stat == GSS_S_COMPLETE) {
 989             /* Switch to ssf negotiation */
 990             text->state = SASL_GSSAPI_STATE_SSFCAP;
 991         }
 992         
 993         return SASL_CONTINUE;
 994 
 995     case SASL_GSSAPI_STATE_SSFCAP: {
 996         unsigned char sasldata[4];
 997         gss_buffer_desc name_token;
 998 #ifndef _SUN_SDK_
 999         gss_buffer_desc name_without_realm;
1000         gss_name_t without = NULL;
1001         int equal;
1002 #endif /* !_SUN_SDK_ */
1003         
1004         name_token.value = NULL;
1005 #ifndef _SUN_SDK_
1006         name_without_realm.value = NULL;
1007 #endif /* !_SUN_SDK_ */
1008         
1009         /* We ignore whatever the client sent us at this stage */
1010         
1011         maj_stat = gss_display_name (&min_stat,
1012                                      text->client_name,
1013                                      &name_token,
1014                                      NULL);
1015         
1016         if (GSS_ERROR(maj_stat)) {
1017 #ifndef _SUN_SDK_
1018             if (name_without_realm.value)
1019                 params->utils->free(name_without_realm.value);
1020 #endif /* !_SUN_SDK_ */
1021             
1022             if (name_token.value)
1023                 gss_release_buffer(&min_stat, &name_token);
1024 #ifndef _SUN_SDK_
1025             if (without)
1026                 gss_release_name(&min_stat, &without);
1027 #endif /* !_SUN_SDK_ */
1028 #ifdef _INTEGRATED_SOLARIS_
1029             SETERROR(text->utils, gettext("GSSAPI Failure"));
1030 #else
1031             SETERROR(text->utils, "GSSAPI Failure");
1032 #endif /* _INTEGRATED_SOLARIS_ */
1033             sasl_gss_free_context_contents(text);
1034             return SASL_BADAUTH;
1035         }
1036         
1037 #ifndef _SUN_SDK_
1038         /* If the id contains a realm get the identifier for the user
1039            without the realm and see if it's the same id (i.e. 
1040            tmartin == tmartin@ANDREW.CMU.EDU. If this is the case we just want
1041            to return the id (i.e. just "tmartin" */
1042         if (strchr((char *) name_token.value, (int) '@') != NULL) {
1043             /* NOTE: libc malloc, as it is freed below by a gssapi internal
1044              *       function! */
1045             name_without_realm.value = malloc(strlen(name_token.value)+1);
1046             if (name_without_realm.value == NULL) {
1047                 MEMERROR(text->utils);
1048                 return SASL_NOMEM;
1049             }
1050             
1051             strcpy(name_without_realm.value, name_token.value);
1052             
1053             /* cut off string at '@' */
1054             (strchr(name_without_realm.value,'@'))[0] = '\0';
1055             
1056             name_without_realm.length = strlen( (char *) name_without_realm.value );
1057             
1058             maj_stat = gss_import_name (&min_stat,
1059                                         &name_without_realm,
1060             /* Solaris 8/9 gss_import_name doesn't accept GSS_C_NULL_OID here,
1061                so use GSS_C_NT_USER_NAME instead if available.  */
1062 #ifdef HAVE_GSS_C_NT_USER_NAME
1063                                         GSS_C_NT_USER_NAME,
1064 #else
1065                                         GSS_C_NULL_OID,
1066 #endif
1067                                         &without);
1068             
1069             if (GSS_ERROR(maj_stat)) {
1070                 params->utils->free(name_without_realm.value);
1071                 if (name_token.value)
1072                     gss_release_buffer(&min_stat, &name_token);
1073                 if (without)
1074                     gss_release_name(&min_stat, &without);
1075                 SETERROR(text->utils, "GSSAPI Failure");
1076                 sasl_gss_free_context_contents(text);
1077                 return SASL_BADAUTH;
1078             }
1079             
1080             maj_stat = gss_compare_name(&min_stat,
1081                                         text->client_name,
1082                                         without,
1083                                         &equal);
1084             
1085             if (GSS_ERROR(maj_stat)) {
1086                 params->utils->free(name_without_realm.value);
1087                 if (name_token.value)
1088                     gss_release_buffer(&min_stat, &name_token);
1089                 if (without)
1090                     gss_release_name(&min_stat, &without);
1091                 SETERROR(text->utils, "GSSAPI Failure");
1092                 sasl_gss_free_context_contents(text);
1093                 return SASL_BADAUTH;
1094             }
1095             
1096             gss_release_name(&min_stat,&without);
1097         } else {
1098             equal = 0;
1099         }
1100         
1101         if (equal) {
1102             text->authid = strdup(name_without_realm.value);
1103             
1104             if (text->authid == NULL) {
1105                 MEMERROR(params->utils);
1106                 return SASL_NOMEM;
1107             }
1108         } else {
1109             text->authid = strdup(name_token.value);
1110             
1111             if (text->authid == NULL) {
1112                 MEMERROR(params->utils);
1113                 return SASL_NOMEM;
1114             }
1115         }
1116 #else
1117         {
1118             ret = _plug_strdup(params->utils, name_token.value,
1119                 &text->authid, NULL);
1120         }
1121 #endif /* _SUN_SDK_ */
1122         
1123         if (name_token.value)
1124             gss_release_buffer(&min_stat, &name_token);
1125 
1126 #ifdef _SUN_SDK_
1127         if (ret != SASL_OK)
1128             return (ret);
1129 #else
1130         if (name_without_realm.value)
1131             params->utils->free(name_without_realm.value);
1132 #endif /* _SUN_SDK_ */
1133         
1134         
1135         /* we have to decide what sort of encryption/integrity/etc.,
1136            we support */
1137         if (params->props.max_ssf < params->external_ssf) {
1138             text->limitssf = 0;
1139         } else {
1140             text->limitssf = params->props.max_ssf - params->external_ssf;
1141         }
1142         if (params->props.min_ssf < params->external_ssf) {
1143             text->requiressf = 0;
1144         } else {
1145             text->requiressf = params->props.min_ssf - params->external_ssf;
1146         }
1147         
1148         /* build up our security properties token */
1149         if (params->props.maxbufsize > 0xFFFFFF) {
1150             /* make sure maxbufsize isn't too large */
1151             /* maxbufsize = 0xFFFFFF */
1152             sasldata[1] = sasldata[2] = sasldata[3] = 0xFF;
1153         } else {
1154             sasldata[1] = (params->props.maxbufsize >> 16) & 0xFF;
1155             sasldata[2] = (params->props.maxbufsize >> 8) & 0xFF;
1156             sasldata[3] = (params->props.maxbufsize >> 0) & 0xFF;
1157         }
1158         sasldata[0] = 0;
1159         if(text->requiressf != 0 && !params->props.maxbufsize) {
1160 #ifdef _SUN_SDK_
1161             params->utils->log(params->utils->conn, SASL_LOG_ERR,
1162                 "GSSAPI needs a security layer but one is forbidden");
1163 #else
1164             params->utils->seterror(params->utils->conn, 0,
1165                                     "GSSAPI needs a security layer but one is forbidden");
1166 #endif /* _SUN_SDK_ */
1167             return SASL_TOOWEAK;
1168         }
1169         
1170         if (text->requiressf == 0) {
1171             sasldata[0] |= 1; /* authentication */
1172         }
1173         if (text->requiressf <= 1 && text->limitssf >= 1
1174             && params->props.maxbufsize) {
1175             sasldata[0] |= 2;
1176         }
1177         if (text->requiressf <= 56 && text->limitssf >= 56
1178             && params->props.maxbufsize) {
1179             sasldata[0] |= 4;
1180         }
1181         
1182         real_input_token.value = (void *)sasldata;
1183         real_input_token.length = 4;
1184         
1185         maj_stat = gss_wrap(&min_stat,
1186                             text->gss_ctx,
1187                             0, /* Just integrity checking here */
1188                             GSS_C_QOP_DEFAULT,
1189                             input_token,
1190                             NULL,
1191                             output_token);
1192         
1193         if (GSS_ERROR(maj_stat)) {
1194             sasl_gss_seterror(text->utils, maj_stat, min_stat);
1195             if (output_token->value)
1196                 gss_release_buffer(&min_stat, output_token);
1197             sasl_gss_free_context_contents(text);
1198             return SASL_FAIL;
1199         }
1200         
1201         
1202         if (serveroutlen)
1203             *serveroutlen = output_token->length;
1204         if (output_token->value) {
1205             if (serverout) {
1206                 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1207                                       &(text->out_buf_len), *serveroutlen);
1208                 if(ret != SASL_OK) {
1209                     gss_release_buffer(&min_stat, output_token);
1210                     return ret;
1211                 }
1212                 memcpy(text->out_buf, output_token->value, *serveroutlen);
1213                 *serverout = text->out_buf;
1214             }
1215             
1216             gss_release_buffer(&min_stat, output_token);
1217         }
1218         
1219         /* Wait for ssf request and authid */
1220         text->state = SASL_GSSAPI_STATE_SSFREQ; 
1221         
1222         return SASL_CONTINUE;
1223     }
1224 
1225     case SASL_GSSAPI_STATE_SSFREQ: {
1226         int layerchoice;
1227         
1228         real_input_token.value = (void *)clientin;
1229         real_input_token.length = clientinlen;
1230         
1231         maj_stat = gss_unwrap(&min_stat,
1232                               text->gss_ctx,
1233                               input_token,
1234                               output_token,
1235                               NULL,
1236                               NULL);
1237         
1238         if (GSS_ERROR(maj_stat)) {
1239             sasl_gss_seterror(text->utils, maj_stat, min_stat);
1240             sasl_gss_free_context_contents(text);
1241             return SASL_FAIL;
1242         }
1243         
1244         layerchoice = (int)(((char *)(output_token->value))[0]);
1245         if (layerchoice == 1 && text->requiressf == 0) { /* no encryption */
1246             oparams->encode = NULL;
1247             oparams->decode = NULL;
1248             oparams->mech_ssf = 0;
1249         } else if (layerchoice == 2 && text->requiressf <= 1 &&
1250                    text->limitssf >= 1) { /* integrity */
1251             oparams->encode=&gssapi_integrity_encode;
1252             oparams->decode=&gssapi_decode;
1253             oparams->mech_ssf=1;
1254         } else if (layerchoice == 4 && text->requiressf <= 56 &&
1255                    text->limitssf >= 56) { /* privacy */
1256             oparams->encode = &gssapi_privacy_encode;
1257             oparams->decode = &gssapi_decode;
1258             oparams->mech_ssf = 56;
1259         } else {
1260             /* not a supported encryption layer */
1261 #ifdef _SUN_SDK_
1262             text->utils->log(text->utils->conn, SASL_LOG_ERR,
1263                 "protocol violation: client requested invalid layer");
1264 #else
1265             SETERROR(text->utils,
1266                      "protocol violation: client requested invalid layer");
1267 #endif /* _SUN_SDK_ */
1268             /* Mark that we attempted negotiation */
1269             oparams->mech_ssf = 2;
1270             if (output_token->value)
1271                 gss_release_buffer(&min_stat, output_token);
1272             sasl_gss_free_context_contents(text);
1273             return SASL_FAIL;
1274         }
1275         
1276         if (output_token->length > 4) {
1277             int ret;
1278             
1279             ret = params->canon_user(params->utils->conn,
1280                                      ((char *) output_token->value) + 4,
1281                                      (output_token->length - 4) * sizeof(char),
1282                                      SASL_CU_AUTHZID, oparams);
1283             
1284             if (ret != SASL_OK) {
1285                 sasl_gss_free_context_contents(text);
1286                 return ret;
1287             }
1288             
1289             ret = params->canon_user(params->utils->conn,
1290                                      text->authid,
1291                                      0, /* strlen(text->authid) */
1292                                      SASL_CU_AUTHID, oparams);
1293             if (ret != SASL_OK) {
1294                 sasl_gss_free_context_contents(text);
1295                 return ret;
1296             }
1297         } else if(output_token->length == 4) {
1298             /* null authzid */
1299             int ret;
1300             
1301             ret = params->canon_user(params->utils->conn,
1302                                      text->authid,
1303                                      0, /* strlen(text->authid) */
1304                                      SASL_CU_AUTHZID | SASL_CU_AUTHID,
1305                                      oparams);
1306             
1307             if (ret != SASL_OK) {
1308                 sasl_gss_free_context_contents(text);
1309                 return ret;
1310             }       
1311         } else {
1312 #ifdef _SUN_SDK_
1313             text->utils->log(text->utils->conn, SASL_LOG_ERR,
1314                              "token too short");
1315 #else
1316             SETERROR(text->utils,
1317                      "token too short");
1318 #endif /* _SUN_SDK_ */
1319             gss_release_buffer(&min_stat, output_token);
1320             sasl_gss_free_context_contents(text);
1321             return SASL_FAIL;
1322         }       
1323         
1324         /* No matter what, set the rest of the oparams */
1325         oparams->maxoutbuf =
1326             (((unsigned char *) output_token->value)[1] << 16) |
1327             (((unsigned char *) output_token->value)[2] << 8) |
1328             (((unsigned char *) output_token->value)[3] << 0);
1329 
1330 #ifdef _SUN_SDK_
1331         if (oparams->mech_ssf) {
1332             oparams->maxoutbuf -= 4; /* Allow for 4 byte tag */
1333             maj_stat = gss_wrap_size_limit(&min_stat,
1334                                         text->gss_ctx,
1335                                         oparams->mech_ssf > 1,
1336                                         GSS_C_QOP_DEFAULT,
1337                                         oparams->maxoutbuf,
1338                                         &max_input_size);
1339             if (GSS_ERROR(maj_stat)) {
1340                 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1341                 (void) gss_release_buffer(&min_stat, output_token);
1342                 sasl_gss_free_context_contents(text);
1343                 return (SASL_FAIL);
1344             }
1345 
1346             /*
1347              * gss_wrap_size_limit will return very big sizes for
1348              * small input values
1349              */
1350             if (max_input_size < oparams->maxoutbuf)
1351                 oparams->maxoutbuf = max_input_size;
1352             else {
1353                 oparams->maxoutbuf = 0;
1354             }
1355         }
1356 #else
1357         if (oparams->mech_ssf) {
1358             /* xxx this is probably too big */
1359             oparams->maxoutbuf -= 50;
1360         }
1361 #endif /* _SUN_SDK_ */
1362         
1363         gss_release_buffer(&min_stat, output_token);
1364         
1365         text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
1366         
1367         oparams->doneflag = 1;
1368         
1369         return SASL_OK;
1370     }
1371     
1372     default:
1373 #ifdef _SUN_SDK_
1374         params->utils->log(text->utils->conn, SASL_LOG_ERR,
1375                            "Invalid GSSAPI server step %d", text->state);
1376 #else
1377         params->utils->log(NULL, SASL_LOG_ERR,
1378                            "Invalid GSSAPI server step %d\n", text->state);
1379 #endif /* _SUN_SDK_ */
1380         return SASL_FAIL;
1381     }
1382     
1383 #ifndef _SUN_SDK_
1384     return SASL_FAIL; /* should never get here */
1385 #endif /* !_SUN_SDK_ */
1386 }
1387 
1388 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
1389 static int 
1390 _gssapi_server_mech_step(void *conn_context,
1391                         sasl_server_params_t *params,
1392                         const char *clientin,
1393                         unsigned clientinlen,
1394                         const char **serverout,
1395                         unsigned *serveroutlen,
1396                         sasl_out_params_t *oparams)
1397 {
1398     int ret;
1399 
1400     if (LOCK_MUTEX(&global_mutex) < 0)
1401         return (SASL_FAIL);
1402 
1403     ret = gssapi_server_mech_step(conn_context, params, clientin, clientinlen,
1404         serverout, serveroutlen, oparams);
1405 
1406     UNLOCK_MUTEX(&global_mutex);
1407     return (ret);
1408 }
1409 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
1410 
1411 static sasl_server_plug_t gssapi_server_plugins[] = 
1412 {
1413     {
1414         "GSSAPI",                       /* mech_name */
1415         56,                             /* max_ssf */
1416         SASL_SEC_NOPLAINTEXT
1417         | SASL_SEC_NOACTIVE
1418         | SASL_SEC_NOANONYMOUS
1419         | SASL_SEC_MUTUAL_AUTH,         /* security_flags */
1420         SASL_FEAT_WANT_CLIENT_FIRST
1421         | SASL_FEAT_ALLOWS_PROXY,       /* features */
1422         NULL,                           /* glob_context */
1423         &gssapi_server_mech_new,    /* mech_new */
1424 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
1425         &_gssapi_server_mech_step,  /* mech_step */
1426 #else
1427         &gssapi_server_mech_step,   /* mech_step */
1428 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
1429         &gssapi_common_mech_dispose,        /* mech_dispose */
1430         NULL,                           /* mech_free */
1431         NULL,                           /* setpass */
1432         NULL,                           /* user_query */
1433         NULL,                           /* idle */
1434         NULL,                           /* mech_avail */
1435         NULL                            /* spare */
1436     }
1437 };
1438 
1439 int gssapiv2_server_plug_init(
1440 #ifndef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1441     const sasl_utils_t *utils __attribute__((unused)),
1442 #else
1443     const sasl_utils_t *utils,
1444 #endif 
1445     int maxversion,
1446     int *out_version,
1447     sasl_server_plug_t **pluglist,
1448     int *plugcount)
1449 {
1450 #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1451     const char *keytab = NULL;
1452     char keytab_path[1024];
1453     unsigned int rl;
1454 #endif
1455     
1456     if (maxversion < SASL_SERVER_PLUG_VERSION) {
1457         return SASL_BADVERS;
1458     }
1459     
1460 #ifndef _SUN_SDK_
1461 #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
1462     /* unfortunately, we don't check for readability of keytab if it's
1463        the standard one, since we don't know where it is */
1464     
1465     /* FIXME: This code is broken */
1466     
1467     utils->getopt(utils->getopt_context, "GSSAPI", "keytab", &keytab, &rl);
1468     if (keytab != NULL) {
1469         if (access(keytab, R_OK) != 0) {
1470             utils->log(NULL, SASL_LOG_ERR,
1471                        "Could not find keytab file: %s: %m",
1472                        keytab, errno);
1473             return SASL_FAIL;
1474         }
1475         
1476         if(strlen(keytab) > 1024) {
1477             utils->log(NULL, SASL_LOG_ERR,
1478                        "path to keytab is > 1024 characters");
1479             return SASL_BUFOVER;
1480         }
1481         
1482         strncpy(keytab_path, keytab, 1024);
1483         
1484         gsskrb5_register_acceptor_identity(keytab_path);
1485     }
1486 #endif
1487 #endif /* !_SUN_SDK_ */
1488     
1489 #ifdef _INTEGRATED_SOLARIS_
1490     /*
1491      * Let libsasl know that we are a "Sun" plugin so that privacy
1492      * and integrity will be allowed.
1493      */
1494     REG_PLUG("GSSAPI", gssapi_server_plugins);
1495 #endif /* _INTEGRATED_SOLARIS_ */
1496 
1497     *out_version = SASL_SERVER_PLUG_VERSION;
1498     *pluglist = gssapi_server_plugins;
1499     *plugcount = 1;  
1500     
1501     return SASL_OK;
1502 }
1503 
1504 /*****************************  Client Section  *****************************/
1505 
1506 static int gssapi_client_mech_new(void *glob_context __attribute__((unused)), 
1507                                   sasl_client_params_t *params,
1508                                   void **conn_context)
1509 {
1510     context_t *text;
1511 #ifdef _SUN_SDK_
1512     const char *use_authid = NULL;
1513 #endif /* _SUN_SDK_ */
1514     
1515     /* holds state are in */
1516 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
1517     if (LOCK_MUTEX(&global_mutex) < 0)
1518         return (SASL_FAIL);
1519 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
1520     text = gss_new_context(params->utils);
1521 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
1522     UNLOCK_MUTEX(&global_mutex);
1523 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
1524     if (text == NULL) {
1525 #ifndef _SUN_SDK_
1526         MEMERROR(params->utils);
1527 #endif /* !_SUN_SDK_ */
1528         return SASL_NOMEM;
1529     }
1530     
1531     text->state = SASL_GSSAPI_STATE_AUTHNEG;
1532     text->gss_ctx = GSS_C_NO_CONTEXT;
1533     text->client_name = GSS_C_NO_NAME;
1534     text->server_creds = GSS_C_NO_CREDENTIAL;
1535     
1536 #ifdef _SUN_SDK_
1537     params->utils->getopt(params->utils->getopt_context,
1538                           "GSSAPI", "use_authid", &use_authid, NULL);
1539     text->use_authid = (use_authid != NULL) &&
1540         (*use_authid == 'y' || *use_authid == 'Y' || *use_authid == '1');
1541 #endif /* _SUN_SDK_ */
1542     
1543     *conn_context = text;
1544     
1545     return SASL_OK;
1546 }
1547 
1548 static int gssapi_client_mech_step(void *conn_context,
1549                                    sasl_client_params_t *params,
1550                                    const char *serverin,
1551                                    unsigned serverinlen,
1552                                    sasl_interact_t **prompt_need,
1553                                    const char **clientout,
1554                                    unsigned *clientoutlen,
1555                                    sasl_out_params_t *oparams)
1556 {
1557     context_t *text = (context_t *)conn_context;
1558     gss_buffer_t input_token, output_token;
1559     gss_buffer_desc real_input_token, real_output_token;
1560     OM_uint32 maj_stat, min_stat;
1561 #ifdef _SUN_SDK_
1562     OM_uint32 max_input_size;
1563 #endif /* _SUN_SDK_ */
1564     gss_buffer_desc name_token;
1565     int ret;
1566     OM_uint32 req_flags, out_req_flags;
1567     input_token = &real_input_token;
1568     output_token = &real_output_token;
1569     output_token->value = NULL;
1570     input_token->value = NULL; 
1571     input_token->length = 0;
1572     
1573     *clientout = NULL;
1574     *clientoutlen = 0;
1575     
1576     switch (text->state) {
1577 
1578     case SASL_GSSAPI_STATE_AUTHNEG:
1579         /* try to get the userid */
1580 #ifdef _SUN_SDK_
1581         if (text->user == NULL ||
1582                 (text->use_authid && text->client_authid == NULL)) {
1583             int auth_result = SASL_OK;
1584             int user_result = SASL_OK;
1585 
1586             if (text->use_authid && text->client_authid == NULL) {
1587                 auth_result = _plug_get_authid(params->utils,
1588                                                &text->client_authid,
1589                                                prompt_need);
1590         
1591                 if ((auth_result != SASL_OK) &&
1592                         (auth_result != SASL_INTERACT)) {
1593                     sasl_gss_free_context_contents(text);
1594                     return auth_result;
1595                 }
1596             }
1597             if (text->user == NULL) {
1598                 user_result = _plug_get_userid(params->utils, &text->user,
1599                                                prompt_need);
1600             
1601                 if ((user_result != SASL_OK) &&
1602                         (user_result != SASL_INTERACT)) {
1603                     sasl_gss_free_context_contents(text);
1604                     return user_result;
1605                 }
1606             }
1607 #else
1608         if (text->user == NULL) {
1609             int user_result = SASL_OK;
1610             
1611             user_result = _plug_get_userid(params->utils, &text->user,
1612                                            prompt_need);
1613             
1614             if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) {
1615                 sasl_gss_free_context_contents(text);
1616                 return user_result;
1617             }
1618 #endif /* _SUN_SDK_ */
1619                     
1620             /* free prompts we got */
1621             if (prompt_need && *prompt_need) {
1622                 params->utils->free(*prompt_need);
1623                 *prompt_need = NULL;
1624             }
1625                     
1626             /* if there are prompts not filled in */
1627 #ifdef _SUN_SDK_
1628             if ((user_result == SASL_INTERACT) ||
1629                         (auth_result == SASL_INTERACT)) {
1630                 /* make the prompt list */
1631 #ifdef _INTEGRATED_SOLARIS_
1632                 int result = _plug_make_prompts(params->utils, &text->h,
1633                            prompt_need,
1634                            user_result == SASL_INTERACT ?
1635                            convert_prompt(params->utils, &text->h,
1636                             gettext("Please enter your authorization name"))
1637                                 : NULL, NULL,
1638                            auth_result == SASL_INTERACT ?
1639                            convert_prompt(params->utils, &text->h,
1640                             gettext("Please enter your authentication name"))
1641                                 : NULL, NULL,
1642                            NULL, NULL,
1643                            NULL, NULL, NULL,
1644                            NULL, NULL, NULL);
1645 #else
1646                 int result = _plug_make_prompts(params->utils, prompt_need,
1647                            user_result == SASL_INTERACT ?
1648                                 "Please enter your authorization name"
1649                                 : NULL, NULL,
1650                            auth_result == SASL_INTERACT ?
1651                                 "Please enter your authentication name"
1652                                 : NULL, NULL,
1653                            NULL, NULL,
1654                            NULL, NULL, NULL,
1655                            NULL, NULL, NULL);
1656 #endif /* _INTEGRATED_SOLARIS_ */
1657         
1658                 if (result != SASL_OK) return result;
1659 
1660                 return SASL_INTERACT;
1661             }
1662 #else
1663             if (user_result == SASL_INTERACT) {
1664                 /* make the prompt list */
1665                 int result =
1666                     _plug_make_prompts(params->utils, prompt_need,
1667                                        user_result == SASL_INTERACT ?
1668                                        "Please enter your authorization name" : NULL, NULL,
1669                                        NULL, NULL,
1670                                        NULL, NULL,
1671                                        NULL, NULL, NULL,
1672                                        NULL, NULL, NULL);
1673                 if (result != SASL_OK) return result;
1674                 
1675                 return SASL_INTERACT;
1676             }
1677 #endif /* _SUN_SDK_ */
1678         }
1679             
1680         if (text->server_name == GSS_C_NO_NAME) { /* only once */
1681             name_token.length = strlen(params->service) + 1 + strlen(params->serverFQDN);
1682             name_token.value = (char *)params->utils->malloc((name_token.length + 1) * sizeof(char));
1683             if (name_token.value == NULL) {
1684                 sasl_gss_free_context_contents(text);
1685                 return SASL_NOMEM;
1686             }
1687             if (params->serverFQDN == NULL
1688                 || strlen(params->serverFQDN) == 0) {
1689 #ifdef _SUN_SDK_
1690                 text->utils->log(text->utils->conn, SASL_LOG_ERR,
1691                                  "GSSAPI Failure: no serverFQDN");
1692 #else
1693                 SETERROR(text->utils, "GSSAPI Failure: no serverFQDN");
1694 #endif /* _SUN_SDK_ */
1695                 return SASL_FAIL;
1696             }
1697             
1698 #ifdef _SUN_SDK_
1699             snprintf(name_token.value, name_token.length + 1,
1700                 "%s@%s", params->service, params->serverFQDN);
1701 #else
1702             sprintf(name_token.value,"%s@%s", params->service, params->serverFQDN);
1703 #endif /* _SUN_SDK_ */
1704             
1705             maj_stat = gss_import_name (&min_stat,
1706                                         &name_token,
1707                                         GSS_C_NT_HOSTBASED_SERVICE,
1708                                         &text->server_name);
1709             
1710             params->utils->free(name_token.value);
1711             name_token.value = NULL;
1712             
1713             if (GSS_ERROR(maj_stat)) {
1714                 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1715                 sasl_gss_free_context_contents(text);
1716                 return SASL_FAIL;
1717             }
1718         }
1719             
1720         if (serverinlen == 0)
1721             input_token = GSS_C_NO_BUFFER;
1722 
1723         if (serverinlen) {
1724             real_input_token.value = (void *)serverin;
1725             real_input_token.length = serverinlen;
1726         }
1727         else if (text->gss_ctx != GSS_C_NO_CONTEXT ) {
1728             /* This can't happen under GSSAPI: we have a non-null context
1729              * and no input from the server.  However, thanks to Imap,
1730              * which discards our first output, this happens all the time.
1731              * Throw away the context and try again. */
1732             maj_stat = gss_delete_sec_context (&min_stat,&text->gss_ctx,GSS_C_NO_BUFFER);
1733             text->gss_ctx = GSS_C_NO_CONTEXT;
1734         }
1735             
1736         /* Setup req_flags properly */
1737         req_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
1738         if(params->props.max_ssf > params->external_ssf) {
1739             /* We are requesting a security layer */
1740             req_flags |= GSS_C_INTEG_FLAG;
1741             if(params->props.max_ssf - params->external_ssf > 56) {
1742                 /* We want to try for privacy */
1743                 req_flags |= GSS_C_CONF_FLAG;
1744             }
1745         }
1746         
1747 #ifdef _SUN_SDK_
1748         if (text->use_authid && text->client_creds == GSS_C_NO_CREDENTIAL) {
1749             gss_OID_set desired_mechs = GSS_C_NULL_OID_SET;
1750             gss_buffer_desc name_token;
1751 
1752             name_token.length = strlen(text->client_authid);
1753             name_token.value = (char *)text->client_authid;
1754 
1755             maj_stat = gss_import_name (&min_stat,
1756                                         &name_token,
1757 #ifdef HAVE_GSS_C_NT_USER_NAME
1758                                         GSS_C_NT_USER_NAME,
1759 #else
1760                                         GSS_C_NULL_OID,
1761 #endif
1762                                         &text->client_name);
1763             if (GSS_ERROR(maj_stat)) {
1764                 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1765                 sasl_gss_free_context_contents(text);
1766                 return SASL_FAIL;
1767             }
1768 
1769             if (text->mech_oid != GSS_C_NULL_OID) {
1770                 ret = add_mech_to_set(text, &desired_mechs);
1771                 if (ret != SASL_OK)
1772                     return (ret);
1773             }
1774 
1775             maj_stat = gss_acquire_cred(&min_stat, 
1776                                         text->client_name,
1777                                         GSS_C_INDEFINITE, 
1778                                         desired_mechs,
1779                                         GSS_C_INITIATE,
1780                                         &text->client_creds, 
1781                                         NULL, 
1782                                         NULL);
1783 
1784             if (desired_mechs != GSS_C_NULL_OID_SET) {
1785                 OM_uint32 min_stat2;
1786                 (void) gss_release_oid_set(&min_stat2, &desired_mechs);
1787             }
1788 
1789             if (GSS_ERROR(maj_stat)) {
1790                 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1791                 sasl_gss_free_context_contents(text);
1792                 return SASL_FAIL;
1793             }
1794         }
1795 #endif /* _SUN_SDK_ */
1796 
1797         maj_stat = gss_init_sec_context(&min_stat,
1798 #ifdef _SUN_SDK_
1799                                         text->client_creds,
1800 #else
1801                                         GSS_C_NO_CREDENTIAL,
1802 #endif /* _SUN_SDK_ */
1803                                         &text->gss_ctx,
1804                                         text->server_name,
1805 #ifdef _SUN_SDK_
1806                                         text->mech_oid,
1807 #else
1808                                         GSS_C_NO_OID,
1809 #endif /* _SUN_SDK_ */
1810                                         req_flags,
1811                                         0,
1812                                         GSS_C_NO_CHANNEL_BINDINGS,
1813                                         input_token,
1814                                         NULL,
1815                                         output_token,
1816                                         &out_req_flags,
1817                                         NULL);
1818         
1819         if (GSS_ERROR(maj_stat)) {
1820             sasl_gss_seterror(text->utils, maj_stat, min_stat);
1821             if (output_token->value)
1822                 gss_release_buffer(&min_stat, output_token);
1823             sasl_gss_free_context_contents(text);
1824             return SASL_FAIL;
1825         }
1826             
1827         *clientoutlen = output_token->length;
1828             
1829         if (output_token->value) {
1830             if (clientout) {
1831                 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
1832                                       &(text->out_buf_len), *clientoutlen);
1833                 if(ret != SASL_OK) {
1834                     gss_release_buffer(&min_stat, output_token);
1835                     return ret;
1836                 }
1837                 memcpy(text->out_buf, output_token->value, *clientoutlen);
1838                 *clientout = text->out_buf;
1839             }
1840             
1841             gss_release_buffer(&min_stat, output_token);
1842         }
1843         
1844         if (maj_stat == GSS_S_COMPLETE) {
1845             maj_stat = gss_inquire_context(&min_stat,
1846                                            text->gss_ctx,
1847                                            &text->client_name,
1848                                            NULL,       /* targ_name */
1849                                            NULL,       /* lifetime */
1850                                            NULL,       /* mech */
1851                                            NULL,       /* flags */
1852                                            NULL,       /* local init */
1853                                            NULL);      /* open */
1854             
1855             if (GSS_ERROR(maj_stat)) {
1856                 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1857                 sasl_gss_free_context_contents(text);
1858                 return SASL_FAIL;
1859             }
1860             
1861             name_token.length = 0;
1862             maj_stat = gss_display_name(&min_stat,
1863                                         text->client_name,
1864                                         &name_token,
1865                                         NULL);
1866             
1867             if (GSS_ERROR(maj_stat)) {
1868                 if (name_token.value)
1869                     gss_release_buffer(&min_stat, &name_token);
1870 #ifdef _INTEGRATED_SOLARIS_
1871                 SETERROR(text->utils, gettext("GSSAPI Failure"));
1872 #else
1873                 SETERROR(text->utils, "GSSAPI Failure");
1874 #endif /* _INTEGRATED_SOLARIS_ */
1875                 sasl_gss_free_context_contents(text);
1876                 return SASL_FAIL;
1877             }
1878             
1879             if (text->user && text->user[0]) {
1880                 ret = params->canon_user(params->utils->conn,
1881                                          text->user, 0,
1882                                          SASL_CU_AUTHZID, oparams);
1883                 if (ret == SASL_OK) 
1884                     ret = params->canon_user(params->utils->conn,
1885                                              name_token.value, 0,
1886                                              SASL_CU_AUTHID, oparams);
1887             } else {
1888                 ret = params->canon_user(params->utils->conn,
1889                                          name_token.value, 0,
1890                                          SASL_CU_AUTHID | SASL_CU_AUTHZID,
1891                                          oparams);
1892             }
1893             gss_release_buffer(&min_stat, &name_token);
1894             
1895             if (ret != SASL_OK) return ret;
1896             
1897             /* Switch to ssf negotiation */
1898             text->state = SASL_GSSAPI_STATE_SSFCAP;
1899         }
1900         
1901         return SASL_CONTINUE;
1902 
1903     case SASL_GSSAPI_STATE_SSFCAP: {
1904         sasl_security_properties_t *secprops = &(params->props);
1905         unsigned int alen, external = params->external_ssf;
1906         sasl_ssf_t need, allowed;
1907         char serverhas, mychoice;
1908         
1909         real_input_token.value = (void *) serverin;
1910         real_input_token.length = serverinlen;
1911         
1912         maj_stat = gss_unwrap(&min_stat,
1913                               text->gss_ctx,
1914                               input_token,
1915                               output_token,
1916                               NULL,
1917                               NULL);
1918         
1919         if (GSS_ERROR(maj_stat)) {
1920             sasl_gss_seterror(text->utils, maj_stat, min_stat);
1921             sasl_gss_free_context_contents(text);
1922             if (output_token->value)
1923                 gss_release_buffer(&min_stat, output_token);
1924             return SASL_FAIL;
1925         }
1926         
1927         /* taken from kerberos.c */
1928         if (secprops->min_ssf > (56 + external)) {
1929             return SASL_TOOWEAK;
1930         } else if (secprops->min_ssf > secprops->max_ssf) {
1931             return SASL_BADPARAM;
1932         }
1933         
1934         /* need bits of layer -- sasl_ssf_t is unsigned so be careful */
1935         if (secprops->max_ssf >= external) {
1936             allowed = secprops->max_ssf - external;
1937         } else {
1938             allowed = 0;
1939         }
1940         if (secprops->min_ssf >= external) {
1941             need = secprops->min_ssf - external;
1942         } else {
1943             /* good to go */
1944             need = 0;
1945         }
1946         
1947         /* bit mask of server support */
1948         serverhas = ((char *)output_token->value)[0];
1949         
1950         /* if client didn't set use strongest layer available */
1951         if (allowed >= 56 && need <= 56 && (serverhas & 4)) {
1952             /* encryption */
1953             oparams->encode = &gssapi_privacy_encode;
1954             oparams->decode = &gssapi_decode;
1955             oparams->mech_ssf = 56;
1956             mychoice = 4;
1957         } else if (allowed >= 1 && need <= 1 && (serverhas & 2)) {
1958             /* integrity */
1959             oparams->encode = &gssapi_integrity_encode;
1960             oparams->decode = &gssapi_decode;
1961             oparams->mech_ssf = 1;
1962             mychoice = 2;
1963 #ifdef _SUN_SDK_
1964         } else if (need == 0 && (serverhas & 1)) {
1965 #else
1966         } else if (need <= 0 && (serverhas & 1)) {
1967 #endif /* _SUN_SDK_ */
1968             /* no layer */
1969             oparams->encode = NULL;
1970             oparams->decode = NULL;
1971             oparams->mech_ssf = 0;
1972             mychoice = 1;
1973         } else {
1974             /* there's no appropriate layering for us! */
1975             sasl_gss_free_context_contents(text);
1976             return SASL_TOOWEAK;
1977         }
1978         
1979         oparams->maxoutbuf =
1980             (((unsigned char *) output_token->value)[1] << 16) |
1981             (((unsigned char *) output_token->value)[2] << 8) |
1982             (((unsigned char *) output_token->value)[3] << 0);
1983 
1984 #ifdef _SUN_SDK_
1985         if (oparams->mech_ssf > 0) {
1986             oparams->maxoutbuf -= 4; /* Space for 4 byte length header */
1987             maj_stat = gss_wrap_size_limit(&min_stat,
1988                                         text->gss_ctx,
1989                                         oparams->mech_ssf > 1,
1990                                         GSS_C_QOP_DEFAULT,
1991                                         oparams->maxoutbuf,
1992                                         &max_input_size);
1993             if (GSS_ERROR(maj_stat)) {
1994                 sasl_gss_seterror(text->utils, maj_stat, min_stat);
1995                 (void) gss_release_buffer(&min_stat, output_token);
1996                 sasl_gss_free_context_contents(text);
1997                 return (SASL_FAIL);
1998             }
1999 
2000         /*
2001          * This is a workaround for a Solaris bug where
2002          * gss_wrap_size_limit may return very big sizes for
2003          * small input values
2004          */
2005             if (max_input_size < oparams->maxoutbuf)
2006                 oparams->maxoutbuf = max_input_size;
2007             else {
2008                 oparams->maxoutbuf = 0;
2009             }
2010         }
2011 #else
2012         if(oparams->mech_ssf) {
2013             /* xxx probably too large */
2014             oparams->maxoutbuf -= 50;
2015         }
2016 #endif /* _SUN_SDK_ */
2017         
2018         gss_release_buffer(&min_stat, output_token);
2019         
2020         /* oparams->user is always set, due to canon_user requirements.
2021          * Make sure the client actually requested it though, by checking
2022          * if our context was set.
2023          */
2024         if (text->user && text->user[0])
2025             alen = strlen(oparams->user);
2026         else
2027             alen = 0;
2028         
2029         input_token->length = 4 + alen;
2030         input_token->value =
2031             (char *)params->utils->malloc((input_token->length + 1)*sizeof(char));
2032         if (input_token->value == NULL) {
2033             sasl_gss_free_context_contents(text);
2034             return SASL_NOMEM;
2035         }
2036         
2037         if (alen)
2038             memcpy((char *)input_token->value+4,oparams->user,alen);
2039 
2040         /* build up our security properties token */
2041         if (params->props.maxbufsize > 0xFFFFFF) {
2042             /* make sure maxbufsize isn't too large */
2043             /* maxbufsize = 0xFFFFFF */
2044             ((unsigned char *)input_token->value)[1] = 0xFF;
2045             ((unsigned char *)input_token->value)[2] = 0xFF;
2046             ((unsigned char *)input_token->value)[3] = 0xFF;
2047         } else {
2048             ((unsigned char *)input_token->value)[1] = 
2049                 (params->props.maxbufsize >> 16) & 0xFF;
2050             ((unsigned char *)input_token->value)[2] = 
2051                 (params->props.maxbufsize >> 8) & 0xFF;
2052             ((unsigned char *)input_token->value)[3] = 
2053                 (params->props.maxbufsize >> 0) & 0xFF;
2054         }
2055         ((unsigned char *)input_token->value)[0] = mychoice;
2056         
2057         maj_stat = gss_wrap (&min_stat,
2058                              text->gss_ctx,
2059                              0, /* Just integrity checking here */
2060                              GSS_C_QOP_DEFAULT,
2061                              input_token,
2062                              NULL,
2063                              output_token);
2064         
2065         params->utils->free(input_token->value);
2066         input_token->value = NULL;
2067         
2068         if (GSS_ERROR(maj_stat)) {
2069             sasl_gss_seterror(text->utils, maj_stat, min_stat);
2070             if (output_token->value)
2071                 gss_release_buffer(&min_stat, output_token);
2072             sasl_gss_free_context_contents(text);
2073             return SASL_FAIL;
2074         }
2075         
2076         if (clientoutlen)
2077             *clientoutlen = output_token->length;
2078         if (output_token->value) {
2079             if (clientout) {
2080                 ret = _plug_buf_alloc(text->utils, &(text->out_buf),
2081                                       &(text->out_buf_len), *clientoutlen);
2082                 if (ret != SASL_OK) {
2083                     gss_release_buffer(&min_stat, output_token);
2084                     return ret;
2085                 }
2086                 memcpy(text->out_buf, output_token->value, *clientoutlen);
2087                 *clientout = text->out_buf;
2088             }
2089             
2090             gss_release_buffer(&min_stat, output_token);
2091         }
2092         
2093         text->state = SASL_GSSAPI_STATE_AUTHENTICATED;
2094         
2095         oparams->doneflag = 1;
2096         
2097         return SASL_OK;
2098     }
2099         
2100     default:
2101 #ifdef _SUN_SDK_
2102         params->utils->log(params->utils->conn, SASL_LOG_ERR,
2103                            "Invalid GSSAPI client step %d", text->state);
2104 #else
2105         params->utils->log(NULL, SASL_LOG_ERR,
2106                            "Invalid GSSAPI client step %d\n", text->state);
2107 #endif /* _SUN_SDK_ */
2108         return SASL_FAIL;
2109     }
2110     
2111 #ifndef _SUN_SDK_
2112     return SASL_FAIL; /* should never get here */
2113 #endif /* !_SUN_SDK_ */
2114 }
2115 
2116 #ifdef _SUN_SDK_
2117 static const unsigned long gssapi_required_prompts[] = {
2118 #else
2119 static const long gssapi_required_prompts[] = {
2120 #endif /* _SUN_SDK_ */
2121     SASL_CB_LIST_END
2122 };  
2123 
2124 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
2125 static int _gssapi_client_mech_step(void *conn_context,
2126                                    sasl_client_params_t *params,
2127                                    const char *serverin,
2128                                    unsigned serverinlen,
2129                                    sasl_interact_t **prompt_need,
2130                                    const char **clientout,
2131                                    unsigned *clientoutlen,
2132                                    sasl_out_params_t *oparams)
2133 {
2134     int ret;
2135 
2136     if (LOCK_MUTEX(&global_mutex) < 0)
2137         return (SASL_FAIL);
2138 
2139     ret = gssapi_client_mech_step(conn_context, params, serverin, serverinlen,
2140         prompt_need, clientout, clientoutlen, oparams);
2141 
2142     UNLOCK_MUTEX(&global_mutex);
2143     return (ret);
2144 }
2145 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
2146 
2147 static sasl_client_plug_t gssapi_client_plugins[] = 
2148 {
2149     {
2150         "GSSAPI",                       /* mech_name */
2151         56,                             /* max_ssf */
2152         SASL_SEC_NOPLAINTEXT
2153         | SASL_SEC_NOACTIVE
2154         | SASL_SEC_NOANONYMOUS
2155         | SASL_SEC_MUTUAL_AUTH,         /* security_flags */
2156         SASL_FEAT_WANT_CLIENT_FIRST
2157         | SASL_FEAT_ALLOWS_PROXY,       /* features */
2158         gssapi_required_prompts,        /* required_prompts */
2159         NULL,                           /* glob_context */
2160         &gssapi_client_mech_new,    /* mech_new */
2161 #if defined _SUN_SDK_ && defined GSSAPI_PROTECT
2162         &_gssapi_client_mech_step,  /* mech_step */
2163 #else
2164         &gssapi_client_mech_step,   /* mech_step */
2165 #endif /* _SUN_SDK_ && GSSAPI_PROTECT */
2166         &gssapi_common_mech_dispose,        /* mech_dispose */
2167         NULL,                           /* mech_free */
2168         NULL,                           /* idle */
2169         NULL,                           /* spare */
2170         NULL                            /* spare */
2171     }
2172 };
2173 
2174 int gssapiv2_client_plug_init(const sasl_utils_t *utils __attribute__((unused)), 
2175                               int maxversion,
2176                               int *out_version, 
2177                               sasl_client_plug_t **pluglist,
2178                               int *plugcount)
2179 {
2180     if (maxversion < SASL_CLIENT_PLUG_VERSION) {
2181         SETERROR(utils, "Version mismatch in GSSAPI");
2182         return SASL_BADVERS;
2183     }
2184     
2185 #ifdef _INTEGRATED_SOLARIS_
2186     /*
2187      * Let libsasl know that we are a "Sun" plugin so that privacy
2188      * and integrity will be allowed.
2189      */
2190     REG_PLUG("GSSAPI", gssapi_client_plugins);
2191 #endif /* _INTEGRATED_SOLARIS_ */
2192 
2193     *out_version = SASL_CLIENT_PLUG_VERSION;
2194     *pluglist = gssapi_client_plugins;
2195     *plugcount = 1;
2196     
2197     return SASL_OK;
2198 }