Blame src/tests/gssapi/t_spnego.c

Packit fd8b60
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
Packit fd8b60
/*
Packit fd8b60
 * Copyright 2010  by the Massachusetts Institute of Technology.
Packit fd8b60
 * All Rights Reserved.
Packit fd8b60
 *
Packit fd8b60
 * Export of this software from the United States of America may
Packit fd8b60
 *   require a specific license from the United States Government.
Packit fd8b60
 *   It is the responsibility of any person or organization contemplating
Packit fd8b60
 *   export to obtain such a license before exporting.
Packit fd8b60
 *
Packit fd8b60
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
Packit fd8b60
 * distribute this software and its documentation for any purpose and
Packit fd8b60
 * without fee is hereby granted, provided that the above copyright
Packit fd8b60
 * notice appear in all copies and that both that copyright notice and
Packit fd8b60
 * this permission notice appear in supporting documentation, and that
Packit fd8b60
 * the name of M.I.T. not be used in advertising or publicity pertaining
Packit fd8b60
 * to distribution of the software without specific, written prior
Packit fd8b60
 * permission.  Furthermore if you modify this software you must label
Packit fd8b60
 * your software as modified software and not distribute it in such a
Packit fd8b60
 * fashion that it might be confused with the original M.I.T. software.
Packit fd8b60
 * M.I.T. makes no representations about the suitability of
Packit fd8b60
 * this software for any purpose.  It is provided "as is" without express
Packit fd8b60
 * or implied warranty.
Packit fd8b60
 *
Packit fd8b60
 */
Packit fd8b60
Packit fd8b60
#include <stdio.h>
Packit fd8b60
#include <stdlib.h>
Packit fd8b60
#include <string.h>
Packit fd8b60
#include <assert.h>
Packit fd8b60
Packit fd8b60
#include "common.h"
Packit fd8b60
Packit fd8b60
static gss_OID_desc mech_krb5_wrong = {
Packit fd8b60
    9, "\052\206\110\202\367\022\001\002\002"
Packit fd8b60
};
Packit fd8b60
gss_OID_set_desc mechset_krb5_wrong = { 1, &mech_krb5_wrong };
Packit fd8b60
Packit fd8b60
/*
Packit fd8b60
 * Test program for SPNEGO and gss_set_neg_mechs
Packit fd8b60
 *
Packit fd8b60
 * Example usage:
Packit fd8b60
 *
Packit fd8b60
 * kinit testuser
Packit fd8b60
 * ./t_spnego host/test.host@REALM testhost.keytab
Packit fd8b60
 */
Packit fd8b60
Packit fd8b60
/* Replace *tok and *len with the concatenation of prefix and *tok. */
Packit fd8b60
static void
Packit fd8b60
prepend(const void *prefix, size_t plen, uint8_t **tok, size_t *len)
Packit fd8b60
{
Packit fd8b60
    uint8_t *newtok;
Packit fd8b60
Packit fd8b60
    newtok = malloc(plen + *len);
Packit fd8b60
    assert(newtok != NULL);
Packit fd8b60
    memcpy(newtok, prefix, plen);
Packit fd8b60
    memcpy(newtok + plen, *tok, *len);
Packit fd8b60
    free(*tok);
Packit fd8b60
    *tok = newtok;
Packit fd8b60
    *len = plen + *len;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Replace *tok and *len with *tok wrapped in a DER tag with the given tag
Packit fd8b60
 * byte.  *len must be less than 2^16. */
Packit fd8b60
static void
Packit fd8b60
der_wrap(uint8_t tag, uint8_t **tok, size_t *len)
Packit fd8b60
{
Packit fd8b60
    char lenbuf[3];
Packit fd8b60
    uint8_t *wrapped;
Packit fd8b60
    size_t llen;
Packit fd8b60
Packit fd8b60
    if (*len < 128) {
Packit fd8b60
        lenbuf[0] = *len;
Packit fd8b60
        llen = 1;
Packit fd8b60
    } else if (*len < 256) {
Packit fd8b60
        lenbuf[0] = 0x81;
Packit fd8b60
        lenbuf[1] = *len;
Packit fd8b60
        llen = 2;
Packit fd8b60
    } else {
Packit fd8b60
        assert(*len >> 16 == 0);
Packit fd8b60
        lenbuf[0] = 0x82;
Packit fd8b60
        lenbuf[1] = *len >> 8;
Packit fd8b60
        lenbuf[2] = *len & 0xFF;
Packit fd8b60
        llen = 3;
Packit fd8b60
    }
Packit fd8b60
    wrapped = malloc(1 + llen + *len);
Packit fd8b60
    assert(wrapped != NULL);
Packit fd8b60
    *wrapped = tag;
Packit fd8b60
    memcpy(wrapped + 1, lenbuf, llen);
Packit fd8b60
    memcpy(wrapped + 1 + llen, *tok, *len);
Packit fd8b60
    free(*tok);
Packit fd8b60
    *tok = wrapped;
Packit fd8b60
    *len = 1 + llen + *len;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/*
Packit fd8b60
 * Create a SPNEGO initiator token for the erroneous Microsoft krb5 mech OID,
Packit fd8b60
 * wrapping a krb5 token ktok.  The token should look like:
Packit fd8b60
 *
Packit fd8b60
 * 60 <len> (GSS framing sequence)
Packit fd8b60
 *   06 06 2B 06 01 05 05 02 (SPNEGO OID)
Packit fd8b60
 *   A0 <len> (NegotiationToken choice 0, negTokenInit)
Packit fd8b60
 *     30 <len> (sequence)
Packit fd8b60
 *       A0 0D (context tag 0, mechTypes)
Packit fd8b60
 *         30 0B (sequence of)
Packit fd8b60
 *           06 09 2A 86 48 82 F7 12 01 02 02 (wrong krb5 OID)
Packit fd8b60
 *       A2 <len> (context tag 2, mechToken)
Packit fd8b60
 *         04 <len> (octet string)
Packit fd8b60
 *           <mech token octets>
Packit fd8b60
 */
Packit fd8b60
static void
Packit fd8b60
create_mskrb5_spnego_token(gss_buffer_t ktok, gss_buffer_desc *tok_out)
Packit fd8b60
{
Packit fd8b60
    uint8_t *tok;
Packit fd8b60
    size_t len;
Packit fd8b60
Packit fd8b60
    len = ktok->length;
Packit fd8b60
    tok = malloc(len);
Packit fd8b60
    assert(tok != NULL);
Packit fd8b60
    memcpy(tok, ktok->value, len);
Packit fd8b60
    /* Wrap the krb5 token in OCTET STRING and [2] tags. */
Packit fd8b60
    der_wrap(0x04, &tok, &len;;
Packit fd8b60
    der_wrap(0xA2, &tok, &len;;
Packit fd8b60
    /* Prepend the wrong krb5 OID inside OBJECT IDENTIFIER and [0] tags. */
Packit fd8b60
    prepend("\xA0\x0D\x30\x0B\x06\x09\x2A\x86\x48\x82\xF7\x12\x01\x02\x02", 15,
Packit fd8b60
            &tok, &len;;
Packit fd8b60
    /* Wrap the previous two things in SEQUENCE and [0] tags. */
Packit fd8b60
    der_wrap(0x30, &tok, &len;;
Packit fd8b60
    der_wrap(0xA0, &tok, &len;;
Packit fd8b60
    /* Prepend the SPNEGO OID in an OBJECT IDENTIFIER tag. */
Packit fd8b60
    prepend("\x06\x06\x2B\x06\x01\x05\x05\x02", 8, &tok, &len;;
Packit fd8b60
    /* Wrap the whole thing in an [APPLICATION 0] tag. */
Packit fd8b60
    der_wrap(0x60, &tok, &len;;
Packit fd8b60
    tok_out->value = tok;
Packit fd8b60
    tok_out->length = len;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/*
Packit fd8b60
 * Test that the SPNEGO acceptor code accepts and properly reflects back the
Packit fd8b60
 * erroneous Microsoft mech OID in the supportedMech field of the NegTokenResp
Packit fd8b60
 * message.  Use acred as the verifier cred handle.
Packit fd8b60
 */
Packit fd8b60
static void
Packit fd8b60
test_mskrb_oid(gss_name_t tname, gss_cred_id_t acred)
Packit fd8b60
{
Packit fd8b60
    OM_uint32 major, minor;
Packit fd8b60
    gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT;
Packit fd8b60
    gss_buffer_desc atok = GSS_C_EMPTY_BUFFER, ktok = GSS_C_EMPTY_BUFFER, stok;
Packit fd8b60
    const unsigned char *atok_oid;
Packit fd8b60
Packit fd8b60
    /*
Packit fd8b60
     * Our SPNEGO mech no longer acquires creds for the wrong mech OID, so we
Packit fd8b60
     * have to construct a SPNEGO token ourselves.
Packit fd8b60
     */
Packit fd8b60
    major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, tname,
Packit fd8b60
                                 &mech_krb5, 0, GSS_C_INDEFINITE,
Packit fd8b60
                                 GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &ktok,
Packit fd8b60
                                 NULL, NULL);
Packit fd8b60
    check_gsserr("gss_init_sec_context(mskrb)", major, minor);
Packit fd8b60
    assert(major == GSS_S_COMPLETE);
Packit fd8b60
    create_mskrb5_spnego_token(&ktok, &stok);
Packit fd8b60
Packit fd8b60
    /*
Packit fd8b60
     * Look directly at the DER encoding of the response token.  Since we
Packit fd8b60
     * didn't request mutual authentication, the SPNEGO reply will contain no
Packit fd8b60
     * underlying mech token; therefore, the encoding of the correct
Packit fd8b60
     * NegotiationToken response is completely predictable:
Packit fd8b60
     *
Packit fd8b60
     *   A1 14 (choice 1, length 20, meaning negTokenResp)
Packit fd8b60
     *     30 12 (sequence, length 18)
Packit fd8b60
     *       A0 03 (context tag 0, length 3)
Packit fd8b60
     *         0A 01 00 (enumerated value 0, meaning accept-completed)
Packit fd8b60
     *       A1 0B (context tag 1, length 11)
Packit fd8b60
     *         06 09 (object identifier, length 9)
Packit fd8b60
     *           2A 86 48 82 F7 12 01 02 02 (the erroneous krb5 OID)
Packit fd8b60
     *
Packit fd8b60
     * So we can just compare the length to 22 and the nine bytes at offset 13
Packit fd8b60
     * to the expected OID.
Packit fd8b60
     */
Packit fd8b60
    major = gss_accept_sec_context(&minor, &actx, acred, &stok,
Packit fd8b60
                                   GSS_C_NO_CHANNEL_BINDINGS, NULL,
Packit fd8b60
                                   NULL, &atok, NULL, NULL, NULL);
Packit fd8b60
    check_gsserr("gss_accept_sec_context(mskrb)", major, minor);
Packit fd8b60
    assert(atok.length == 22);
Packit fd8b60
    atok_oid = (unsigned char *)atok.value + 13;
Packit fd8b60
    assert(memcmp(atok_oid, mech_krb5_wrong.elements, 9) == 0);
Packit fd8b60
Packit fd8b60
    (void)gss_delete_sec_context(&minor, &ictx, NULL);
Packit fd8b60
    (void)gss_delete_sec_context(&minor, &actx, NULL);
Packit fd8b60
    (void)gss_release_buffer(&minor, &ktok);
Packit fd8b60
    (void)gss_release_buffer(&minor, &atok);
Packit fd8b60
    free(stok.value);
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Check that we return a compatibility NegTokenInit2 message containing
Packit fd8b60
 * NegHints for an empty initiator token. */
Packit fd8b60
static void
Packit fd8b60
test_neghints()
Packit fd8b60
{
Packit fd8b60
    OM_uint32 major, minor;
Packit fd8b60
    gss_buffer_desc itok = GSS_C_EMPTY_BUFFER, atok;
Packit fd8b60
    gss_ctx_id_t actx = GSS_C_NO_CONTEXT;
Packit fd8b60
    const char *expected =
Packit fd8b60
        /* RFC 2743 token framing: [APPLICATION 0] IMPLICIT SEQUENCE followed
Packit fd8b60
         * by OBJECT IDENTIFIER and the SPNEGO OID */
Packit fd8b60
        "\x60\x47\x06\x06" "\x2B\x06\x01\x05\x05\x02"
Packit fd8b60
        /* [0] SEQUENCE for the NegotiationToken negtokenInit choice */
Packit fd8b60
        "\xA0\x3D\x30\x3B"
Packit fd8b60
        /* [0] MechTypeList containing the krb5 OID */
Packit fd8b60
        "\xA0\x0D\x30\x0B\x06\x09" "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"
Packit fd8b60
        /* [3] NegHints containing [0] GeneralString containing the dummy
Packit fd8b60
         * hintName string defined in [MS-SPNG] */
Packit fd8b60
        "\xA3\x2A\x30\x28\xA0\x26\x1B\x24"
Packit fd8b60
        "not_defined_in_RFC4178@please_ignore";
Packit fd8b60
Packit fd8b60
    /* Produce a hint token. */
Packit fd8b60
    major = gss_accept_sec_context(&minor, &actx, GSS_C_NO_CREDENTIAL, &itok,
Packit fd8b60
                                   GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,
Packit fd8b60
                                   &atok, NULL, NULL, NULL);
Packit fd8b60
    check_gsserr("gss_accept_sec_context(neghints)", major, minor);
Packit fd8b60
Packit fd8b60
    /* Verify it against the expected contents, which are fixed as long as we
Packit fd8b60
     * only list the krb5 mech in the token. */
Packit fd8b60
    assert(atok.length == strlen(expected));
Packit fd8b60
    assert(memcmp(atok.value, expected, atok.length) == 0);
Packit fd8b60
Packit fd8b60
    (void)gss_release_buffer(&minor, &atok);
Packit fd8b60
    (void)gss_delete_sec_context(&minor, &actx, NULL);
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
int
Packit fd8b60
main(int argc, char *argv[])
Packit fd8b60
{
Packit fd8b60
    OM_uint32 minor, major, flags;
Packit fd8b60
    gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL;
Packit fd8b60
    gss_cred_id_t initiator_cred_handle = GSS_C_NO_CREDENTIAL;
Packit fd8b60
    gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
Packit fd8b60
    gss_ctx_id_t initiator_context, acceptor_context;
Packit fd8b60
    gss_name_t target_name, source_name = GSS_C_NO_NAME;
Packit fd8b60
    gss_OID mech = GSS_C_NO_OID;
Packit fd8b60
    gss_OID_desc pref_oids[2];
Packit fd8b60
    gss_OID_set_desc pref_mechs;
Packit fd8b60
Packit fd8b60
    if (argc < 2 || argc > 3) {
Packit fd8b60
        fprintf(stderr, "Usage: %s target_name [keytab]\n", argv[0]);
Packit fd8b60
        exit(1);
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    target_name = import_name(argv[1]);
Packit fd8b60
Packit fd8b60
    if (argc >= 3) {
Packit fd8b60
        major = krb5_gss_register_acceptor_identity(argv[2]);
Packit fd8b60
        check_gsserr("krb5_gss_register_acceptor_identity", major, 0);
Packit fd8b60
    }
Packit fd8b60
Packit fd8b60
    /* Get default initiator cred. */
Packit fd8b60
    major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
Packit fd8b60
                             &mechset_spnego, GSS_C_INITIATE,
Packit fd8b60
                             &initiator_cred_handle, NULL, NULL);
Packit fd8b60
    check_gsserr("gss_acquire_cred(initiator)", major, minor);
Packit fd8b60
Packit fd8b60
    /*
Packit fd8b60
     * The following test is designed to exercise SPNEGO reselection on the
Packit fd8b60
     * client and server.  Unfortunately, it no longer does so after tickets
Packit fd8b60
     * #8217 and #8021, since SPNEGO now only acquires a single krb5 cred and
Packit fd8b60
     * there is no way to expand the underlying creds with gss_set_neg_mechs().
Packit fd8b60
     * To fix this we need gss_acquire_cred_with_cred() or some other way to
Packit fd8b60
     * turn a cred with a specifically requested mech set into a SPNEGO cred.
Packit fd8b60
     */
Packit fd8b60
Packit fd8b60
    /* Make the initiator prefer IAKERB and offer krb5 as an alternative. */
Packit fd8b60
    pref_oids[0] = mech_iakerb;
Packit fd8b60
    pref_oids[1] = mech_krb5;
Packit fd8b60
    pref_mechs.count = 2;
Packit fd8b60
    pref_mechs.elements = pref_oids;
Packit fd8b60
    major = gss_set_neg_mechs(&minor, initiator_cred_handle, &pref_mechs);
Packit fd8b60
    check_gsserr("gss_set_neg_mechs(initiator)", major, minor);
Packit fd8b60
Packit fd8b60
    /* Get default acceptor cred. */
Packit fd8b60
    major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
Packit fd8b60
                             &mechset_spnego, GSS_C_ACCEPT,
Packit fd8b60
                             &verifier_cred_handle, &actual_mechs, NULL);
Packit fd8b60
    check_gsserr("gss_acquire_cred(acceptor)", major, minor);
Packit fd8b60
Packit fd8b60
    /* Restrict the acceptor to krb5 (which will force a reselection). */
Packit fd8b60
    major = gss_set_neg_mechs(&minor, verifier_cred_handle, &mechset_krb5);
Packit fd8b60
    check_gsserr("gss_set_neg_mechs(acceptor)", major, minor);
Packit fd8b60
Packit fd8b60
    flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
Packit fd8b60
    establish_contexts(&mech_spnego, initiator_cred_handle,
Packit fd8b60
                       verifier_cred_handle, target_name, flags,
Packit fd8b60
                       &initiator_context, &acceptor_context, &source_name,
Packit fd8b60
                       &mech, NULL);
Packit fd8b60
Packit fd8b60
    display_canon_name("Source name", source_name, &mech_krb5);
Packit fd8b60
    display_oid("Source mech", mech);
Packit fd8b60
Packit fd8b60
    /* Test acceptance of the erroneous Microsoft krb5 OID, with and without an
Packit fd8b60
     * acceptor cred. */
Packit fd8b60
    test_mskrb_oid(target_name, verifier_cred_handle);
Packit fd8b60
    test_mskrb_oid(target_name, GSS_C_NO_CREDENTIAL);
Packit fd8b60
Packit fd8b60
    test_neghints();
Packit fd8b60
Packit fd8b60
    (void)gss_delete_sec_context(&minor, &initiator_context, NULL);
Packit fd8b60
    (void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
Packit fd8b60
    (void)gss_release_name(&minor, &source_name);
Packit fd8b60
    (void)gss_release_name(&minor, &target_name);
Packit fd8b60
    (void)gss_release_cred(&minor, &initiator_cred_handle);
Packit fd8b60
    (void)gss_release_cred(&minor, &verifier_cred_handle);
Packit fd8b60
    (void)gss_release_oid_set(&minor, &actual_mechs);
Packit fd8b60
Packit fd8b60
    return 0;
Packit fd8b60
}