Files
heimdal/lib/gssapi/test_context.c
Luke Howard 4fb6a6adc9 gss: port NegoEx implementation from MIT
An implementation of draft-zhu-negoex-04 for MIT Kerberos was developed in
2011. This has been recently integrated, with many fixes from Greg Hudson. This
commit ports it to Heimdal. The implementation has been interoperability tested
with MIT Kerberos and Windows, using the GSS EAP mechanism developed as part of
the Moonshot project.

The SPNEGO code was also updated to import the state machine from Apple which
improves mechListMIC processing and avoids discarding initial context tokens
generated during mechanism probing, that can be used for optimistic tokens.

Finally, to aid in testing, the GSS-API mechanism glue configuration file can
be changed using the environment variable GSS_MECH_CONFIG. This environment
variable name, along with the format of the configuration file, is compatible
with MIT (although it would be difficult for a single mechanism binary to
support both implementations).
2020-02-04 17:28:35 +11:00

1214 lines
37 KiB
C

/*
* Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of KTH nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "krb5/gsskrb5_locl.h"
#include <err.h>
#include <getarg.h>
#include <gssapi.h>
#include <gssapi_krb5.h>
#include <gssapi_spnego.h>
#include <gssapi_ntlm.h>
#include "test_common.h"
static char *type_string;
static char *mech_string;
static char *mechs_string;
static char *ret_mech_string;
static char *client_name;
static char *client_password;
static int dns_canon_flag = -1;
static int mutual_auth_flag = 0;
static int dce_style_flag = 0;
static int wrapunwrap_flag = 0;
static int iov_flag = 0;
static int aead_flag = 0;
static int getverifymic_flag = 0;
static int deleg_flag = 0;
static int policy_deleg_flag = 0;
static int server_no_deleg_flag = 0;
static int ei_flag = 0;
static char *client_ccache = NULL;
static char *client_keytab = NULL;
static char *gsskrb5_acceptor_identity = NULL;
static char *session_enctype_string = NULL;
static int client_time_offset = 0;
static int server_time_offset = 0;
static int max_loops = 0;
static char *limit_enctype_string = NULL;
static int version_flag = 0;
static int verbose_flag = 0;
static int help_flag = 0;
static krb5_context context;
static krb5_enctype limit_enctype = 0;
static gss_OID_desc test_negoex_1_mech = { 6, "\x69\x85\xa2\xc0\xac\x66" };
static gss_OID_desc test_negoex_2_mech = { 6, "\x69\x84\xb0\xd1\xa8\x2c" };
static struct {
const char *name;
gss_OID oid;
} o2n[] = {
{ "krb5", NULL /* GSS_KRB5_MECHANISM */ },
{ "spnego", NULL /* GSS_SPNEGO_MECHANISM */ },
{ "ntlm", NULL /* GSS_NTLM_MECHANISM */ },
{ "sasl-digest-md5", NULL /* GSS_SASL_DIGEST_MD5_MECHANISM */ },
{ "test_negoex_1", NULL },
{ "test_negoex_2", NULL },
};
static void
init_o2n(void)
{
o2n[0].oid = GSS_KRB5_MECHANISM;
o2n[1].oid = GSS_SPNEGO_MECHANISM;
o2n[2].oid = GSS_NTLM_MECHANISM;
o2n[3].oid = GSS_SASL_DIGEST_MD5_MECHANISM;
o2n[4].oid = &test_negoex_1_mech;
o2n[5].oid = &test_negoex_2_mech;
}
static gss_OID
string_to_oid(const char *name)
{
size_t i;
for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
if (strcasecmp(name, o2n[i].name) == 0)
return o2n[i].oid;
errx(1, "name '%s' not known", name);
}
static void
string_to_oids(gss_OID_set *oidsetp, gss_OID_set oidset,
gss_OID_desc *oidarray, size_t oidarray_len,
char *names)
{
char *name;
char *s;
if (names[0] == '\0') {
*oidsetp = GSS_C_NO_OID_SET;
return;
}
oidset->elements = &oidarray[0];
if (strcasecmp(names, "all") == 0) {
if (sizeof(o2n)/sizeof(o2n[0]) > oidarray_len)
errx(1, "internal error: oidarray must be enlarged");
for (oidset->count = 0; oidset->count < oidarray_len; oidset->count++)
oidset->elements[oidset->count] = *o2n[oidset->count].oid;
} else {
for (oidset->count = 0, name = strtok_r(names, ", ", &s);
name != NULL;
oidset->count++, name = strtok_r(NULL, ", ", &s)) {
if (oidset->count >= oidarray_len)
errx(1, "too many mech names given");
oidset->elements[oidset->count] = *string_to_oid(name);
}
}
*oidsetp = oidset;
}
static const char *
oid_to_string(const gss_OID oid)
{
size_t i;
for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
if (gss_oid_equal(oid, o2n[i].oid))
return o2n[i].name;
return "unknown oid";
}
static void
loop(gss_OID mechoid,
gss_OID nameoid, const char *target,
gss_cred_id_t init_cred,
gss_ctx_id_t *sctx, gss_ctx_id_t *cctx,
gss_OID *actual_mech,
gss_cred_id_t *deleg_cred)
{
int server_done = 0, client_done = 0;
int num_loops = 0;
OM_uint32 maj_stat, min_stat;
gss_name_t gss_target_name;
gss_buffer_desc input_token, output_token;
OM_uint32 flags = 0, ret_cflags, ret_sflags;
gss_OID actual_mech_client;
gss_OID actual_mech_server;
*actual_mech = GSS_C_NO_OID;
flags |= GSS_C_INTEG_FLAG;
flags |= GSS_C_CONF_FLAG;
if (mutual_auth_flag)
flags |= GSS_C_MUTUAL_FLAG;
if (dce_style_flag)
flags |= GSS_C_DCE_STYLE;
if (deleg_flag)
flags |= GSS_C_DELEG_FLAG;
if (policy_deleg_flag)
flags |= GSS_C_DELEG_POLICY_FLAG;
input_token.value = rk_UNCONST(target);
input_token.length = strlen(target);
maj_stat = gss_import_name(&min_stat,
&input_token,
nameoid,
&gss_target_name);
if (GSS_ERROR(maj_stat))
err(1, "import name creds failed with: %d", maj_stat);
input_token.length = 0;
input_token.value = NULL;
while (!server_done || !client_done) {
num_loops++;
gsskrb5_set_time_offset(client_time_offset);
maj_stat = gss_init_sec_context(&min_stat,
init_cred,
cctx,
gss_target_name,
mechoid,
flags,
0,
NULL,
&input_token,
&actual_mech_client,
&output_token,
&ret_cflags,
NULL);
if (GSS_ERROR(maj_stat))
errx(1, "init_sec_context: %s",
gssapi_err(maj_stat, min_stat, mechoid));
if (maj_stat & GSS_S_CONTINUE_NEEDED)
;
else
client_done = 1;
gsskrb5_get_time_offset(&client_time_offset);
if (client_done && server_done)
break;
if (input_token.length != 0)
gss_release_buffer(&min_stat, &input_token);
gsskrb5_set_time_offset(server_time_offset);
maj_stat = gss_accept_sec_context(&min_stat,
sctx,
GSS_C_NO_CREDENTIAL,
&output_token,
GSS_C_NO_CHANNEL_BINDINGS,
NULL,
&actual_mech_server,
&input_token,
&ret_sflags,
NULL,
deleg_cred);
if (GSS_ERROR(maj_stat))
errx(1, "accept_sec_context: %s",
gssapi_err(maj_stat, min_stat, actual_mech_server));
gsskrb5_get_time_offset(&server_time_offset);
if (output_token.length != 0)
gss_release_buffer(&min_stat, &output_token);
if (maj_stat & GSS_S_CONTINUE_NEEDED)
;
else
server_done = 1;
}
if (output_token.length != 0)
gss_release_buffer(&min_stat, &output_token);
if (input_token.length != 0)
gss_release_buffer(&min_stat, &input_token);
gss_release_name(&min_stat, &gss_target_name);
if (deleg_flag || policy_deleg_flag) {
if (server_no_deleg_flag) {
if (*deleg_cred != GSS_C_NO_CREDENTIAL)
errx(1, "got delegated cred but didn't expect one");
} else if (*deleg_cred == GSS_C_NO_CREDENTIAL)
errx(1, "asked for delegarated cred but did get one");
} else if (*deleg_cred != GSS_C_NO_CREDENTIAL)
errx(1, "got deleg_cred cred but didn't ask");
if (gss_oid_equal(actual_mech_server, actual_mech_client) == 0)
errx(1, "mech mismatch");
*actual_mech = actual_mech_server;
if (max_loops && num_loops > max_loops)
errx(1, "num loops %d was lager then max loops %d",
num_loops, max_loops);
if (verbose_flag) {
printf("server time offset: %d\n", server_time_offset);
printf("client time offset: %d\n", client_time_offset);
printf("num loops %d\n", num_loops);
}
}
static void
wrapunwrap(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
{
gss_buffer_desc input_token, output_token, output_token2;
OM_uint32 min_stat, maj_stat;
gss_qop_t qop_state;
int conf_state;
input_token.value = "foo";
input_token.length = 3;
maj_stat = gss_wrap(&min_stat, cctx, flags, 0, &input_token,
&conf_state, &output_token);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_wrap failed: %s",
gssapi_err(maj_stat, min_stat, mechoid));
maj_stat = gss_unwrap(&min_stat, sctx, &output_token,
&output_token2, &conf_state, &qop_state);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_unwrap failed: %s",
gssapi_err(maj_stat, min_stat, mechoid));
gss_release_buffer(&min_stat, &output_token);
gss_release_buffer(&min_stat, &output_token2);
#if 0 /* doesn't work for NTLM yet */
if (!!conf_state != !!flags)
errx(1, "conf_state mismatch");
#endif
}
#define USE_CONF 1
#define USE_HEADER_ONLY 2
#define USE_SIGN_ONLY 4
#define FORCE_IOV 8
/* NO_DATA comes from <netdb.h>; we don't use it here; we appropriate it */
#ifdef NO_DATA
#undef NO_DATA
#endif
#define NO_DATA 16
static void
wrapunwrap_iov(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
{
krb5_data token, header, trailer;
OM_uint32 min_stat, maj_stat;
gss_qop_t qop_state;
int conf_state, conf_state2;
gss_iov_buffer_desc iov[6];
unsigned char *p;
int iov_len;
char header_data[9] = "ABCheader";
char trailer_data[10] = "trailerXYZ";
char token_data[16] = "0123456789abcdef";
memset(&iov, 0, sizeof(iov));
if (flags & USE_SIGN_ONLY) {
header.data = header_data;
header.length = 9;
trailer.data = trailer_data;
trailer.length = 10;
} else {
header.data = NULL;
header.length = 0;
trailer.data = NULL;
trailer.length = 0;
}
token.data = token_data;
token.length = 16;
iov_len = sizeof(iov)/sizeof(iov[0]);
memset(iov, 0, sizeof(iov));
iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
if (header.length != 0) {
iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
iov[1].buffer.length = header.length;
iov[1].buffer.value = header.data;
} else {
iov[1].type = GSS_IOV_BUFFER_TYPE_EMPTY;
iov[1].buffer.length = 0;
iov[1].buffer.value = NULL;
}
iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
if (flags & NO_DATA) {
iov[2].buffer.length = 0;
} else {
iov[2].buffer.length = token.length;
}
iov[2].buffer.value = token.data;
if (trailer.length != 0) {
iov[3].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
iov[3].buffer.length = trailer.length;
iov[3].buffer.value = trailer.data;
} else {
iov[3].type = GSS_IOV_BUFFER_TYPE_EMPTY;
iov[3].buffer.length = 0;
iov[3].buffer.value = NULL;
}
if (dce_style_flag) {
iov[4].type = GSS_IOV_BUFFER_TYPE_EMPTY;
} else {
iov[4].type = GSS_IOV_BUFFER_TYPE_PADDING | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
}
iov[4].buffer.length = 0;
iov[4].buffer.value = 0;
if (dce_style_flag) {
iov[5].type = GSS_IOV_BUFFER_TYPE_EMPTY;
} else if (flags & USE_HEADER_ONLY) {
iov[5].type = GSS_IOV_BUFFER_TYPE_EMPTY;
} else {
iov[5].type = GSS_IOV_BUFFER_TYPE_TRAILER | GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE;
}
iov[5].buffer.length = 0;
iov[5].buffer.value = 0;
maj_stat = gss_wrap_iov(&min_stat, cctx, dce_style_flag || flags & USE_CONF, 0, &conf_state,
iov, iov_len);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_wrap_iov failed");
token.length =
iov[0].buffer.length +
iov[1].buffer.length +
iov[2].buffer.length +
iov[3].buffer.length +
iov[4].buffer.length +
iov[5].buffer.length;
token.data = emalloc(token.length);
p = token.data;
memcpy(p, iov[0].buffer.value, iov[0].buffer.length);
p += iov[0].buffer.length;
memcpy(p, iov[1].buffer.value, iov[1].buffer.length);
p += iov[1].buffer.length;
memcpy(p, iov[2].buffer.value, iov[2].buffer.length);
p += iov[2].buffer.length;
memcpy(p, iov[3].buffer.value, iov[3].buffer.length);
p += iov[3].buffer.length;
memcpy(p, iov[4].buffer.value, iov[4].buffer.length);
p += iov[4].buffer.length;
memcpy(p, iov[5].buffer.value, iov[5].buffer.length);
p += iov[5].buffer.length;
assert(p - ((unsigned char *)token.data) == token.length);
if ((flags & (USE_SIGN_ONLY|FORCE_IOV)) == 0) {
gss_buffer_desc input, output;
input.value = token.data;
input.length = token.length;
maj_stat = gss_unwrap(&min_stat, sctx, &input,
&output, &conf_state2, &qop_state);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_unwrap from gss_wrap_iov failed: %s",
gssapi_err(maj_stat, min_stat, mechoid));
gss_release_buffer(&min_stat, &output);
} else {
maj_stat = gss_unwrap_iov(&min_stat, sctx, &conf_state2, &qop_state,
iov, iov_len);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_unwrap_iov failed: %x %s", flags,
gssapi_err(maj_stat, min_stat, mechoid));
}
if (conf_state2 != conf_state)
errx(1, "conf state wrong for iov: %x", flags);
gss_release_iov_buffer(&min_stat, iov, iov_len);
free(token.data);
}
static void
wrapunwrap_aead(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
{
gss_buffer_desc token, assoc, message = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output;
OM_uint32 min_stat, maj_stat;
gss_qop_t qop_state;
int conf_state, conf_state2;
char assoc_data[9] = "ABCheader";
char token_data[16] = "0123456789abcdef";
if (flags & USE_SIGN_ONLY) {
assoc.value = assoc_data;
assoc.length = 9;
} else {
assoc.value = NULL;
assoc.length = 0;
}
token.value = token_data;
token.length = 16;
maj_stat = gss_wrap_aead(&min_stat, cctx, dce_style_flag || flags & USE_CONF,
GSS_C_QOP_DEFAULT, &assoc, &token,
&conf_state, &message);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_wrap_aead failed");
if ((flags & (USE_SIGN_ONLY|FORCE_IOV)) == 0) {
maj_stat = gss_unwrap(&min_stat, sctx, &message,
&output, &conf_state2, &qop_state);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_unwrap from gss_wrap_aead failed: %s",
gssapi_err(maj_stat, min_stat, mechoid));
} else {
maj_stat = gss_unwrap_aead(&min_stat, sctx, &message, &assoc,
&output, &conf_state2, &qop_state);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_unwrap_aead failed: %x %s", flags,
gssapi_err(maj_stat, min_stat, mechoid));
}
if (output.length != token.length)
errx(1, "plaintext length wrong for aead");
else if (memcmp(output.value, token.value, token.length) != 0)
errx(1, "plaintext wrong for aead");
if (conf_state2 != conf_state)
errx(1, "conf state wrong for aead: %x", flags);
gss_release_buffer(&min_stat, &message);
gss_release_buffer(&min_stat, &output);
}
static void
getverifymic(gss_ctx_id_t cctx, gss_ctx_id_t sctx, gss_OID mechoid)
{
gss_buffer_desc input_token, output_token;
OM_uint32 min_stat, maj_stat;
gss_qop_t qop_state;
input_token.value = "bar";
input_token.length = 3;
maj_stat = gss_get_mic(&min_stat, cctx, 0, &input_token,
&output_token);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_get_mic failed: %s",
gssapi_err(maj_stat, min_stat, mechoid));
maj_stat = gss_verify_mic(&min_stat, sctx, &input_token,
&output_token, &qop_state);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_verify_mic failed: %s",
gssapi_err(maj_stat, min_stat, mechoid));
gss_release_buffer(&min_stat, &output_token);
}
static void
empty_release(void)
{
gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
gss_name_t name = GSS_C_NO_NAME;
gss_OID_set oidset = GSS_C_NO_OID_SET;
OM_uint32 junk;
gss_delete_sec_context(&junk, &ctx, NULL);
gss_release_cred(&junk, &cred);
gss_release_name(&junk, &name);
gss_release_oid_set(&junk, &oidset);
}
/*
*
*/
static struct getargs args[] = {
{"name-type",0, arg_string, &type_string, "type of name", NULL },
{"mech-type",0, arg_string, &mech_string, "mech type (name)", NULL },
{"mech-types",0, arg_string, &mechs_string, "mech types (names)", NULL },
{"ret-mech-type",0, arg_string, &ret_mech_string,
"type of return mech", NULL },
{"dns-canonicalize",0,arg_negative_flag, &dns_canon_flag,
"use dns to canonicalize", NULL },
{"mutual-auth",0, arg_flag, &mutual_auth_flag,"mutual auth", NULL },
{"client-ccache",0, arg_string, &client_ccache, "client credentials cache", NULL },
{"client-keytab",0, arg_string, &client_keytab, "client keytab", NULL },
{"client-name", 0, arg_string, &client_name, "client name", NULL },
{"client-password", 0, arg_string, &client_password, "client password", NULL },
{"limit-enctype",0, arg_string, &limit_enctype_string, "enctype", NULL },
{"dce-style",0, arg_flag, &dce_style_flag, "dce-style", NULL },
{"wrapunwrap",0, arg_flag, &wrapunwrap_flag, "wrap/unwrap", NULL },
{"iov", 0, arg_flag, &iov_flag, "wrap/unwrap iov", NULL },
{"aead", 0, arg_flag, &aead_flag, "wrap/unwrap aead", NULL },
{"getverifymic",0, arg_flag, &getverifymic_flag,
"get and verify mic", NULL },
{"delegate",0, arg_flag, &deleg_flag, "delegate credential", NULL },
{"policy-delegate",0, arg_flag, &policy_deleg_flag, "policy delegate credential", NULL },
{"server-no-delegate",0, arg_flag, &server_no_deleg_flag,
"server should get a credential", NULL },
{"export-import-cred",0, arg_flag, &ei_flag, "test export/import cred", NULL },
{"gsskrb5-acceptor-identity", 0, arg_string, &gsskrb5_acceptor_identity, "keytab", NULL },
{"session-enctype", 0, arg_string, &session_enctype_string, "enctype", NULL },
{"client-time-offset", 0, arg_integer, &client_time_offset, "time", NULL },
{"server-time-offset", 0, arg_integer, &server_time_offset, "time", NULL },
{"max-loops", 0, arg_integer, &max_loops, "time", NULL },
{"version", 0, arg_flag, &version_flag, "print version", NULL },
{"verbose", 'v', arg_flag, &verbose_flag, "verbose", NULL },
{"help", 0, arg_flag, &help_flag, NULL, NULL }
};
static void
usage (int ret)
{
arg_printusage (args, sizeof(args)/sizeof(*args),
NULL, "service@host");
exit (ret);
}
int
main(int argc, char **argv)
{
int optidx = 0;
OM_uint32 min_stat, maj_stat;
gss_ctx_id_t cctx, sctx;
void *ctx;
gss_OID nameoid, mechoid, actual_mech, actual_mech2;
gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL, deleg_cred = GSS_C_NO_CREDENTIAL;
gss_name_t cname = GSS_C_NO_NAME;
gss_buffer_desc credential_data = GSS_C_EMPTY_BUFFER;
gss_OID_desc oids[6];
gss_OID_set_desc mechoid_descs;
gss_OID_set mechoids = GSS_C_NO_OID_SET;
gss_key_value_element_desc client_cred_elements[2];
gss_key_value_set_desc client_cred_store;
setprogname(argv[0]);
init_o2n();
if (krb5_init_context(&context))
errx(1, "krb5_init_context");
cctx = sctx = GSS_C_NO_CONTEXT;
if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
usage(1);
if (help_flag)
usage (0);
if(version_flag){
print_version(NULL);
exit(0);
}
argc -= optidx;
argv += optidx;
if (argc != 1)
usage(1);
if (dns_canon_flag != -1)
gsskrb5_set_dns_canonicalize(dns_canon_flag);
if (type_string == NULL)
nameoid = GSS_C_NT_HOSTBASED_SERVICE;
else if (strcmp(type_string, "hostbased-service") == 0)
nameoid = GSS_C_NT_HOSTBASED_SERVICE;
else if (strcmp(type_string, "krb5-principal-name") == 0)
nameoid = GSS_KRB5_NT_PRINCIPAL_NAME;
else
errx(1, "%s not supported", type_string);
if (mech_string == NULL)
mechoid = GSS_KRB5_MECHANISM;
else
mechoid = string_to_oid(mech_string);
if (mechs_string == NULL) {
/*
* We ought to be able to use the OID set of the one mechanism
* OID given. But there's some breakage that conspires to make
* that fail though it should succeed:
*
* - the NTLM gss_acquire_cred() refuses to work with
* desired_name == GSS_C_NO_NAME
* - gss_acquire_cred() with desired_mechs == GSS_C_NO_OID_SET
* does work here because we happen to have Kerberos
* credentials in check-ntlm, and the subsequent
* gss_init_sec_context() call finds no cred element for NTLM
* but plows on anyways, surprisingly enough, and then the
* NTLM gss_init_sec_context() just works.
*
* In summary, there's some breakage in gss_init_sec_context()
* and some breakage in NTLM that conspires against us here.
*
* We work around this in check-ntlm and check-spnego by adding
* --client-name=user1@${R} to the invocations of this test
* program that require it.
*/
oids[0] = *mechoid;
mechoid_descs.elements = &oids[0];
mechoid_descs.count = 1;
mechoids = &mechoid_descs;
} else {
string_to_oids(&mechoids, &mechoid_descs,
oids, sizeof(oids)/sizeof(oids[0]), mechs_string);
}
if (gsskrb5_acceptor_identity) {
/* XXX replace this with cred store, but test suites will need work */
maj_stat = gsskrb5_register_acceptor_identity(gsskrb5_acceptor_identity);
if (maj_stat)
errx(1, "gsskrb5_acceptor_identity: %s",
gssapi_err(maj_stat, 0, GSS_C_NO_OID));
}
if (client_password && (client_ccache || client_keytab)) {
errx(1, "password option mutually exclusive with ccache or keytab option");
}
if (client_password) {
credential_data.value = client_password;
credential_data.length = strlen(client_password);
}
client_cred_store.count = 0;
client_cred_store.elements = client_cred_elements;
if (client_ccache) {
client_cred_store.elements[client_cred_store.count].key = "ccache";
client_cred_store.elements[client_cred_store.count].value = client_ccache;
client_cred_store.count++;
}
if (client_keytab) {
client_cred_store.elements[client_cred_store.count].key = "client_keytab";
client_cred_store.elements[client_cred_store.count].value = client_keytab;
client_cred_store.count++;
}
if (client_name) {
gss_buffer_desc cn;
cn.value = client_name;
cn.length = strlen(client_name);
maj_stat = gss_import_name(&min_stat, &cn, GSS_C_NT_USER_NAME, &cname);
if (maj_stat)
errx(1, "gss_import_name: %s",
gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
}
if (client_password) {
maj_stat = gss_acquire_cred_with_password(&min_stat,
cname,
&credential_data,
GSS_C_INDEFINITE,
mechoids,
GSS_C_INITIATE,
&client_cred,
NULL,
NULL);
if (GSS_ERROR(maj_stat)) {
if (mechoids != GSS_C_NO_OID_SET && mechoids->count == 1)
mechoid = &mechoids->elements[0];
else
mechoid = GSS_C_NO_OID;
errx(1, "gss_acquire_cred_with_password: %s",
gssapi_err(maj_stat, min_stat, mechoid));
}
} else {
maj_stat = gss_acquire_cred_from(&min_stat,
cname,
GSS_C_INDEFINITE,
mechoids,
GSS_C_INITIATE,
client_cred_store.count ? &client_cred_store
: GSS_C_NO_CRED_STORE,
&client_cred,
NULL,
NULL);
if (GSS_ERROR(maj_stat))
errx(1, "gss_acquire_cred: %s",
gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
}
if (limit_enctype_string) {
krb5_error_code ret;
ret = krb5_string_to_enctype(context,
limit_enctype_string,
&limit_enctype);
if (ret)
krb5_err(context, 1, ret, "krb5_string_to_enctype");
}
if (limit_enctype) {
if (client_cred == NULL)
errx(1, "client_cred missing");
maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, client_cred,
1, &limit_enctype);
if (maj_stat)
errx(1, "gss_krb5_set_allowable_enctypes: %s",
gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
}
loop(mechoid, nameoid, argv[0], client_cred,
&sctx, &cctx, &actual_mech, &deleg_cred);
if (verbose_flag)
printf("resulting mech: %s\n", oid_to_string(actual_mech));
if (ret_mech_string) {
gss_OID retoid;
retoid = string_to_oid(ret_mech_string);
if (gss_oid_equal(retoid, actual_mech) == 0)
errx(1, "actual_mech mech is not the expected type %s",
ret_mech_string);
}
/* XXX should be actual_mech */
if (gss_oid_equal(mechoid, GSS_KRB5_MECHANISM)) {
time_t sc_time;
gss_buffer_desc authz_data;
gss_buffer_desc in, out1, out2;
krb5_keyblock *keyblock, *keyblock2;
krb5_timestamp now;
krb5_error_code ret;
ret = krb5_timeofday(context, &now);
if (ret)
errx(1, "krb5_timeofday failed");
/* client */
maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
&cctx,
1, /* version */
&ctx);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_krb5_export_lucid_sec_context failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
maj_stat = gss_krb5_free_lucid_sec_context(&maj_stat, ctx);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_krb5_free_lucid_sec_context failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
/* server */
maj_stat = gss_krb5_export_lucid_sec_context(&min_stat,
&sctx,
1, /* version */
&ctx);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_krb5_export_lucid_sec_context failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
maj_stat = gss_krb5_free_lucid_sec_context(&min_stat, ctx);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gss_krb5_free_lucid_sec_context failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
sctx,
&sc_time);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gsskrb5_extract_authtime_from_sec_context failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
if (sc_time > now)
errx(1, "gsskrb5_extract_authtime_from_sec_context failed: "
"time authtime is before now: %ld %ld",
(long)sc_time, (long)now);
maj_stat = gsskrb5_extract_service_keyblock(&min_stat,
sctx,
&keyblock);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gsskrb5_export_service_keyblock failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
krb5_free_keyblock(context, keyblock);
maj_stat = gsskrb5_get_subkey(&min_stat,
sctx,
&keyblock);
if (maj_stat != GSS_S_COMPLETE
&& (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
errx(1, "gsskrb5_get_subkey server failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
if (maj_stat != GSS_S_COMPLETE)
keyblock = NULL;
else if (limit_enctype && keyblock->keytype != limit_enctype)
errx(1, "gsskrb5_get_subkey wrong enctype");
maj_stat = gsskrb5_get_subkey(&min_stat,
cctx,
&keyblock2);
if (maj_stat != GSS_S_COMPLETE
&& (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
errx(1, "gsskrb5_get_subkey client failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
if (maj_stat != GSS_S_COMPLETE)
keyblock2 = NULL;
else if (limit_enctype && keyblock->keytype != limit_enctype)
errx(1, "gsskrb5_get_subkey wrong enctype");
if (keyblock || keyblock2) {
if (keyblock == NULL)
errx(1, "server missing token keyblock");
if (keyblock2 == NULL)
errx(1, "client missing token keyblock");
if (keyblock->keytype != keyblock2->keytype)
errx(1, "enctype mismatch");
if (keyblock->keyvalue.length != keyblock2->keyvalue.length)
errx(1, "key length mismatch");
if (memcmp(keyblock->keyvalue.data, keyblock2->keyvalue.data,
keyblock2->keyvalue.length) != 0)
errx(1, "key data mismatch");
}
if (session_enctype_string) {
krb5_enctype enctype;
ret = krb5_string_to_enctype(context,
session_enctype_string,
&enctype);
if (ret)
krb5_err(context, 1, ret, "krb5_string_to_enctype");
if (enctype != keyblock->keytype)
errx(1, "keytype is not the expected %d != %d",
(int)enctype, (int)keyblock2->keytype);
}
if (keyblock)
krb5_free_keyblock(context, keyblock);
if (keyblock2)
krb5_free_keyblock(context, keyblock2);
maj_stat = gsskrb5_get_initiator_subkey(&min_stat,
sctx,
&keyblock);
if (maj_stat != GSS_S_COMPLETE
&& (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY)))
errx(1, "gsskrb5_get_initiator_subkey failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
if (maj_stat == GSS_S_COMPLETE) {
if (limit_enctype && keyblock->keytype != limit_enctype)
errx(1, "gsskrb5_get_initiator_subkey wrong enctype");
krb5_free_keyblock(context, keyblock);
}
maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat,
sctx,
128,
&authz_data);
if (maj_stat == GSS_S_COMPLETE)
gss_release_buffer(&min_stat, &authz_data);
memset(&out1, 0, sizeof(out1));
memset(&out2, 0, sizeof(out2));
in.value = "foo";
in.length = 3;
gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in,
100, &out1);
gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_FULL, &in,
100, &out2);
if (out1.length != out2.length)
errx(1, "prf len mismatch");
if (memcmp(out1.value, out2.value, out1.length) != 0)
errx(1, "prf data mismatch");
gss_release_buffer(&min_stat, &out1);
gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in,
100, &out1);
if (out1.length != out2.length)
errx(1, "prf len mismatch");
if (memcmp(out1.value, out2.value, out1.length) != 0)
errx(1, "prf data mismatch");
gss_release_buffer(&min_stat, &out1);
gss_release_buffer(&min_stat, &out2);
in.value = "bar";
in.length = 3;
gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_PARTIAL, &in,
100, &out1);
gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_PARTIAL, &in,
100, &out2);
if (out1.length != out2.length)
errx(1, "prf len mismatch");
if (memcmp(out1.value, out2.value, out1.length) != 0)
errx(1, "prf data mismatch");
gss_release_buffer(&min_stat, &out1);
gss_release_buffer(&min_stat, &out2);
wrapunwrap_flag = 1;
getverifymic_flag = 1;
}
if (wrapunwrap_flag) {
wrapunwrap(cctx, sctx, 0, actual_mech);
wrapunwrap(cctx, sctx, 1, actual_mech);
wrapunwrap(sctx, cctx, 0, actual_mech);
wrapunwrap(sctx, cctx, 1, actual_mech);
}
if (iov_flag) {
wrapunwrap_iov(cctx, sctx, 0, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
/* works */
wrapunwrap_iov(cctx, sctx, 0, actual_mech);
wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
/* works */
wrapunwrap_iov(cctx, sctx, NO_DATA, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_SIGN_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_SIGN_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY, actual_mech);
wrapunwrap_iov(cctx, sctx, NO_DATA|USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
}
if (aead_flag) {
wrapunwrap_aead(cctx, sctx, 0, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_CONF, actual_mech);
wrapunwrap_aead(cctx, sctx, FORCE_IOV, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_aead(cctx, sctx, 0, actual_mech);
wrapunwrap_aead(cctx, sctx, FORCE_IOV, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_CONF, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech);
wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
}
if (getverifymic_flag) {
getverifymic(cctx, sctx, actual_mech);
getverifymic(cctx, sctx, actual_mech);
getverifymic(sctx, cctx, actual_mech);
getverifymic(sctx, cctx, actual_mech);
}
gss_delete_sec_context(&min_stat, &cctx, NULL);
gss_delete_sec_context(&min_stat, &sctx, NULL);
if (deleg_cred != GSS_C_NO_CREDENTIAL) {
gss_cred_id_t cred2 = GSS_C_NO_CREDENTIAL;
gss_buffer_desc cb;
if (verbose_flag)
printf("checking actual mech (%s) on delegated cred\n",
oid_to_string(actual_mech));
loop(actual_mech, nameoid, argv[0], deleg_cred, &sctx, &cctx, &actual_mech2, &cred2);
gss_delete_sec_context(&min_stat, &cctx, NULL);
gss_delete_sec_context(&min_stat, &sctx, NULL);
gss_release_cred(&min_stat, &cred2);
#if 0
/*
* XXX We can't do this. Delegated credentials only work with
* the actual_mech. We could gss_store_cred the delegated
* credentials *then* gss_add/acquire_cred() with SPNEGO, then
* we could try loop() with those credentials.
*/
/* try again using SPNEGO */
if (verbose_flag)
printf("checking spnego on delegated cred\n");
loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], deleg_cred, &sctx, &cctx,
&actual_mech2, &cred2);
gss_delete_sec_context(&min_stat, &cctx, NULL);
gss_delete_sec_context(&min_stat, &sctx, NULL);
gss_release_cred(&min_stat, &cred2);
#endif
/* check export/import */
if (ei_flag) {
maj_stat = gss_export_cred(&min_stat, deleg_cred, &cb);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "export failed: %s",
gssapi_err(maj_stat, min_stat, NULL));
maj_stat = gss_import_cred(&min_stat, &cb, &cred2);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "import failed: %s",
gssapi_err(maj_stat, min_stat, NULL));
gss_release_buffer(&min_stat, &cb);
gss_release_cred(&min_stat, &deleg_cred);
if (verbose_flag)
printf("checking actual mech (%s) on export/imported cred\n",
oid_to_string(actual_mech));
loop(actual_mech, nameoid, argv[0], cred2, &sctx, &cctx,
&actual_mech2, &deleg_cred);
gss_release_cred(&min_stat, &deleg_cred);
gss_delete_sec_context(&min_stat, &cctx, NULL);
gss_delete_sec_context(&min_stat, &sctx, NULL);
#if 0
/* XXX See above */
/* try again using SPNEGO */
if (verbose_flag)
printf("checking SPNEGO on export/imported cred\n");
loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], cred2, &sctx, &cctx,
&actual_mech2, &deleg_cred);
gss_release_cred(&min_stat, &deleg_cred);
gss_delete_sec_context(&min_stat, &cctx, NULL);
gss_delete_sec_context(&min_stat, &sctx, NULL);
#endif
gss_release_cred(&min_stat, &cred2);
} else {
gss_release_cred(&min_stat, &deleg_cred);
}
}
empty_release();
krb5_free_context(context);
return 0;
}