Path: blob/main/crypto/krb5/src/tests/gssapi/t_spnego.c
34890 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright 2010 by the Massachusetts Institute of Technology.3* All Rights Reserved.4*5* Export of this software from the United States of America may6* require a specific license from the United States Government.7* It is the responsibility of any person or organization contemplating8* export to obtain such a license before exporting.9*10* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and11* distribute this software and its documentation for any purpose and12* without fee is hereby granted, provided that the above copyright13* notice appear in all copies and that both that copyright notice and14* this permission notice appear in supporting documentation, and that15* the name of M.I.T. not be used in advertising or publicity pertaining16* to distribution of the software without specific, written prior17* permission. Furthermore if you modify this software you must label18* your software as modified software and not distribute it in such a19* fashion that it might be confused with the original M.I.T. software.20* M.I.T. makes no representations about the suitability of21* this software for any purpose. It is provided "as is" without express22* or implied warranty.23*24*/2526#include <stdio.h>27#include <stdlib.h>28#include <string.h>29#include <assert.h>3031#include "common.h"3233static gss_OID_desc mech_krb5_wrong = {349, "\052\206\110\202\367\022\001\002\002"35};36gss_OID_set_desc mechset_krb5_wrong = { 1, &mech_krb5_wrong };3738/*39* Test program for SPNEGO and gss_set_neg_mechs40*41* Example usage:42*43* kinit testuser44* ./t_spnego host/test.host@REALM testhost.keytab45*/4647/* Replace *tok and *len with the concatenation of prefix and *tok. */48static void49prepend(const void *prefix, size_t plen, uint8_t **tok, size_t *len)50{51uint8_t *newtok;5253newtok = malloc(plen + *len);54assert(newtok != NULL);55memcpy(newtok, prefix, plen);56memcpy(newtok + plen, *tok, *len);57free(*tok);58*tok = newtok;59*len = plen + *len;60}6162/* Replace *tok and *len with *tok wrapped in a DER tag with the given tag63* byte. *len must be less than 2^16. */64static void65der_wrap(uint8_t tag, uint8_t **tok, size_t *len)66{67char lenbuf[3];68uint8_t *wrapped;69size_t llen;7071if (*len < 128) {72lenbuf[0] = *len;73llen = 1;74} else if (*len < 256) {75lenbuf[0] = 0x81;76lenbuf[1] = *len;77llen = 2;78} else {79assert(*len >> 16 == 0);80lenbuf[0] = 0x82;81lenbuf[1] = *len >> 8;82lenbuf[2] = *len & 0xFF;83llen = 3;84}85wrapped = malloc(1 + llen + *len);86assert(wrapped != NULL);87*wrapped = tag;88memcpy(wrapped + 1, lenbuf, llen);89memcpy(wrapped + 1 + llen, *tok, *len);90free(*tok);91*tok = wrapped;92*len = 1 + llen + *len;93}9495/*96* Create a SPNEGO initiator token for the erroneous Microsoft krb5 mech OID,97* wrapping a krb5 token ktok. The token should look like:98*99* 60 <len> (GSS framing sequence)100* 06 06 2B 06 01 05 05 02 (SPNEGO OID)101* A0 <len> (NegotiationToken choice 0, negTokenInit)102* 30 <len> (sequence)103* A0 0D (context tag 0, mechTypes)104* 30 0B (sequence of)105* 06 09 2A 86 48 82 F7 12 01 02 02 (wrong krb5 OID)106* A2 <len> (context tag 2, mechToken)107* 04 <len> (octet string)108* <mech token octets>109*/110static void111create_mskrb5_spnego_token(gss_buffer_t ktok, gss_buffer_desc *tok_out)112{113uint8_t *tok;114size_t len;115116len = ktok->length;117tok = malloc(len);118assert(tok != NULL);119memcpy(tok, ktok->value, len);120/* Wrap the krb5 token in OCTET STRING and [2] tags. */121der_wrap(0x04, &tok, &len);122der_wrap(0xA2, &tok, &len);123/* Prepend the wrong krb5 OID inside OBJECT IDENTIFIER and [0] tags. */124prepend("\xA0\x0D\x30\x0B\x06\x09\x2A\x86\x48\x82\xF7\x12\x01\x02\x02", 15,125&tok, &len);126/* Wrap the previous two things in SEQUENCE and [0] tags. */127der_wrap(0x30, &tok, &len);128der_wrap(0xA0, &tok, &len);129/* Prepend the SPNEGO OID in an OBJECT IDENTIFIER tag. */130prepend("\x06\x06\x2B\x06\x01\x05\x05\x02", 8, &tok, &len);131/* Wrap the whole thing in an [APPLICATION 0] tag. */132der_wrap(0x60, &tok, &len);133tok_out->value = tok;134tok_out->length = len;135}136137/*138* Test that the SPNEGO acceptor code accepts and properly reflects back the139* erroneous Microsoft mech OID in the supportedMech field of the NegTokenResp140* message. Use acred as the verifier cred handle.141*/142static void143test_mskrb_oid(gss_name_t tname, gss_cred_id_t acred)144{145OM_uint32 major, minor;146gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT;147gss_buffer_desc atok = GSS_C_EMPTY_BUFFER, ktok = GSS_C_EMPTY_BUFFER, stok;148const unsigned char *atok_oid;149150/*151* Our SPNEGO mech no longer acquires creds for the wrong mech OID, so we152* have to construct a SPNEGO token ourselves.153*/154major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, tname,155&mech_krb5, 0, GSS_C_INDEFINITE,156GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &ktok,157NULL, NULL);158check_gsserr("gss_init_sec_context(mskrb)", major, minor);159assert(major == GSS_S_COMPLETE);160create_mskrb5_spnego_token(&ktok, &stok);161162/*163* Look directly at the DER encoding of the response token. Since we164* didn't request mutual authentication, the SPNEGO reply will contain no165* underlying mech token; therefore, the encoding of the correct166* NegotiationToken response is completely predictable:167*168* A1 14 (choice 1, length 20, meaning negTokenResp)169* 30 12 (sequence, length 18)170* A0 03 (context tag 0, length 3)171* 0A 01 00 (enumerated value 0, meaning accept-completed)172* A1 0B (context tag 1, length 11)173* 06 09 (object identifier, length 9)174* 2A 86 48 82 F7 12 01 02 02 (the erroneous krb5 OID)175*176* So we can just compare the length to 22 and the nine bytes at offset 13177* to the expected OID.178*/179major = gss_accept_sec_context(&minor, &actx, acred, &stok,180GSS_C_NO_CHANNEL_BINDINGS, NULL,181NULL, &atok, NULL, NULL, NULL);182check_gsserr("gss_accept_sec_context(mskrb)", major, minor);183assert(atok.length == 22);184atok_oid = (unsigned char *)atok.value + 13;185assert(memcmp(atok_oid, mech_krb5_wrong.elements, 9) == 0);186187(void)gss_delete_sec_context(&minor, &ictx, NULL);188(void)gss_delete_sec_context(&minor, &actx, NULL);189(void)gss_release_buffer(&minor, &ktok);190(void)gss_release_buffer(&minor, &atok);191free(stok.value);192}193194/* Check that we return a compatibility NegTokenInit2 message containing195* NegHints for an empty initiator token. */196static void197test_neghints(void)198{199OM_uint32 major, minor;200gss_buffer_desc itok = GSS_C_EMPTY_BUFFER, atok;201gss_ctx_id_t actx = GSS_C_NO_CONTEXT;202const char *expected =203/* RFC 2743 token framing: [APPLICATION 0] IMPLICIT SEQUENCE followed204* by OBJECT IDENTIFIER and the SPNEGO OID */205"\x60\x47\x06\x06" "\x2B\x06\x01\x05\x05\x02"206/* [0] SEQUENCE for the NegotiationToken negtokenInit choice */207"\xA0\x3D\x30\x3B"208/* [0] MechTypeList containing the krb5 OID */209"\xA0\x0D\x30\x0B\x06\x09" "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"210/* [3] NegHints containing [0] GeneralString containing the dummy211* hintName string defined in [MS-SPNG] */212"\xA3\x2A\x30\x28\xA0\x26\x1B\x24"213"not_defined_in_RFC4178@please_ignore";214215/* Produce a hint token. */216major = gss_accept_sec_context(&minor, &actx, GSS_C_NO_CREDENTIAL, &itok,217GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,218&atok, NULL, NULL, NULL);219check_gsserr("gss_accept_sec_context(neghints)", major, minor);220221/* Verify it against the expected contents, which are fixed as long as we222* only list the krb5 mech in the token. */223assert(atok.length == strlen(expected));224assert(memcmp(atok.value, expected, atok.length) == 0);225226(void)gss_release_buffer(&minor, &atok);227(void)gss_delete_sec_context(&minor, &actx, NULL);228}229230int231main(int argc, char *argv[])232{233OM_uint32 minor, major, flags;234gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL;235gss_cred_id_t initiator_cred_handle = GSS_C_NO_CREDENTIAL;236gss_OID_set actual_mechs = GSS_C_NO_OID_SET;237gss_ctx_id_t initiator_context, acceptor_context;238gss_name_t target_name, source_name = GSS_C_NO_NAME;239gss_OID mech = GSS_C_NO_OID;240gss_OID_desc pref_oids[2];241gss_OID_set_desc pref_mechs;242243if (argc < 2 || argc > 3) {244fprintf(stderr, "Usage: %s target_name [keytab]\n", argv[0]);245exit(1);246}247248target_name = import_name(argv[1]);249250if (argc >= 3) {251major = krb5_gss_register_acceptor_identity(argv[2]);252check_gsserr("krb5_gss_register_acceptor_identity", major, 0);253}254255/* Get default initiator cred. */256major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,257&mechset_spnego, GSS_C_INITIATE,258&initiator_cred_handle, NULL, NULL);259check_gsserr("gss_acquire_cred(initiator)", major, minor);260261/*262* The following test is designed to exercise SPNEGO reselection on the263* client and server. Unfortunately, it no longer does so after tickets264* #8217 and #8021, since SPNEGO now only acquires a single krb5 cred and265* there is no way to expand the underlying creds with gss_set_neg_mechs().266* To fix this we need gss_acquire_cred_with_cred() or some other way to267* turn a cred with a specifically requested mech set into a SPNEGO cred.268*/269270/* Make the initiator prefer IAKERB and offer krb5 as an alternative. */271pref_oids[0] = mech_iakerb;272pref_oids[1] = mech_krb5;273pref_mechs.count = 2;274pref_mechs.elements = pref_oids;275major = gss_set_neg_mechs(&minor, initiator_cred_handle, &pref_mechs);276check_gsserr("gss_set_neg_mechs(initiator)", major, minor);277278/* Get default acceptor cred. */279major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,280&mechset_spnego, GSS_C_ACCEPT,281&verifier_cred_handle, &actual_mechs, NULL);282check_gsserr("gss_acquire_cred(acceptor)", major, minor);283284/* Restrict the acceptor to krb5 (which will force a reselection). */285major = gss_set_neg_mechs(&minor, verifier_cred_handle, &mechset_krb5);286check_gsserr("gss_set_neg_mechs(acceptor)", major, minor);287288flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;289establish_contexts(&mech_spnego, initiator_cred_handle,290verifier_cred_handle, target_name, flags,291&initiator_context, &acceptor_context, &source_name,292&mech, NULL);293294display_canon_name("Source name", source_name, &mech_krb5);295display_oid("Source mech", mech);296297/* Test acceptance of the erroneous Microsoft krb5 OID, with and without an298* acceptor cred. */299test_mskrb_oid(target_name, verifier_cred_handle);300test_mskrb_oid(target_name, GSS_C_NO_CREDENTIAL);301302test_neghints();303304(void)gss_delete_sec_context(&minor, &initiator_context, NULL);305(void)gss_delete_sec_context(&minor, &acceptor_context, NULL);306(void)gss_release_name(&minor, &source_name);307(void)gss_release_name(&minor, &target_name);308(void)gss_release_cred(&minor, &initiator_cred_handle);309(void)gss_release_cred(&minor, &verifier_cred_handle);310(void)gss_release_oid_set(&minor, &actual_mechs);311312return 0;313}314315316