예제 #1
0
int
main(int argc, char **argv)
{
    int optind = 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;

    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, &optind))
	usage(1);

    if (help_flag)
	usage (0);

    if(version_flag){
	print_version(NULL);
	exit(0);
    }

    argc -= optind;
    argv += optind;

    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 suppported", type_string);

    if (mech_string == NULL)
	mechoid = GSS_KRB5_MECHANISM;
    else
	mechoid = string_to_oid(mech_string);

    if (gsskrb5_acceptor_identity) {
	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) {
	credential_data.value = client_password;
	credential_data.length = strlen(client_password);
    }

    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,
						  GSS_C_NO_OID_SET,
						  GSS_C_INITIATE,
						  &client_cred,
						  NULL,
						  NULL);
	if (GSS_ERROR(maj_stat))
	    errx(1, "gss_acquire_cred_with_password: %s",
		 gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
    } else {
	maj_stat = gss_acquire_cred(&min_stat,
				    cname,
				    GSS_C_INDEFINITE,
				    GSS_C_NO_OID_SET,
				    GSS_C_INITIATE,
				    &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 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,
							     &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 (time > now)
	    errx(1, "gsskrb5_extract_authtime_from_sec_context failed: "
		 "time authtime is before now: %ld %ld",
		 (long)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);
    }

    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);

	/* 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);

	/* 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);

	    /* 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);

	    gss_release_cred(&min_stat, &cred2);

	} else  {
	    gss_release_cred(&min_stat, &deleg_cred);
	}

    }

    empty_release();

    krb5_free_context(context);

    return 0;
}
예제 #2
0
파일: gssapi_pac.c 프로젝트: encukou/samba
NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx,
				gss_ctx_id_t gssapi_context,
				gss_name_t gss_client_name,
				DATA_BLOB *pac_blob)
{
	NTSTATUS status;
	OM_uint32 gss_maj, gss_min;
#ifdef HAVE_GSS_GET_NAME_ATTRIBUTE
/*
 * gss_get_name_attribute() in MIT krb5 1.10.0 can return unintialized pac_display_buffer
 * and later gss_release_buffer() will crash on attempting to release it.
 *
 * So always initialize the buffer descriptors.
 *
 * See following links for more details:
 * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=658514
 * http://krbdev.mit.edu/rt/Ticket/Display.html?user=guest&pass=guest&id=7087
 */
	gss_buffer_desc pac_buffer = {
		.value = NULL,
		.length = 0
	};
	gss_buffer_desc pac_display_buffer = {
		.value = NULL,
		.length = 0
	};
	gss_buffer_desc pac_name = {
		.value = discard_const("urn:mspac:"),
		.length = sizeof("urn:mspac:")-1
	};
	int more = -1;
	int authenticated = false;
	int complete = false;

	gss_maj = gss_get_name_attribute(
		&gss_min, gss_client_name, &pac_name,
		&authenticated, &complete,
		&pac_buffer, &pac_display_buffer, &more);

	if (gss_maj != 0) {
		gss_OID oid = discard_const(gss_mech_krb5);
		DBG_NOTICE("obtaining PAC via GSSAPI gss_get_name_attribute "
			   "failed: %s\n", gssapi_error_string(mem_ctx,
							       gss_maj, gss_min,
							       oid));
		return NT_STATUS_ACCESS_DENIED;
	} else if (authenticated && complete) {
		/* The PAC blob is returned directly */
		*pac_blob = data_blob_talloc(mem_ctx, pac_buffer.value,
					    pac_buffer.length);

		if (!pac_blob->data) {
			status = NT_STATUS_NO_MEMORY;
		} else {
			status = NT_STATUS_OK;
		}

		gss_maj = gss_release_buffer(&gss_min, &pac_buffer);
		gss_maj = gss_release_buffer(&gss_min, &pac_display_buffer);
		return status;
	} else {
		DEBUG(0, ("obtaining PAC via GSSAPI failed: authenticated: %s, complete: %s, more: %s\n",
			  authenticated ? "true" : "false",
			  complete ? "true" : "false",
			  more ? "true" : "false"));
		return NT_STATUS_ACCESS_DENIED;
	}

#elif defined(HAVE_GSS_INQUIRE_SEC_CONTEXT_BY_OID)
	gss_OID_desc pac_data_oid = {
		.elements = discard_const(EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID),
		.length = EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH
	};

	gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;

	/* If we didn't have the routine to get a verified, validated
	 * PAC (supplied only by MIT at the time of writing), then try
	 * with the Heimdal OID (fetches the PAC directly and always
	 * validates) */
	gss_maj = gss_inquire_sec_context_by_oid(
				&gss_min, gssapi_context,
				&pac_data_oid, &set);

	/* First check for the error MIT gives for an unknown OID */
	if (gss_maj == GSS_S_UNAVAILABLE) {
		DEBUG(1, ("unable to obtain a PAC against this GSSAPI library.  "
			  "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n"));
	} else if (gss_maj != 0) {
		DEBUG(2, ("obtaining PAC via GSSAPI gss_inqiure_sec_context_by_oid (Heimdal OID) failed: %s\n",
			  gssapi_error_string(mem_ctx, gss_maj, gss_min, gss_mech_krb5)));
	} else {
		if (set == GSS_C_NO_BUFFER_SET) {
			DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown "
				  "data in results.\n"));
			return NT_STATUS_INTERNAL_ERROR;
		}

		/* The PAC blob is returned directly */
		*pac_blob = data_blob_talloc(mem_ctx, set->elements[0].value,
					    set->elements[0].length);
		if (!pac_blob->data) {
			status = NT_STATUS_NO_MEMORY;
		} else {
			status = NT_STATUS_OK;
		}

		gss_maj = gss_release_buffer_set(&gss_min, &set);
		return status;
	}
#else
	DEBUG(1, ("unable to obtain a PAC against this GSSAPI library.  "
		  "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n"));
#endif
	return NT_STATUS_ACCESS_DENIED;
}

NTSTATUS gssapi_get_session_key(TALLOC_CTX *mem_ctx,
				gss_ctx_id_t gssapi_context,
				DATA_BLOB *session_key, 
				uint32_t *keytype)
{
	OM_uint32 gss_min, gss_maj;
	gss_buffer_set_t set = GSS_C_NO_BUFFER_SET;

	gss_maj = gss_inquire_sec_context_by_oid(
				&gss_min, gssapi_context,
				&gse_sesskey_inq_oid, &set);
	if (gss_maj) {
		DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n",
			  gssapi_error_string(mem_ctx,
					      gss_maj,
					      gss_min,
					      discard_const_p(struct gss_OID_desc_struct,
							      gss_mech_krb5))));
		return NT_STATUS_NO_USER_SESSION_KEY;
	}

	if ((set == GSS_C_NO_BUFFER_SET) ||
	    (set->count == 0)) {
#ifdef HAVE_GSSKRB5_GET_SUBKEY
		krb5_keyblock *subkey;
		gss_maj = gsskrb5_get_subkey(&gss_min,
					     gssapi_context,
					     &subkey);
		if (gss_maj != 0) {
			DEBUG(1, ("NO session key for this mech\n"));
			return NT_STATUS_NO_USER_SESSION_KEY;
		}
		if (session_key) {
			*session_key = data_blob_talloc(mem_ctx,
							KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey));
		}
		if (keytype) {
			*keytype = KRB5_KEY_TYPE(subkey);
		}
		krb5_free_keyblock(NULL /* should be krb5_context */, subkey);
		return NT_STATUS_OK;
#else
		DEBUG(0, ("gss_inquire_sec_context_by_oid didn't return any session key (and no alternative method available)\n"));
		return NT_STATUS_NO_USER_SESSION_KEY;
#endif
	}

	if (session_key) {
		*session_key = data_blob_talloc(mem_ctx, set->elements[0].value,
						set->elements[0].length);
	}

	if (keytype) {
		int diflen, i;
		const uint8_t *p;

		*keytype = 0;
		if (set->count < 2) {

#ifdef HAVE_GSSKRB5_GET_SUBKEY
			krb5_keyblock *subkey;
			gss_maj = gsskrb5_get_subkey(&gss_min,
						     gssapi_context,
						     &subkey);
			if (gss_maj == 0) {
				*keytype = KRB5_KEY_TYPE(subkey);
				krb5_free_keyblock(NULL /* should be krb5_context */, subkey);
			}
#endif
			gss_maj = gss_release_buffer_set(&gss_min, &set);
	
			return NT_STATUS_OK;

		} else if (memcmp(set->elements[1].value,
				  gse_sesskeytype_oid.elements,
				  gse_sesskeytype_oid.length) != 0) {
			/* Perhaps a non-krb5 session key */
			gss_maj = gss_release_buffer_set(&gss_min, &set);
			return NT_STATUS_OK;
		}
		p = (const uint8_t *)set->elements[1].value + gse_sesskeytype_oid.length;
		diflen = set->elements[1].length - gse_sesskeytype_oid.length;
		if (diflen <= 0) {
			gss_maj = gss_release_buffer_set(&gss_min, &set);
			return NT_STATUS_INVALID_PARAMETER;
		}
		for (i = 0; i < diflen; i++) {
			*keytype = (*keytype << 7) | (p[i] & 0x7f);
			if (i + 1 != diflen && (p[i] & 0x80) == 0) {
				gss_maj = gss_release_buffer_set(&gss_min, &set);
				return NT_STATUS_INVALID_PARAMETER;
			}
		}
	}

	gss_maj = gss_release_buffer_set(&gss_min, &set);
	return NT_STATUS_OK;
}


char *gssapi_error_string(TALLOC_CTX *mem_ctx,
			  OM_uint32 maj_stat, OM_uint32 min_stat,
			  const gss_OID mech)
{
	OM_uint32 disp_min_stat, disp_maj_stat;
	gss_buffer_desc maj_error_message;
	gss_buffer_desc min_error_message;
	char *maj_error_string, *min_error_string;
	OM_uint32 msg_ctx = 0;

	char *ret;

	maj_error_message.value = NULL;
	min_error_message.value = NULL;
	maj_error_message.length = 0;
	min_error_message.length = 0;

	disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat,
					   GSS_C_GSS_CODE, mech,
					   &msg_ctx, &maj_error_message);
	if (disp_maj_stat != 0) {
		maj_error_message.value = NULL;
		maj_error_message.length = 0;
	}
	disp_maj_stat = gss_display_status(&disp_min_stat, min_stat,
					   GSS_C_MECH_CODE, mech,
					   &msg_ctx, &min_error_message);
	if (disp_maj_stat != 0) {
		min_error_message.value = NULL;
		min_error_message.length = 0;
	}

	maj_error_string = talloc_strndup(mem_ctx,
					  (char *)maj_error_message.value,
					  maj_error_message.length);

	min_error_string = talloc_strndup(mem_ctx,
					  (char *)min_error_message.value,
					  min_error_message.length);

	ret = talloc_asprintf(mem_ctx, "%s: %s",
				maj_error_string, min_error_string);

	talloc_free(maj_error_string);
	talloc_free(min_error_string);

	gss_release_buffer(&disp_min_stat, &maj_error_message);
	gss_release_buffer(&disp_min_stat, &min_error_message);

	return ret;
}