Implement the GSS-API credential store API extensions defined by MIT here: https://k5wiki.kerberos.org/wiki/Projects/Credential_Store_extensions Note: we kill off gss_acquire_cred_ext() here. This was never a public API, although mechanisms could have implemented it and I briefly used it in my BrowserID prototype mechanism. gss_acquire_cred_ext_from() occupies the place in the dispatch table where gss_acquire_cred_ext() used to, but this structure was never visible outside Heimdal (i.e. it is only used by internal mechanisms); (Mechanisms that need to accept arbitrary key/value dictionaries from applications should now implement gss_acquire_cred_from().)
		
			
				
	
	
		
			1207 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1207 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 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 */ }
 | 
						|
};
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
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 unknown", 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[4];
 | 
						|
    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;
 | 
						|
}
 |