1 /*
   2  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
   4  */
   5 /*
   6  * Copyright 2000, 2004  by the Massachusetts Institute of Technology.
   7  * All Rights Reserved.
   8  *
   9  * Export of this software from the United States of America may
  10  *   require a specific license from the United States Government.
  11  *   It is the responsibility of any person or organization contemplating
  12  *   export to obtain such a license before exporting.
  13  * 
  14  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
  15  * distribute this software and its documentation for any purpose and
  16  * without fee is hereby granted, provided that the above copyright
  17  * notice appear in all copies and that both that copyright notice and
  18  * this permission notice appear in supporting documentation, and that
  19  * the name of M.I.T. not be used in advertising or publicity pertaining
  20  * to distribution of the software without specific, written prior
  21  * permission.  Furthermore if you modify this software you must label
  22  * your software as modified software and not distribute it in such a
  23  * fashion that it might be confused with the original M.I.T. software.
  24  * M.I.T. makes no representations about the suitability of
  25  * this software for any purpose.  It is provided "as is" without express
  26  * or implied warranty.
  27  * 
  28  */
  29 /*
  30  * Copyright 1993 by OpenVision Technologies, Inc.
  31  * 
  32  * Permission to use, copy, modify, distribute, and sell this software
  33  * and its documentation for any purpose is hereby granted without fee,
  34  * provided that the above copyright notice appears in all copies and
  35  * that both that copyright notice and this permission notice appear in
  36  * supporting documentation, and that the name of OpenVision not be used
  37  * in advertising or publicity pertaining to distribution of the software
  38  * without specific, written prior permission. OpenVision makes no
  39  * representations about the suitability of this software for any
  40  * purpose.  It is provided "as is" without express or implied warranty.
  41  * 
  42  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  43  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
  44  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  45  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  46  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  47  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  48  * PERFORMANCE OF THIS SOFTWARE.
  49  */
  50 
  51 /*
  52  * Copyright (C) 1998 by the FundsXpress, INC.
  53  * 
  54  * All rights reserved.
  55  * 
  56  * Export of this software from the United States of America may require
  57  * a specific license from the United States Government.  It is the
  58  * responsibility of any person or organization contemplating export to
  59  * obtain such a license before exporting.
  60  * 
  61  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
  62  * distribute this software and its documentation for any purpose and
  63  * without fee is hereby granted, provided that the above copyright
  64  * notice appear in all copies and that both that copyright notice and
  65  * this permission notice appear in supporting documentation, and that
  66  * the name of FundsXpress. not be used in advertising or publicity pertaining
  67  * to distribution of the software without specific, written prior
  68  * permission.  FundsXpress makes no representations about the suitability of
  69  * this software for any purpose.  It is provided "as is" without express
  70  * or implied warranty.
  71  * 
  72  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  73  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  74  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  75  */
  76 
  77 #include "k5-int.h"
  78 #include "gssapiP_krb5.h"
  79 #ifdef HAVE_MEMORY_H
  80 #include <memory.h>
  81 #endif
  82 #include <assert.h>
  83 #include "auth_con.h"
  84 #ifdef CFX_EXERCISE
  85 #define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
  86 #else
  87 #define CFX_ACCEPTOR_SUBKEY 1
  88 #endif
  89 #include <syslog.h>
  90 #include <locale.h> /* Solaris Kerberos */
  91 
  92 /*
  93  * Decode, decrypt and store the forwarded creds in the local ccache.
  94  * and populate the callers delegated credential handle if it
  95  * was provided.
  96  */
  97 static krb5_error_code
  98 rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
  99     krb5_context context;
 100     krb5_auth_context auth_context;
 101     krb5_data *inbuf;
 102     krb5_gss_cred_id_t *out_cred;
 103 {
 104     krb5_creds ** creds = NULL;
 105     krb5_error_code retval;
 106     krb5_ccache ccache = NULL;
 107     krb5_gss_cred_id_t cred = NULL;
 108     krb5_auth_context new_auth_ctx = NULL;
 109         krb5_int32 flags_org;
 110 
 111     /* Solaris Kerberos */
 112     KRB5_LOG0(KRB5_INFO, "rd_and_store_for_creds() start");
 113 
 114         if ((retval = krb5_auth_con_getflags(context, auth_context, &flags_org)))
 115                 return retval;
 116         krb5_auth_con_setflags(context, auth_context,
 117                                0);
 118 
 119         /*
 120          * By the time krb5_rd_cred is called here (after krb5_rd_req has been
 121          * called in krb5_gss_accept_sec_context), the "keyblock" field of
 122          * auth_context contains a pointer to the session key, and the
 123          * "recv_subkey" field might contain a session subkey.  Either of
 124          * these (the "recv_subkey" if it isn't NULL, otherwise the
 125          * "keyblock") might have been used to encrypt the encrypted part of
 126          * the KRB_CRED message that contains the forwarded credentials.  (The
 127          * Java Crypto and Security Implementation from the DSTC in Australia
 128          * always uses the session key.  But apparently it never negotiates a
 129          * subkey, so this code works fine against a JCSI client.)  Up to the
 130          * present, though, GSSAPI clients linked against the MIT code (which
 131          * is almost all GSSAPI clients) don't encrypt the KRB_CRED message at
 132          * all -- at this level.  So if the first call to krb5_rd_cred fails,
 133          * we should call it a second time with another auth context freshly
 134          * created by krb5_auth_con_init.  All of its keyblock fields will be
 135          * NULL, so krb5_rd_cred will assume that the KRB_CRED message is
 136          * unencrypted.  (The MIT code doesn't actually send the KRB_CRED
 137          * message in the clear -- the "authenticator" whose "checksum" ends up
 138          * containing the KRB_CRED message does get encrypted.)
 139          */
 140     /* Solaris Kerberos */
 141     if ((retval = krb5_rd_cred(context, auth_context, inbuf, &creds, NULL))) {
 142         krb5_error_code retval2 = retval;
 143 
 144         /* Try to krb5_rd_cred() likely unencrypted KRB-CRED */
 145                 if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
 146                         goto cleanup;
 147                 krb5_auth_con_setflags(context, new_auth_ctx, 0);
 148                 if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
 149                                            &creds, NULL))) {
 150                         /* Solaris Kerberos */
 151                         KRB5_LOG1(KRB5_ERR, "rd_and_store_for_creds() error "
 152                             "krb5_rd_cred() retval = %d original = %d\n",
 153                             retval, retval2);
 154                         goto cleanup;
 155                 }
 156     }
 157 
 158     if ((retval = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) {
 159         ccache = NULL;
 160         goto cleanup;
 161     }
 162 
 163     if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client))) {
 164         /* Solaris Kerberos */
 165         KRB5_LOG(KRB5_ERR, "rd_and_store_for_creds() error "
 166                 "krb5_cc_initialize() retval = %d\n", retval);
 167         goto cleanup;
 168     }
 169 
 170     if ((retval = krb5_cc_store_cred(context, ccache, creds[0]))) {
 171         /* Solaris Kerberos */
 172         KRB5_LOG(KRB5_ERR, "rd_and_store_for_creds() error "
 173                 "krb5_cc_store_cred() retval = %d\n", retval);
 174         goto cleanup;
 175     }
 176 
 177     /* generate a delegated credential handle */
 178     if (out_cred) {
 179         /* allocate memory for a cred_t... */
 180         if (!(cred =
 181               (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec)))) {
 182             retval = ENOMEM; /* out of memory? */
 183             *out_cred = NULL;
 184             goto cleanup;
 185         }
 186 
 187         /* zero it out... */
 188         /* Solaris Kerberos */
 189         (void) memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
 190 
 191         retval = k5_mutex_init(&cred->lock);
 192         if (retval) {
 193             xfree(cred);
 194             cred = NULL;
 195             goto cleanup;
 196         }
 197 
 198         /* copy the client principle into it... */
 199         if ((retval =
 200              krb5_copy_principal(context, creds[0]->client, &(cred->princ)))) {
 201              /* Solaris Kerberos */
 202             KRB5_LOG(KRB5_ERR, "rd_and_store_for_creds() error "
 203                     "krb5_copy_principal() retval = %d\n", retval);
 204             k5_mutex_destroy(&cred->lock);
 205             retval = ENOMEM; /* out of memory? */
 206             xfree(cred); /* clean up memory on failure */
 207             cred = NULL;
 208             goto cleanup;
 209         }
 210 
 211         cred->usage = GSS_C_INITIATE; /* we can't accept with this */
 212         /* cred->princ already set */
 213         cred->prerfc_mech = 1; /* this cred will work with all three mechs */
 214         cred->rfc_mech = 1;
 215         cred->keytab = NULL; /* no keytab associated with this... */
 216         cred->tgt_expire = creds[0]->times.endtime; /* store the end time */
 217         cred->ccache = ccache; /* the ccache containing the credential */
 218         ccache = NULL; /* cred takes ownership so don't destroy */
 219     }
 220 
 221     /* If there were errors, there might have been a memory leak
 222        if (!cred)
 223        if ((retval = krb5_cc_close(context, ccache)))
 224        goto cleanup;
 225     */
 226 cleanup:
 227     if (creds)
 228         krb5_free_tgt_creds(context, creds);
 229 
 230     if (ccache)
 231         (void)krb5_cc_destroy(context, ccache);
 232 
 233     if (out_cred)
 234         *out_cred = cred; /* return credential */
 235 
 236     if (new_auth_ctx)
 237         krb5_auth_con_free(context, new_auth_ctx);
 238 
 239     krb5_auth_con_setflags(context, auth_context, flags_org);
 240 
 241     /* Solaris Kerberos */
 242     KRB5_LOG(KRB5_INFO, "rd_and_store_for_creds() end retval %d", retval);
 243     return retval;
 244 }
 245 
 246 /*
 247  * SUNW15resync
 248  * Most of the logic here left "as is" because of lots of fixes MIT
 249  * does not have yet
 250  */
 251 OM_uint32
 252 krb5_gss_accept_sec_context(minor_status, context_handle, 
 253                             verifier_cred_handle, input_token,
 254                             input_chan_bindings, src_name, mech_type,
 255                             output_token, ret_flags, time_rec,
 256                             delegated_cred_handle)
 257      OM_uint32 *minor_status;
 258      gss_ctx_id_t *context_handle;
 259      gss_cred_id_t verifier_cred_handle;
 260      gss_buffer_t input_token;
 261      gss_channel_bindings_t input_chan_bindings;
 262      gss_name_t *src_name;
 263      gss_OID *mech_type;
 264      gss_buffer_t output_token;
 265      OM_uint32 *ret_flags;
 266      OM_uint32 *time_rec;
 267      gss_cred_id_t *delegated_cred_handle;
 268 {
 269    krb5_context context;
 270    unsigned char *ptr, *ptr2;
 271    krb5_gss_ctx_id_rec *ctx = 0;
 272    char *sptr;
 273    long tmp;
 274    size_t md5len;
 275    int bigend;
 276    krb5_gss_cred_id_t cred = 0;
 277    krb5_data ap_rep, ap_req;
 278    krb5_ap_req *request = NULL;
 279    int i;
 280    krb5_error_code code;
 281    krb5_address addr, *paddr;
 282    krb5_authenticator *authdat = 0;
 283    krb5_checksum reqcksum;
 284    krb5_principal client_name = NULL;
 285    krb5_principal server_name = NULL;
 286    krb5_ui_4 gss_flags = 0;
 287    krb5_timestamp now;
 288    gss_buffer_desc token;
 289    krb5_auth_context auth_context = NULL;
 290    krb5_ticket * ticket = NULL;
 291    int option_id;
 292    krb5_data option;
 293    const gss_OID_desc *mech_used = NULL;
 294    OM_uint32 major_status = GSS_S_FAILURE;
 295    krb5_error krb_error_data;
 296    krb5_data scratch;
 297    gss_cred_id_t cred_handle = NULL;
 298    krb5_gss_cred_id_t deleg_cred = NULL;
 299    OM_uint32 saved_ap_options = 0;
 300    krb5int_access kaccess;
 301    int cred_rcache = 0;
 302    int no_encap;
 303    OM_uint32 t_minor_status = 0;
 304    int acquire_fail = 0;
 305 
 306    KRB5_LOG0(KRB5_INFO,"krb5_gss_accept_sec_context() start");
 307 
 308    /* Solaris Kerberos */
 309    memset(&krb_error_data, 0, sizeof(krb_error_data));
 310 
 311    code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
 312    if (code) {
 313        *minor_status = code;
 314        return(GSS_S_FAILURE);
 315    }
 316 
 317    code = krb5_gss_init_context(&context);
 318    if (code) {
 319        *minor_status = code;
 320        return GSS_S_FAILURE;
 321    }
 322 
 323    /* set up returns to be freeable */
 324 
 325    if (src_name)
 326       *src_name = (gss_name_t) NULL;
 327    output_token->length = 0;
 328    output_token->value = NULL;
 329    token.value = 0;
 330    reqcksum.contents = 0;
 331    ap_req.data = 0;
 332    ap_rep.data = 0;
 333    
 334    if (mech_type)
 335       *mech_type = GSS_C_NULL_OID;
 336    /* initialize the delegated cred handle to NO_CREDENTIAL for now */
 337    if (delegated_cred_handle)
 338       *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
 339 
 340    /*
 341     * Context handle must be unspecified.  Actually, it must be
 342     * non-established, but currently, accept_sec_context never returns
 343     * a non-established context handle.
 344     */
 345    /*SUPPRESS 29*/
 346    if (*context_handle != GSS_C_NO_CONTEXT) {
 347       *minor_status = 0;
 348 
 349        /* Solaris kerberos: the original Solaris code returned GSS_S_NO_CONTEXT
 350         * for this error.  This conflicts somewhat with RFC2743 which states
 351         * GSS_S_NO_CONTEXT should be returned only for sucessor calls following
 352         * GSS_S_CONTINUE_NEEDED status returns.  Note the MIT code doesn't
 353         * return GSS_S_NO_CONTEXT at all.
 354         */
 355 
 356       major_status = GSS_S_NO_CONTEXT;
 357       KRB5_LOG0(KRB5_ERR,"krb5_gss_accept_sec_context() "
 358               "error GSS_S_NO_CONTEXT");
 359       goto cleanup;
 360    }
 361 
 362    /* verify the token's integrity, and leave the token in ap_req.
 363       figure out which mech oid was used, and save it */
 364 
 365    ptr = (unsigned char *) input_token->value;
 366 
 367    if (!(code = g_verify_token_header(gss_mech_krb5,
 368                                       (uint32_t *)&(ap_req.length),
 369                                       &ptr, KG_TOK_CTX_AP_REQ,
 370                                       input_token->length, 1))) {
 371        mech_used = gss_mech_krb5;
 372    } else if ((code == G_WRONG_MECH) &&
 373                 !(code = g_verify_token_header(gss_mech_krb5_wrong,
 374                                                 (uint32_t *)&(ap_req.length),
 375                                                 &ptr, KG_TOK_CTX_AP_REQ,
 376                                                 input_token->length, 1))) {
 377         mech_used = gss_mech_krb5_wrong;
 378    } else if ((code == G_WRONG_MECH) &&
 379               !(code = g_verify_token_header(gss_mech_krb5_old,
 380                                              (uint32_t *)&(ap_req.length),
 381                                              &ptr, KG_TOK_CTX_AP_REQ,
 382                                              input_token->length, 1))) {
 383        /*
 384         * Previous versions of this library used the old mech_id
 385         * and some broken behavior (wrong IV on checksum
 386         * encryption).  We support the old mech_id for
 387         * compatibility, and use it to decide when to use the
 388         * old behavior.
 389         */
 390        mech_used = gss_mech_krb5_old;
 391   } else if (code == G_WRONG_TOKID) {
 392         major_status = GSS_S_CONTINUE_NEEDED;
 393         code = KRB5KRB_AP_ERR_MSG_TYPE;
 394         mech_used = gss_mech_krb5;
 395         goto fail;
 396    } else if (code == G_BAD_TOK_HEADER) {
 397         /* DCE style not encapsulated */
 398         ap_req.length = input_token->length;
 399         ap_req.data = input_token->value;
 400         mech_used = gss_mech_krb5;
 401         no_encap = 1;
 402    } else {
 403         major_status = GSS_S_DEFECTIVE_TOKEN;
 404         goto fail;
 405    }
 406 
 407    sptr = (char *) ptr;
 408    TREAD_STR(sptr, ap_req.data, ap_req.length);
 409 
 410    /*
 411     * Solaris Kerberos: 
 412     *  We need to decode the request now so that we can get the
 413     *  service principal in order to try and acquire a cred for it.
 414     *  below in the "handle default cred handle" code block.
 415     */
 416    if (!krb5_is_ap_req(&ap_req)) {
 417        code = KRB5KRB_AP_ERR_MSG_TYPE;
 418        goto fail;
 419    }
 420    /* decode the AP-REQ into request */
 421    if ((code = decode_krb5_ap_req(&ap_req, &request))) {
 422        if (code == KRB5_BADMSGTYPE)
 423            code = KRB5KRB_AP_ERR_BADVERSION;
 424        goto fail;
 425    }
 426 
 427    /* handle default cred handle */
 428    /* 
 429     * Solaris Kerberos:
 430     * If there is no princ associated with the cred then treat it the
 431     * the same as GSS_C_NO_CREDENTIAL. 
 432     */
 433    if (verifier_cred_handle == GSS_C_NO_CREDENTIAL ||
 434     ((krb5_gss_cred_id_t)verifier_cred_handle)->princ == NULL) {
 435        /* Note that we try to acquire a cred for the service principal
 436         * named in the AP-REQ. This allows us to implement option (ii)
 437         * of the recommended behaviour for GSS_Accept_sec_context() as
 438         * described in section 1.1.1.3 of RFC2743.
 439 
 440         * This is far more useful that option (i), for which we would
 441         * acquire a cred for GSS_C_NO_NAME.
 442         */
 443        /* copy the princ from the ap-req or we'll lose it when we free
 444           the ap-req */
 445        krb5_principal princ;
 446        if ((code = krb5_copy_principal(context, request->ticket->server,
 447                                        &princ))) {
 448            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 449                     "krb5_copy_principal() error code %d", code);
 450            major_status = GSS_S_FAILURE;
 451            goto fail;
 452        }
 453        /* intern the acceptor name */
 454        if (! kg_save_name((gss_name_t) princ)) {
 455            code = G_VALIDATE_FAILED;
 456            major_status = GSS_S_FAILURE;
 457            goto fail;
 458        }
 459        major_status = krb5_gss_acquire_cred((OM_uint32*) &code,
 460                                             (gss_name_t) princ,
 461                                             GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
 462                                             GSS_C_ACCEPT, &cred_handle,
 463                                             NULL, NULL);
 464 
 465        if (major_status != GSS_S_COMPLETE){
 466            /* Solaris kerberos: RFC2743 indicate this should be returned if we
 467             * can't aquire a default cred.
 468             */
 469            KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
 470                   "krb5_gss_acquire_cred() error"
 471                    "orig major_status = %d, now = GSS_S_NO_CRED\n",
 472                    major_status);
 473            acquire_fail = 1;
 474            major_status = GSS_S_NO_CRED;
 475            goto fail;
 476        }
 477 
 478    } else {
 479        cred_handle = verifier_cred_handle;
 480    }
 481 
 482    major_status = krb5_gss_validate_cred((OM_uint32*) &code,
 483                                                  cred_handle);
 484 
 485    if (GSS_ERROR(major_status)){
 486 
 487        /* Solaris kerberos: RFC2743 indicate GSS_S_NO_CRED should be returned if
 488         * the supplied cred isn't valid.
 489         */
 490 
 491        KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
 492               "krb5_gss_validate_cred() error"
 493                "orig major_status = %d, now = GSS_S_NO_CRED\n",
 494                major_status);
 495 
 496        major_status = GSS_S_NO_CRED;
 497        goto fail;
 498    }
 499 
 500    cred = (krb5_gss_cred_id_t) cred_handle;
 501 
 502    /* make sure the supplied credentials are valid for accept */
 503 
 504    if ((cred->usage != GSS_C_ACCEPT) &&
 505        (cred->usage != GSS_C_BOTH)) {
 506        code = 0;
 507       KRB5_LOG0(KRB5_ERR,"krb5_gss_accept_sec_context() "
 508               "error GSS_S_NO_CONTEXT");
 509        major_status = GSS_S_NO_CRED;
 510        goto fail;
 511    }
 512 
 513    /* construct the sender_addr */
 514 
 515    if ((input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS) &&
 516        (input_chan_bindings->initiator_addrtype == GSS_C_AF_INET)) {
 517        /* XXX is this right? */
 518        addr.addrtype = ADDRTYPE_INET;
 519        addr.length = input_chan_bindings->initiator_address.length;
 520        addr.contents = input_chan_bindings->initiator_address.value;
 521 
 522        paddr = &addr;
 523    } else {
 524        paddr = NULL;
 525    }
 526 
 527    /* verify the AP_REQ message - setup the auth_context and rcache */
 528 
 529    if ((code = krb5_auth_con_init(context, &auth_context))) {
 530        major_status = GSS_S_FAILURE;
 531        save_error_info((OM_uint32)code, context);
 532        /* Solaris Kerberos */
 533        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 534               "krb5_auth_con_init() error code %d", code);
 535        goto fail;
 536    }
 537 
 538    (void) krb5_auth_con_setflags(context, auth_context,
 539                           KRB5_AUTH_CONTEXT_DO_SEQUENCE);
 540 
 541    if (cred->rcache) {
 542        cred_rcache = 1;
 543        if ((code = krb5_auth_con_setrcache(context, auth_context, cred->rcache))) {
 544            major_status = GSS_S_FAILURE;
 545            /* Solaris Kerberos */
 546            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 547                     "krb5_auth_con_setrcache() error code %d", code);
 548            goto fail;
 549        }
 550    }
 551    if ((code = krb5_auth_con_setaddrs(context, auth_context, NULL, paddr))) {
 552        major_status = GSS_S_FAILURE;
 553        /* Solaris Kerberos */
 554        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 555               "krb5_auth_con_setaddrs() error code %d", code);
 556        goto fail;
 557    }
 558 
 559    if ((code = krb5_rd_req_decoded(context, &auth_context, request,
 560                            cred->princ, cred->keytab, NULL, &ticket))) {
 561        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 562               "krb5_rd_req() error code %d", code);
 563        if (code == KRB5_KT_KVNONOTFOUND) {
 564            char *s_name;
 565            if (krb5_unparse_name(context, cred->princ, &s_name) == 0) {  
 566                krb5_set_error_message(context, KRB5KRB_AP_ERR_BADKEYVER,
 567                                     dgettext(TEXT_DOMAIN,
 568                                             "Key version %d is not available for principal %s"),
 569                                     request->ticket->enc_part.kvno,
 570                                     s_name);
 571                krb5_free_unparsed_name(context, s_name);
 572            }
 573            major_status = GSS_S_DEFECTIVE_CREDENTIAL;
 574            code = KRB5KRB_AP_ERR_BADKEYVER;
 575        } else if (code == KRB5_KT_NOTFOUND) {
 576            char *s_name;
 577            if (krb5_unparse_name(context, cred->princ, &s_name) == 0) {  
 578                krb5_set_error_message(context, KRB5KRB_AP_ERR_NOKEY,
 579                                     dgettext(TEXT_DOMAIN,
 580                                             "Service key %s not available"),
 581                                     s_name);
 582                krb5_free_unparsed_name(context, s_name);
 583            }
 584            major_status = GSS_S_DEFECTIVE_CREDENTIAL;
 585            code = KRB5KRB_AP_ERR_NOKEY;
 586        }
 587        else if (code == KRB5KRB_AP_WRONG_PRINC) {
 588            major_status = GSS_S_NO_CRED;
 589            code = KRB5KRB_AP_ERR_NOT_US;
 590        }
 591        else if (code == KRB5KRB_AP_ERR_REPEAT)
 592            major_status = GSS_S_DUPLICATE_TOKEN;
 593        else
 594            major_status = GSS_S_FAILURE;
 595        goto fail;
 596    }
 597    krb5_auth_con_setflags(context, auth_context,
 598                           KRB5_AUTH_CONTEXT_DO_SEQUENCE);
 599 
 600    /* Solaris Kerberos */
 601    code = krb5_auth_con_getauthenticator(context, auth_context, &authdat);
 602    if (code) {
 603            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 604                   "krb5_auth_con_getauthenticator() error code %d", code);
 605            major_status = GSS_S_FAILURE;
 606            goto fail;
 607    }
 608 
 609 #if 0
 610    /* make sure the necessary parts of the authdat are present */
 611 
 612    if ((authdat->authenticator->subkey == NULL) ||
 613        (authdat->ticket->enc_part2 == NULL)) {
 614            code = KG_NO_SUBKEY;
 615            major_status = GSS_S_FAILURE;
 616            goto fail;
 617    }
 618 #endif
 619 
 620    {
 621        /* gss krb5 v1 */
 622 
 623        /* stash this now, for later. */
 624        code = krb5_c_checksum_length(context, CKSUMTYPE_RSA_MD5, &md5len);
 625        if (code) {
 626            /* Solaris Kerberos */
 627            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 628                   "krb5_c_checksum_length() error code %d", code);
 629            major_status = GSS_S_FAILURE;
 630            goto fail;
 631        }
 632 
 633        /* verify that the checksum is correct */
 634        if (authdat->checksum == NULL) {
 635           /* missing checksum counts as "inappropriate type" */
 636           code = KRB5KRB_AP_ERR_INAPP_CKSUM;
 637           major_status = GSS_S_FAILURE;
 638           goto fail;
 639        }
 640 
 641        /*
 642          The checksum may be either exactly 24 bytes, in which case
 643          no options are specified, or greater than 24 bytes, in which case
 644          one or more options are specified. Currently, the only valid
 645          option is KRB5_GSS_FOR_CREDS_OPTION ( = 1 ).
 646        */
 647 
 648        if ((authdat->checksum->checksum_type != CKSUMTYPE_KG_CB) ||
 649            (authdat->checksum->length < 24)) {
 650            code = 0;
 651            major_status = GSS_S_BAD_BINDINGS;
 652            goto fail;
 653        }
 654 
 655        /*
 656          "Be liberal in what you accept, and
 657          conservative in what you send"
 658          -- rfc1123
 659 
 660          This code will let this acceptor interoperate with an initiator
 661          using little-endian or big-endian integer encoding.
 662        */
 663 
 664        ptr = (unsigned char *) authdat->checksum->contents;
 665        bigend = 0;
 666 
 667        TREAD_INT(ptr, tmp, bigend);
 668 
 669        if (tmp != md5len) {
 670            ptr = (unsigned char *) authdat->checksum->contents;
 671            bigend = 1;
 672 
 673            TREAD_INT(ptr, tmp, bigend);
 674 
 675            if (tmp != md5len) {
 676                code = KG_BAD_LENGTH;
 677                major_status = GSS_S_FAILURE;
 678                goto fail;
 679            }
 680        }
 681 
 682        /* at this point, bigend is set according to the initiator's
 683           byte order */
 684 
 685 
 686        /* 
 687           The following section of code attempts to implement the
 688           optional channel binding facility as described in RFC2743.
 689 
 690           Since this facility is optional channel binding may or may
 691           not have been provided by either the client or the server.
 692 
 693           If the server has specified input_chan_bindings equal to
 694           GSS_C_NO_CHANNEL_BINDINGS then we skip the check.  If
 695           the server does provide channel bindings then we compute
 696           a checksum and compare against those provided by the
 697           client.  If the check fails we test the clients checksum
 698           to see whether the client specified GSS_C_NO_CHANNEL_BINDINGS.
 699           If either test succeeds we continue without error.
 700         */
 701        if ((code = kg_checksum_channel_bindings(context, 
 702                                                 input_chan_bindings,
 703                                                 &reqcksum, bigend))) {
 704            /* Solaris Kerberos */
 705            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 706                   "kg_checksum_channel_bindings() error code %d", code);
 707          major_status = GSS_S_BAD_BINDINGS;
 708          goto fail;
 709        }
 710 
 711        TREAD_STR(ptr, ptr2, reqcksum.length);
 712 
 713        if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS ) {
 714            if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
 715                xfree(reqcksum.contents);
 716                reqcksum.contents = 0;
 717                 if ((code = kg_checksum_channel_bindings(context,
 718                                                   GSS_C_NO_CHANNEL_BINDINGS,
 719                                                   &reqcksum, bigend))) {
 720                    major_status = GSS_S_BAD_BINDINGS;
 721                    goto fail;
 722                 }
 723                if (memcmp(ptr2, reqcksum.contents, reqcksum.length) != 0) {
 724                    code = 0;
 725                    major_status = GSS_S_BAD_BINDINGS;
 726                    goto fail;
 727                 }
 728            }
 729            
 730        }
 731 
 732        TREAD_INT(ptr, gss_flags, bigend);
 733 
 734        /* if the checksum length > 24, there are options to process */
 735 
 736        if(authdat->checksum->length > 24 && (gss_flags & GSS_C_DELEG_FLAG)) {
 737 
 738            i = authdat->checksum->length - 24;
 739 
 740            if (i >= 4) {
 741 
 742                TREAD_INT16(ptr, option_id, bigend);
 743 
 744                TREAD_INT16(ptr, option.length, bigend);
 745 
 746                i -= 4;
 747 
 748                if (i < option.length || option.length < 0) {
 749                    code = KG_BAD_LENGTH;
 750                    major_status = GSS_S_FAILURE;
 751                    goto fail;
 752                }
 753 
 754                /* have to use ptr2, since option.data is wrong type and
 755                   macro uses ptr as both lvalue and rvalue */
 756 
 757                TREAD_STR(ptr, ptr2, option.length);
 758                option.data = (char *) ptr2;
 759 
 760                i -= option.length;
 761 
 762                if (option_id != KRB5_GSS_FOR_CREDS_OPTION) {
 763                    major_status = GSS_S_FAILURE;
 764                    goto fail;
 765                }
 766 
 767                    /* store the delegated credential */
 768 
 769                    code = rd_and_store_for_creds(context, auth_context, &option,
 770                                                  (delegated_cred_handle) ?
 771                                                  &deleg_cred : NULL);
 772                    if (code) {
 773                        major_status = GSS_S_FAILURE;
 774                        goto fail;
 775                    }
 776 
 777            } /* if i >= 4 */
 778            /* ignore any additional trailing data, for now */
 779 #ifdef CFX_EXERCISE
 780            {
 781                FILE *f = fopen("/tmp/gsslog", "a");
 782                if (f) {
 783                    fprintf(f,
 784                            "initial context token with delegation, %d extra bytes\n",
 785                            i);
 786                    fclose(f);
 787                }
 788            }
 789 #endif
 790        } else {
 791 #ifdef CFX_EXERCISE
 792            {
 793                FILE *f = fopen("/tmp/gsslog", "a");
 794                if (f) {
 795                    if (gss_flags & GSS_C_DELEG_FLAG)
 796                        fprintf(f,
 797                                "initial context token, delegation flag but too small\n");
 798                    else
 799                        /* no deleg flag, length might still be too big */
 800                        fprintf(f,
 801                                "initial context token, %d extra bytes\n",
 802                                authdat->checksum->length - 24);
 803                    fclose(f);
 804                }
 805            }
 806 #endif
 807        }
 808    }
 809 
 810    /* create the ctx struct and start filling it in */
 811 
 812    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
 813        == NULL) {
 814        code = ENOMEM;
 815        major_status = GSS_S_FAILURE;
 816        goto fail;
 817    }
 818 
 819    memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
 820    ctx->mech_used = (gss_OID) mech_used;
 821    ctx->auth_context = auth_context;
 822    ctx->initiate = 0;
 823    ctx->gss_flags = (GSS_C_TRANS_FLAG |
 824                     ((gss_flags) & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG |
 825                             GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
 826                             GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG)));
 827    ctx->seed_init = 0;
 828    ctx->big_endian = bigend;
 829    ctx->cred_rcache = cred_rcache;
 830 
 831    /* Intern the ctx pointer so that delete_sec_context works */
 832    if (! kg_save_ctx_id((gss_ctx_id_t) ctx)) {
 833        xfree(ctx);
 834        ctx = 0;
 835 
 836         /* Solaris Kerberos */
 837        KRB5_LOG0(KRB5_ERR, "krb5_gss_accept_sec_context() "
 838               "kg_save_ctx_id() error");
 839        code = G_VALIDATE_FAILED;
 840        major_status = GSS_S_FAILURE;
 841        goto fail;
 842    }
 843 
 844    /* XXX move this into gss_name_t */
 845    if ((code = krb5_merge_authdata(context,
 846                                 ticket->enc_part2->authorization_data,
 847                                 authdat->authorization_data,
 848                                 &ctx->authdata))) {
 849         major_status = GSS_S_FAILURE;
 850         goto fail;
 851    }
 852 
 853    if ((code = krb5_copy_principal(context, cred->princ, &ctx->here))) {
 854         /* Solaris Kerberos */
 855        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 856               "krb5_copy_principal() error code %d", code);
 857        major_status = GSS_S_FAILURE;
 858        goto fail;
 859    }
 860 
 861    if ((code = krb5_copy_principal(context, authdat->client, &ctx->there))) {
 862         /* Solaris Kerberos */
 863        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 864               "krb5_copy_principal() 2 error code %d", code);
 865        major_status = GSS_S_FAILURE;
 866        goto fail;
 867    }
 868 
 869    if ((code = krb5_auth_con_getrecvsubkey(context, auth_context,
 870                                            &ctx->subkey))) { 
 871         /* Solaris Kerberos */
 872        KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 873               "krb5_auth_con_getremotesubkey() error code %d", code);
 874        major_status = GSS_S_FAILURE;      
 875        goto fail;
 876    }
 877 
 878    /* use the session key if the subkey isn't present */
 879 
 880    if (ctx->subkey == NULL) {
 881        if ((code = krb5_auth_con_getkey(context, auth_context,
 882                                         &ctx->subkey))) {
 883         /* Solaris Kerberos */
 884            KRB5_LOG(KRB5_ERR, "krb5_gss_accept_sec_context() "
 885                       "krb5_auth_con_getkey() error code %d", code);
 886            *minor_status = (OM_uint32) KRB5KDC_ERR_NULL_KEY;
 887            major_status = GSS_S_FAILURE;
 888            goto fail;
 889        }
 890    }
 891 
 892    if (ctx->subkey == NULL) {
 893        /* this isn't a very good error, but it's not clear to me this
 894           can actually happen */
 895        major_status = GSS_S_FAILURE;
 896        code = KRB5KDC_ERR_NULL_KEY;
 897        goto fail;
 898    }
 899 
 900     /* Solaris Kerberos */
 901    KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() "
 902            "ctx->subkey->enctype=%d", ctx->subkey->enctype);
 903 
 904    ctx->proto = 0;
 905    switch(ctx->subkey->enctype) {
 906    case ENCTYPE_DES_CBC_MD5:
 907    case ENCTYPE_DES_CBC_CRC:
 908        ctx->subkey->enctype = ENCTYPE_DES_CBC_RAW;
 909        ctx->signalg = SGN_ALG_DES_MAC_MD5;
 910        ctx->cksum_size = 8;
 911        ctx->sealalg = SEAL_ALG_DES;
 912 
 913        /* fill in the encryption descriptors */
 914 
 915        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
 916            major_status = GSS_S_FAILURE;
 917            goto fail;
 918        }
 919 
 920        for (i=0; i<ctx->enc->length; i++)
 921            /*SUPPRESS 113*/
 922            ctx->enc->contents[i] ^= 0xf0;
 923 
 924        goto copy_subkey_to_seq;
 925        break;
 926 
 927    case ENCTYPE_DES3_CBC_SHA1:
 928        ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
 929        ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
 930        ctx->cksum_size = 20;
 931        ctx->sealalg = SEAL_ALG_DES3KD;
 932 
 933        /* fill in the encryption descriptors */
 934    copy_subkey:
 935        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
 936            major_status = GSS_S_FAILURE;
 937            goto fail;
 938        }
 939    copy_subkey_to_seq:
 940        if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
 941            major_status = GSS_S_FAILURE;
 942            goto fail;
 943        }
 944        break;
 945 
 946    case ENCTYPE_ARCFOUR_HMAC:
 947        ctx->signalg = SGN_ALG_HMAC_MD5 ;
 948        ctx->cksum_size = 8;
 949        ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
 950        goto copy_subkey;
 951 
 952    default:
 953        ctx->signalg = -1;
 954        ctx->sealalg = -1;
 955        ctx->proto = 1;
 956        code = (*kaccess.krb5int_c_mandatory_cksumtype)(context, ctx->subkey->enctype,
 957                                             &ctx->cksumtype);
 958        if (code)
 959            goto fail;
 960        code = krb5_c_checksum_length(context, ctx->cksumtype,
 961                 (size_t *)&ctx->cksum_size);
 962        if (code)
 963            goto fail;
 964        ctx->have_acceptor_subkey = 0;
 965        goto copy_subkey;
 966    }
 967 
 968     /* Solaris Kerberos */
 969    KRB5_LOG1(KRB5_ERR, "accept_sec_context:  subkey enctype = %d proto = %d",
 970         ctx->subkey->enctype, ctx->proto);
 971 
 972    ctx->endtime = ticket->enc_part2->times.endtime;
 973    ctx->krb_flags = ticket->enc_part2->flags;
 974 
 975    krb5_free_ticket(context, ticket); /* Done with ticket */
 976 
 977    {
 978        krb5_ui_4 seq_temp;
 979        krb5_auth_con_getremoteseqnumber(context, auth_context,
 980                 (krb5_int32 *)&seq_temp);
 981        ctx->seq_recv = seq_temp;
 982    }
 983 
 984    if ((code = krb5_timeofday(context, &now))) {
 985        major_status = GSS_S_FAILURE;
 986        goto fail;
 987    }
 988 
 989    if (ctx->endtime < now) {
 990        code = 0;
 991        major_status = GSS_S_CREDENTIALS_EXPIRED;
 992        goto fail;
 993    }
 994 
 995    g_order_init(&(ctx->seqstate), ctx->seq_recv,
 996                 (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
 997                 (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
 998 
 999    /* at this point, the entire context structure is filled in, 
1000       so it can be released.  */
1001 
1002    /* generate an AP_REP if necessary */
1003 
1004    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
1005        unsigned char * ptr3;
1006        krb5_ui_4 seq_temp;
1007        int cfx_generate_subkey;
1008 
1009        if (ctx->proto == 1)
1010            cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
1011        else
1012            cfx_generate_subkey = 0;
1013 
1014        if (cfx_generate_subkey) {
1015            krb5_int32 acflags;
1016            code = krb5_auth_con_getflags(context, auth_context, &acflags);
1017            if (code == 0) {
1018                acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
1019                code = krb5_auth_con_setflags(context, auth_context, acflags);
1020            }
1021            if (code) {
1022                major_status = GSS_S_FAILURE;
1023                goto fail;
1024            }
1025        }
1026 
1027        if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
1028            major_status = GSS_S_FAILURE;
1029            goto fail;
1030        }
1031 
1032        krb5_auth_con_getlocalseqnumber(context, auth_context,
1033                 (krb5_int32 *)&seq_temp);
1034        ctx->seq_send = seq_temp & 0xffffffffL;
1035 
1036        if (cfx_generate_subkey) {
1037            /* Get the new acceptor subkey.  With the code above, there
1038               should always be one if we make it to this point.  */
1039            code = krb5_auth_con_getsendsubkey(context, auth_context,
1040                                               &ctx->acceptor_subkey);
1041            if (code != 0) {
1042                major_status = GSS_S_FAILURE;
1043                goto fail;
1044            }
1045            code = (*kaccess.krb5int_c_mandatory_cksumtype)(context,
1046                                                 ctx->acceptor_subkey->enctype,
1047                                                 &ctx->acceptor_subkey_cksumtype);
1048            if (code) {
1049                major_status = GSS_S_FAILURE;
1050                goto fail;
1051            }
1052            ctx->have_acceptor_subkey = 1;
1053        }
1054 
1055        /* the reply token hasn't been sent yet, but that's ok. */
1056        ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
1057        ctx->established = 1;
1058 
1059        token.length = g_token_size(mech_used, ap_rep.length);
1060 
1061        if ((token.value = (unsigned char *) xmalloc(token.length))
1062            == NULL) {
1063            major_status = GSS_S_FAILURE;
1064            code = ENOMEM;
1065            goto fail;
1066        }
1067        ptr3 = token.value;
1068        g_make_token_header(mech_used, ap_rep.length,
1069                            &ptr3, KG_TOK_CTX_AP_REP);
1070 
1071        TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
1072 
1073        ctx->established = 1;
1074 
1075    } else {
1076        token.length = 0;
1077        token.value = NULL;
1078        ctx->seq_send = ctx->seq_recv;
1079 
1080        ctx->established = 1;
1081    }
1082 
1083    /* set the return arguments */
1084 
1085    /*
1086     * Solaris Kerberos
1087     * Regardless of src_name, get name for error msg if neeeded.
1088     */
1089    if ((code = krb5_copy_principal(context, ctx->there, &client_name))) {
1090         major_status = GSS_S_FAILURE;
1091         goto fail;
1092    }
1093    if ((code = krb5_copy_principal(context, ctx->here, &server_name))) {
1094         major_status = GSS_S_FAILURE;
1095         goto fail;
1096    }
1097    /* intern the src_name */
1098    if (! kg_save_name((gss_name_t) client_name)) {
1099         code = G_VALIDATE_FAILED;
1100         major_status = GSS_S_FAILURE;
1101         goto fail;
1102    }
1103 
1104    if (time_rec)
1105       *time_rec = ctx->endtime - now;
1106 
1107    if (ret_flags)
1108       *ret_flags = ctx->gss_flags;
1109 
1110    *context_handle = (gss_ctx_id_t)ctx;
1111    *output_token = token;
1112 
1113    if (src_name)
1114       *src_name = (gss_name_t) client_name;
1115 
1116    if (delegated_cred_handle && deleg_cred) {
1117        if (!kg_save_cred_id((gss_cred_id_t) deleg_cred)) {
1118            /* Solaris Kerberos */
1119            KRB5_LOG0(KRB5_ERR, "krb5_gss_accept_sec_context() "
1120                       "kg_save_cred_id() error");
1121            major_status = GSS_S_FAILURE;
1122            code = (OM_uint32) G_VALIDATE_FAILED;
1123            goto fail;
1124        }
1125 
1126        *delegated_cred_handle = (gss_cred_id_t) deleg_cred;
1127    }
1128 
1129    /* finally! */
1130 
1131    *minor_status = 0;
1132    major_status = GSS_S_COMPLETE;
1133 
1134  fail:
1135    if (mech_type) {
1136         unsigned int min;
1137         gss_buffer_desc oidstr;
1138         oidstr.value = NULL;
1139 
1140         /*
1141          * This needs to be set/returned even on fail so
1142          * gss_accept_sec_context() can map_error_oid() the correct
1143          * error/oid for later use by gss_display_status().
1144          * (needed in CIFS/SPNEGO case)
1145          */
1146         *mech_type = (gss_OID) mech_used;
1147 
1148         (void) gss_oid_to_str(&min, *mech_type, &oidstr);
1149    }
1150 
1151    if (authdat)
1152        krb5_free_authenticator(context, authdat);
1153    /* The ctx structure has the handle of the auth_context */
1154    if (auth_context && !ctx) {
1155        if (cred_rcache)
1156            (void)krb5_auth_con_setrcache(context, auth_context, NULL);
1157 
1158        krb5_auth_con_free(context, auth_context);
1159    }
1160    if (reqcksum.contents)
1161        xfree(reqcksum.contents);
1162    if (ap_rep.data)
1163        xfree(ap_rep.data);
1164 
1165    if (request != NULL) {
1166         saved_ap_options = request->ap_options;
1167         krb5_free_ap_req(context, request);
1168         request = NULL;
1169    }
1170 
1171    if (!GSS_ERROR(major_status) && major_status != GSS_S_CONTINUE_NEEDED) {
1172         if (!verifier_cred_handle && cred_handle) {
1173                 krb5_gss_release_cred(minor_status, &cred_handle);
1174         }
1175 
1176         if (ctx)
1177             ctx->k5_context = context;
1178 
1179         return(major_status);
1180    }
1181 
1182    /* from here on is the real "fail" code */
1183 
1184    if (ctx)
1185        (void) krb5_gss_delete_sec_context(minor_status, 
1186                                           (gss_ctx_id_t *) &ctx, NULL);
1187    if (deleg_cred) { /* free memory associated with the deleg credential */
1188        if (deleg_cred->ccache)
1189            (void)krb5_cc_close(context, deleg_cred->ccache);
1190        if (deleg_cred->princ)
1191            krb5_free_principal(context, deleg_cred->princ);
1192        xfree(deleg_cred);
1193    }
1194    if (token.value)
1195        xfree(token.value);
1196 
1197    *minor_status = code;
1198 
1199    if (saved_ap_options & AP_OPTS_MUTUAL_REQUIRED)
1200         gss_flags |= GSS_C_MUTUAL_FLAG;
1201 
1202    if (cred
1203        && ((gss_flags & GSS_C_MUTUAL_FLAG)
1204            || (major_status == GSS_S_CONTINUE_NEEDED))) {
1205        unsigned int tmsglen;
1206        int toktype;
1207 
1208        /*
1209         * The client is expecting a response, so we can send an
1210         * error token back
1211         */
1212 
1213        /*
1214         * Solaris Kerberos: We need to remap error conditions for buggy
1215         * Windows clients if the MS_INTEROP env var has been set.
1216         */
1217        if ((code == KRB5KRB_AP_ERR_BAD_INTEGRITY ||
1218           code == KRB5KRB_AP_ERR_NOKEY || code == KRB5KRB_AP_ERR_BADKEYVER)
1219           && krb5_getenv("MS_INTEROP")) {
1220            code = KRB5KRB_AP_ERR_MODIFIED;
1221            major_status = GSS_S_CONTINUE_NEEDED;
1222        }
1223 
1224             /*
1225              * SUNW17PACresync / Solaris Kerberos
1226              * Set e-data to Windows constant.
1227              * (verified by MSFT)
1228              * 
1229              * This facilitates the Windows CIFS client clock skew
1230              * recovery feature.
1231              */
1232        if (code == KRB5KRB_AP_ERR_SKEW && krb5_getenv("MS_INTEROP")) {
1233             char *ms_e_data = "\x30\x05\xa1\x03\x02\x01\x02";
1234             int len = strlen(ms_e_data);
1235 
1236             krb_error_data.e_data.data = malloc(len);
1237             if (krb_error_data.e_data.data) {
1238                     (void) memcpy(krb_error_data.e_data.data, ms_e_data, len);
1239                     krb_error_data.e_data.length = len;
1240             }
1241             major_status = GSS_S_CONTINUE_NEEDED;
1242        }
1243 
1244        code -= ERROR_TABLE_BASE_krb5;
1245        if (code < 0 || code > 128)
1246            code = 60 /* KRB_ERR_GENERIC */;
1247 
1248        krb_error_data.error = code;
1249        (void) krb5_us_timeofday(context, &krb_error_data.stime,
1250                                 &krb_error_data.susec);
1251        krb_error_data.server = cred->princ;
1252 
1253        code = krb5_mk_error(context, &krb_error_data, &scratch);
1254        if (code)
1255            goto cleanup;
1256 
1257        tmsglen = scratch.length;
1258        toktype = KG_TOK_CTX_ERROR;
1259 
1260        token.length = g_token_size(mech_used, tmsglen);
1261        token.value = (unsigned char *) xmalloc(token.length);
1262        if (!token.value)
1263           goto cleanup;
1264 
1265        ptr = token.value;
1266        g_make_token_header(mech_used, tmsglen, &ptr, toktype);
1267 
1268        TWRITE_STR(ptr, scratch.data, scratch.length);
1269        xfree(scratch.data);
1270 
1271        *output_token = token;
1272    }
1273 
1274 cleanup:
1275    /* Solaris Kerberos */
1276    if (krb_error_data.e_data.data != NULL)
1277         free(krb_error_data.e_data.data);
1278         
1279    if (!verifier_cred_handle && cred_handle) {
1280         krb5_gss_release_cred(&t_minor_status, &cred_handle);
1281    }
1282 
1283    /*
1284     * Solaris Kerberos
1285     * Enhance the error message.
1286     */
1287    if (GSS_ERROR(major_status)) {
1288        if (client_name && server_name &&
1289          (*minor_status == (OM_uint32)KRB5KRB_AP_ERR_BAD_INTEGRITY)) {
1290             char *c_name = NULL;
1291             char *s_name = NULL;
1292             krb5_error_code cret, sret;
1293             cret = krb5_unparse_name(context, (krb5_principal) client_name,
1294                                     &c_name);
1295             sret = krb5_unparse_name(context, (krb5_principal) server_name,
1296                                     &s_name);
1297             krb5_set_error_message(context, *minor_status,
1298                                 dgettext(TEXT_DOMAIN,
1299                                         "Decrypt integrity check failed for client '%s' and server '%s'"),
1300                                 cret == 0 ? c_name : "unknown",
1301                                 sret == 0 ? s_name : "unknown");
1302             if (s_name)
1303                     krb5_free_unparsed_name(context, s_name);
1304             if (c_name)
1305                     krb5_free_unparsed_name(context, c_name);
1306             }
1307        /*
1308         * Solaris Kerberos
1309         * krb5_gss_acquire_cred() does not take a context arg
1310         * (and does a save_error_info() itself) so re-calling
1311         * save_error_info() here is trouble.
1312         */
1313        if (!acquire_fail)
1314             save_error_info(*minor_status, context);
1315    }
1316    if (client_name) {
1317         (void) kg_delete_name((gss_name_t) client_name);
1318    }
1319    if (server_name)
1320         krb5_free_principal(context, server_name);
1321    krb5_free_context(context);
1322 
1323    /* Solaris Kerberos */
1324    KRB5_LOG(KRB5_ERR,"krb5_gss_accept_sec_context() end, "
1325               "major_status = %d", major_status);
1326    return (major_status);
1327 }