summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/appl/gss-sample/gss-client.c90
-rw-r--r--src/appl/gss-sample/gss-server.c13
-rw-r--r--src/appl/gss-sample/t_gss_sample.py46
-rw-r--r--src/include/k5-int.h28
-rw-r--r--src/include/krb5/krb5.hin1
-rw-r--r--src/lib/gssapi/generic/gssapi_ext.h27
-rw-r--r--src/lib/gssapi/krb5/Makefile.in3
-rw-r--r--src/lib/gssapi/krb5/accept_sec_context.c175
-rw-r--r--src/lib/gssapi/krb5/acquire_cred.c343
-rw-r--r--src/lib/gssapi/krb5/add_cred.c6
-rw-r--r--src/lib/gssapi/krb5/gssapiP_krb5.h140
-rw-r--r--src/lib/gssapi/krb5/gssapi_err_krb5.et1
-rw-r--r--src/lib/gssapi/krb5/gssapi_krb5.c55
-rw-r--r--src/lib/gssapi/krb5/gssapi_krb5.hin1
-rw-r--r--src/lib/gssapi/krb5/iakerb.c1046
-rw-r--r--src/lib/gssapi/krb5/init_sec_context.c184
-rw-r--r--src/lib/gssapi/krb5/inq_cred.c4
-rw-r--r--src/lib/gssapi/krb5/inq_names.c3
-rw-r--r--src/lib/gssapi/krb5/rel_cred.c5
-rw-r--r--src/lib/gssapi/krb5/ser_sctx.c1
-rw-r--r--src/lib/gssapi/libgssapi_krb5.exports2
-rw-r--r--src/lib/gssapi/mechglue/Makefile.in3
-rw-r--r--src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c544
-rw-r--r--src/lib/gssapi/mechglue/g_initialize.c94
-rw-r--r--src/lib/gssapi/mechglue/mglueP.h3
-rw-r--r--src/lib/gssapi/spnego/gssapiP_spnego.h12
-rw-r--r--src/lib/gssapi/spnego/spnego_mech.c64
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.c41
-rw-r--r--src/lib/krb5/asn.1/asn1_k_decode.h6
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.c31
-rw-r--r--src/lib/krb5/asn.1/krb5_decode.c24
-rw-r--r--src/lib/krb5/error_tables/krb5_err.et4
-rw-r--r--src/lib/krb5/krb/kfree.c19
-rw-r--r--src/lib/krb5/libkrb5.exports6
-rw-r--r--src/tests/asn.1/krb5_decode_leak.c22
-rw-r--r--src/tests/asn.1/krb5_decode_test.c16
-rw-r--r--src/tests/asn.1/krb5_encode_test.c22
-rw-r--r--src/tests/asn.1/ktest.c35
-rw-r--r--src/tests/asn.1/ktest.h4
-rw-r--r--src/tests/asn.1/ktest_equal.c23
-rw-r--r--src/tests/asn.1/ktest_equal.h6
-rw-r--r--src/tests/asn.1/reference_encode.out2
-rw-r--r--src/tests/asn.1/trval_reference.out13
43 files changed, 2850 insertions, 318 deletions
diff --git a/src/appl/gss-sample/gss-client.c b/src/appl/gss-sample/gss-client.c
index ad314c270..d922cc3bd 100644
--- a/src/appl/gss-sample/gss-client.c
+++ b/src/appl/gss-sample/gss-client.c
@@ -64,6 +64,8 @@
#endif
#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+#include <gssapi/gssapi_ext.h>
#include "gss-misc.h"
static int verbose = 1;
@@ -72,7 +74,7 @@ static void
usage()
{
fprintf(stderr, "Usage: gss-client [-port port] [-mech mechanism] [-d]\n");
- fprintf(stderr, " [-seq] [-noreplay] [-nomutual]");
+ fprintf(stderr, " [-seq] [-noreplay] [-nomutual] [-user user] [-pass pw]");
#ifdef _WIN32
fprintf(stderr, " [-threads num]");
#endif
@@ -162,6 +164,7 @@ connect_to_server(char *host, u_short port)
static int
client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
int auth_flag, int v1_format, gss_OID oid,
+ char *username, char *password,
gss_ctx_id_t *gss_context, OM_uint32 *ret_flags)
{
if (auth_flag) {
@@ -169,6 +172,53 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
gss_name_t target_name;
OM_uint32 maj_stat, min_stat, init_sec_min_stat;
int token_flags;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t gss_username = GSS_C_NO_NAME;
+ gss_OID_set_desc mechs, *mechsp = GSS_C_NO_OID_SET;
+
+ if (oid != GSS_C_NO_OID) {
+ mechs.elements = oid;
+ mechs.count = 1;
+ mechsp = &mechs;
+ }
+
+ if (username != NULL) {
+ send_tok.value = username;
+ send_tok.length = strlen(username);
+
+ maj_stat = gss_import_name(&min_stat, &send_tok,
+ (gss_OID) gss_nt_user_name,
+ &gss_username);
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("parsing client name", maj_stat, min_stat);
+ return -1;
+ }
+ }
+
+ if (password != NULL) {
+ gss_buffer_desc pwbuf;
+
+ pwbuf.value = password;
+ pwbuf.length = strlen(password);
+
+ maj_stat = gss_acquire_cred_with_password(&min_stat,
+ gss_username,
+ &pwbuf, 0,
+ mechsp, GSS_C_INITIATE,
+ &cred, NULL, NULL);
+ } else if (gss_username != GSS_C_NO_NAME) {
+ maj_stat = gss_acquire_cred(&min_stat,
+ gss_username, 0,
+ mechsp, GSS_C_INITIATE,
+ &cred, NULL, NULL);
+ } else
+ maj_stat = GSS_S_COMPLETE;
+ if (maj_stat != GSS_S_COMPLETE) {
+ display_status("acquiring creds", maj_stat, min_stat);
+ gss_release_name(&min_stat, &gss_username);
+ return -1;
+ }
+ gss_release_name(&min_stat, &gss_username);
/*
* Import the name into target_name. Use send_tok to save
@@ -213,7 +263,7 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
do {
maj_stat = gss_init_sec_context(&init_sec_min_stat,
- GSS_C_NO_CREDENTIAL, gss_context,
+ cred, gss_context,
target_name, oid, gss_flags, 0,
NULL, /* channel bindings */
token_ptr, NULL, /* mech type */
@@ -260,6 +310,7 @@ client_establish_context(int s, char *service_name, OM_uint32 gss_flags,
printf("\n");
} while (maj_stat == GSS_S_CONTINUE_NEEDED);
+ (void) gss_release_cred(&min_stat, &cred);
(void) gss_release_name(&min_stat, &target_name);
} else {
if (send_token(s, TOKEN_NOOP, empty_token) < 0)
@@ -344,7 +395,7 @@ read_file(file_name, in_buf)
static int
call_server(host, port, oid, service_name, gss_flags, auth_flag,
wrap_flag, encrypt_flag, mic_flag, v1_format, msg, use_file,
- mcount)
+ mcount, username, password)
char *host;
u_short port;
gss_OID oid;
@@ -355,6 +406,8 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag,
char *msg;
int use_file;
int mcount;
+ char *username;
+ char *password;
{
gss_ctx_id_t context;
gss_buffer_desc in_buf, out_buf;
@@ -380,7 +433,8 @@ call_server(host, port, oid, service_name, gss_flags, auth_flag,
/* Establish context */
if (client_establish_context(s, service_name, gss_flags, auth_flag,
- v1_format, oid, &context, &ret_flags) < 0) {
+ v1_format, oid, username, password,
+ &context, &ret_flags) < 0) {
(void) close(s);
return -1;
}
@@ -663,13 +717,15 @@ static OM_uint32 min_stat;
static gss_OID oid = GSS_C_NULL_OID;
static int mcount = 1, ccount = 1;
static int auth_flag, wrap_flag, encrypt_flag, mic_flag, v1_format;
+static char *username = NULL;
+static char *password = NULL;
static void
worker_bee(void *unused)
{
if (call_server(server_host, port, oid, service_name,
gss_flags, auth_flag, wrap_flag, encrypt_flag, mic_flag,
- v1_format, msg, use_file, mcount) < 0)
+ v1_format, msg, use_file, mcount, username, password) < 0)
exit(1);
#ifdef _WIN32
@@ -705,17 +761,33 @@ main(argc, argv)
if (!argc)
usage();
mechanism = *argv;
- }
+ } else if (strcmp(*argv, "-user") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ username = *argv;
+ } else if (strcmp(*argv, "-pass") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ password = *argv;
+ } else if (strcmp(*argv, "-iakerb") == 0) {
+ mechanism = "{ 1 3 6 1 5 2 5 }";
+ } else if (strcmp(*argv, "-spnego") == 0) {
+ mechanism = "{ 1 3 6 1 5 5 2 }";
+ } else if (strcmp(*argv, "-krb5") == 0) {
+ mechanism = "{ 1 3 5 1 5 2 }";
#ifdef _WIN32
- else if (strcmp(*argv, "-threads") == 0) {
+ } else if (strcmp(*argv, "-threads") == 0) {
argc--;
argv++;
if (!argc)
usage();
max_threads = atoi(*argv);
- }
#endif
- else if (strcmp(*argv, "-d") == 0) {
+ } else if (strcmp(*argv, "-d") == 0) {
gss_flags |= GSS_C_DELEG_FLAG;
} else if (strcmp(*argv, "-seq") == 0) {
gss_flags |= GSS_C_SEQUENCE_FLAG;
diff --git a/src/appl/gss-sample/gss-server.c b/src/appl/gss-sample/gss-server.c
index 8b59eb276..0ddfaeee8 100644
--- a/src/appl/gss-sample/gss-server.c
+++ b/src/appl/gss-sample/gss-server.c
@@ -58,6 +58,7 @@
#include <ctype.h>
#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
#include "gss-misc.h"
#ifdef HAVE_STRING_H
@@ -75,7 +76,8 @@ usage()
#endif
fprintf(stderr, "\n");
fprintf(stderr,
- " [-inetd] [-export] [-logfile file] service_name\n");
+ " [-inetd] [-export] [-logfile file] [-keytab keytab]\n"
+ " service_name\n");
exit(1);
}
@@ -690,6 +692,15 @@ main(int argc, char **argv)
exit(1);
}
}
+ } else if (strcmp(*argv, "-keytab") == 0) {
+ argc--;
+ argv++;
+ if (!argc)
+ usage();
+ if (krb5_gss_register_acceptor_identity(*argv)) {
+ fprintf(stderr, "failed to register keytab\n");
+ exit(1);
+ }
} else
break;
argc--;
diff --git a/src/appl/gss-sample/t_gss_sample.py b/src/appl/gss-sample/t_gss_sample.py
index 8a09b2123..09d0803ce 100644
--- a/src/appl/gss-sample/t_gss_sample.py
+++ b/src/appl/gss-sample/t_gss_sample.py
@@ -27,14 +27,54 @@ appdir = os.path.join(buildtop, 'appl', 'gss-sample')
gss_client = os.path.join(appdir, 'gss-client')
gss_server = os.path.join(appdir, 'gss-server')
-for realm in multipass_realms():
+# Run a gss-server process and a gss-client process, with additional
+# gss-client flags given by options. Verify that gss-client displayed
+# the expected output for a successful negotiation, and that we
+# obtained credentials for the host service.
+def server_client_test(realm, options):
portstr = str(realm.server_port())
server = realm.start_server([gss_server, '-port', portstr, 'host'],
'starting...')
- output = realm.run_as_client([gss_client, '-port', portstr,
- hostname, 'host', 'testmsg'])
+ output = realm.run_as_client([gss_client, '-port', portstr] + options +
+ [hostname, 'host', 'testmsg'])
if 'Signature verified.' not in output:
fail('Expected message not seen in gss-client output')
stop_daemon(server)
+ realm.klist(realm.user_princ, realm.host_princ)
+
+# Make up a filename to hold user's initial credentials.
+def ccache_savefile(realm):
+ return os.path.join(realm.testdir, 'ccache.copy')
+
+# Move user's initial credentials into the save file.
+def ccache_save(realm):
+ os.rename(realm.ccache, ccache_savefile(realm))
+
+# Copy user's initial credentials from the save file into the ccache.
+def ccache_restore(realm):
+ shutil.copyfile(ccache_savefile(realm), realm.ccache)
+
+# Perform a regular (TGS path) test of the server and client.
+def tgs_test(realm, options):
+ ccache_restore(realm)
+ server_client_test(realm, options)
+
+# Perform a test of the server and client with initial credentials
+# obtained through gss_acquire_cred_with_password().
+def as_test(realm, options):
+ os.remove(realm.ccache)
+ server_client_test(realm, options + ['-user', realm.user_princ,
+ '-pass', password('user')])
+
+for realm in multipass_realms():
+ ccache_save(realm)
+
+ tgs_test(realm, ['-krb5'])
+ tgs_test(realm, ['-spnego'])
+ tgs_test(realm, ['-iakerb'])
+
+ as_test(realm, ['-krb5'])
+ as_test(realm, ['-spnego'])
+ as_test(realm, ['-iakerb'])
success('GSS sample application')
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index fde8e6660..9a23a7e68 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -358,6 +358,10 @@ typedef INT64_TYPE krb5_int64;
#define KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED 79 /* missing paChecksum in PA-PK-AS-REQ */
#define KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED 80 /* bad digest algorithm in SignedData */
#define KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED 81
+#define KRB_AP_ERR_IAKERB_KDC_NOT_FOUND 85 /* The IAKERB proxy could
+not find a KDC */
+#define KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE 86 /* The KDC did not respond
+to the IAKERB proxy */
/*
* This structure is returned in the e-data field of the KRB-ERROR
@@ -1032,6 +1036,15 @@ typedef struct _krb5_ad_signedpath {
krb5_pa_data **method_data;
} krb5_ad_signedpath;
+typedef struct _krb5_iakerb_header {
+ krb5_data target_realm;
+ krb5_data *cookie;
+} krb5_iakerb_header;
+
+typedef struct _krb5_iakerb_finished {
+ krb5_checksum checksum;
+} krb5_iakerb_finished;
+
typedef krb5_error_code
(*krb5_preauth_obtain_proc)(krb5_context, krb5_pa_data *,
krb5_etype_info, krb5_keyblock *,
@@ -1329,6 +1342,9 @@ void KRB5_CALLCONV krb5_free_fast_finished(krb5_context, krb5_fast_finished *);
void KRB5_CALLCONV krb5_free_fast_response(krb5_context, krb5_fast_response *);
void KRB5_CALLCONV krb5_free_ad_kdcissued(krb5_context, krb5_ad_kdcissued *);
void KRB5_CALLCONV krb5_free_ad_signedpath(krb5_context, krb5_ad_signedpath *);
+void KRB5_CALLCONV krb5_free_iakerb_header(krb5_context, krb5_iakerb_header *);
+void KRB5_CALLCONV krb5_free_iakerb_finished(krb5_context,
+ krb5_iakerb_finished *);
/* #include "krb5/wordsize.h" -- comes in through base-defs.h. */
#include "com_err.h"
@@ -1742,6 +1758,12 @@ krb5_error_code
encode_krb5_pa_fx_fast_reply(const krb5_enc_data *, krb5_data **);
krb5_error_code
+encode_krb5_iakerb_header(const krb5_iakerb_header *, krb5_data **);
+
+krb5_error_code
+encode_krb5_iakerb_finished(const krb5_iakerb_finished *, krb5_data **);
+
+krb5_error_code
encode_krb5_fast_response(const krb5_fast_response *, krb5_data **);
krb5_error_code
@@ -1939,6 +1961,12 @@ decode_krb5_ad_kdcissued(const krb5_data *, krb5_ad_kdcissued **);
krb5_error_code
decode_krb5_ad_signedpath(const krb5_data *, krb5_ad_signedpath **);
+krb5_error_code
+decode_krb5_iakerb_header(const krb5_data *, krb5_iakerb_header **);
+
+krb5_error_code
+decode_krb5_iakerb_finished(const krb5_data *, krb5_iakerb_finished **);
+
struct _krb5_key_data; /* kdb.h */
struct ldap_seqof_key_data {
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 98ffb70b8..1c89f9e92 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -637,6 +637,7 @@ krb5_c_keyed_checksum_types(krb5_context context, krb5_enctype enctype,
#define KRB5_KEYUSAGE_PA_S4U_X509_USER_REPLY 27 /* XXX note conflict with above */
#define KRB5_KEYUSAGE_AD_SIGNEDPATH -21
+#define KRB5_KEYUSAGE_IAKERB_FINISHED 42
#define KRB5_KEYUSAGE_PA_PKINIT_KX 44
/* define in draft-ietf-krb-wg-preauth-framework*/
#define KRB5_KEYUSAGE_FAST_REQ_CHKSUM 50
diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h
index 12216775a..8e5c7c9f7 100644
--- a/src/lib/gssapi/generic/gssapi_ext.h
+++ b/src/lib/gssapi/generic/gssapi_ext.h
@@ -47,6 +47,33 @@ int KRB5_CALLCONV __gss_userok
const char * /*username*/);
#endif
+OM_uint32 KRB5_CALLCONV
+gss_acquire_cred_with_password(
+ OM_uint32 *, /* minor_status */
+ const gss_name_t, /* desired_name */
+ const gss_buffer_t, /* password */
+ OM_uint32, /* time_req */
+ const gss_OID_set, /* desired_mechs */
+ gss_cred_usage_t, /* cred_usage */
+ gss_cred_id_t *, /* output_cred_handle */
+ gss_OID_set *, /* actual_mechs */
+ OM_uint32 *); /* time_rec */
+
+OM_uint32 KRB5_CALLCONV
+gss_add_cred_with_password(
+ OM_uint32 *, /* minor_status */
+ const gss_cred_id_t,/* input_cred_handle */
+ const gss_name_t, /* desired_name */
+ const gss_OID, /* desired_mech */
+ const gss_buffer_t, /* password */
+ gss_cred_usage_t, /* cred_usage */
+ OM_uint32, /* initiator_time_req */
+ OM_uint32, /* acceptor_time_req */
+ gss_cred_id_t *, /* output_cred_handle */
+ gss_OID_set *, /* actual_mechs */
+ OM_uint32 *, /* initiator_time_rec */
+ OM_uint32 *); /* acceptor_time_rec */
+
/*
* GGF extensions
*/
diff --git a/src/lib/gssapi/krb5/Makefile.in b/src/lib/gssapi/krb5/Makefile.in
index 01591b2c0..2f4e96976 100644
--- a/src/lib/gssapi/krb5/Makefile.in
+++ b/src/lib/gssapi/krb5/Makefile.in
@@ -52,6 +52,7 @@ SRCS = \
$(srcdir)/export_sec_context.c \
$(srcdir)/get_tkt_flags.c \
$(srcdir)/gssapi_krb5.c \
+ $(srcdir)/iakerb.c \
$(srcdir)/import_name.c \
$(srcdir)/import_sec_context.c \
$(srcdir)/indicate_mechs.c \
@@ -106,6 +107,7 @@ OBJS = \
$(OUTPRE)export_sec_context.$(OBJEXT) \
$(OUTPRE)get_tkt_flags.$(OBJEXT) \
$(OUTPRE)gssapi_krb5.$(OBJEXT) \
+ $(OUTPRE)iakerb.$(OBJEXT) \
$(OUTPRE)import_name.$(OBJEXT) \
$(OUTPRE)import_sec_context.$(OBJEXT) \
$(OUTPRE)indicate_mechs.$(OBJEXT) \
@@ -163,6 +165,7 @@ STLIBOBJS = \
export_sec_context.o \
get_tkt_flags.o \
gssapi_krb5.o \
+ iakerb.o \
import_name.o \
import_sec_context.o \
indicate_mechs.o \
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index ce3075fad..368ef21be 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -105,7 +105,6 @@
#endif
#include <assert.h>
-
#ifdef CFX_EXERCISE
#define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
#else
@@ -384,12 +383,49 @@ fail:
return major_status;
}
+static krb5_error_code
+kg_process_extension(krb5_context context,
+ krb5_auth_context auth_context,
+ int ext_type,
+ krb5_data *ext_data,
+ krb5_gss_ctx_ext_t exts)
+{
+ krb5_error_code code = 0;
+
+ assert(exts != NULL);
+
+ switch (ext_type) {
+ case KRB5_GSS_EXTS_IAKERB_FINISHED:
+ if (exts->iakerb.conv == NULL) {
+ code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
+ } else {
+ krb5_key key;
+
+ code = krb5_auth_con_getrecvsubkey_k(context, auth_context, &key);
+ if (code != 0)
+ break;
+
+ code = iakerb_verify_finished(context, key, exts->iakerb.conv,
+ ext_data);
+ if (code == 0)
+ exts->iakerb.verified = 1;
+
+ krb5_k_free_key(context, key);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return code;
+}
+
static OM_uint32
kg_accept_krb5(minor_status, context_handle,
verifier_cred_handle, input_token,
input_chan_bindings, src_name, mech_type,
output_token, ret_flags, time_rec,
- delegated_cred_handle)
+ delegated_cred_handle, exts)
OM_uint32 *minor_status;
gss_ctx_id_t *context_handle;
gss_cred_id_t verifier_cred_handle;
@@ -401,6 +437,7 @@ kg_accept_krb5(minor_status, context_handle,
OM_uint32 *ret_flags;
OM_uint32 *time_rec;
gss_cred_id_t *delegated_cred_handle;
+ krb5_gss_ctx_ext_t exts;
{
krb5_context context;
unsigned char *ptr, *ptr2;
@@ -741,16 +778,11 @@ kg_accept_krb5(minor_status, context_handle,
/* if the checksum length > 24, there are options to process */
- if(authdat->checksum->length > 24 && (gss_flags & GSS_C_DELEG_FLAG)) {
-
- i = authdat->checksum->length - 24;
-
+ i = authdat->checksum->length - 24;
+ if (i && (gss_flags & GSS_C_DELEG_FLAG)) {
if (i >= 4) {
-
TREAD_INT16(ptr, option_id, bigend);
-
TREAD_INT16(ptr, option.length, bigend);
-
i -= 4;
if (i < option.length || option.length < 0) {
@@ -784,37 +816,41 @@ kg_accept_krb5(minor_status, context_handle,
} /* if i >= 4 */
/* ignore any additional trailing data, for now */
-#ifdef CFX_EXERCISE
- {
- FILE *f = fopen("/tmp/gsslog", "a");
- if (f) {
- fprintf(f,
- "initial context token with delegation, %d extra bytes\n",
- i);
- fclose(f);
- }
+ }
+ while (i > 0) {
+ /* Process Type-Length-Data options */
+ if (i < 8) {
+ code = KG_BAD_LENGTH;
+ major_status = GSS_S_FAILURE;
+ goto fail;
}
-#endif
- } else {
-#ifdef CFX_EXERCISE
- {
- FILE *f = fopen("/tmp/gsslog", "a");
- if (f) {
- if (gss_flags & GSS_C_DELEG_FLAG)
- fprintf(f,
- "initial context token, delegation flag but too small\n");
- else
- /* no deleg flag, length might still be too big */
- fprintf(f,
- "initial context token, %d extra bytes\n",
- authdat->checksum->length - 24);
- fclose(f);
- }
+ TREAD_INT(ptr, option_id, 1);
+ TREAD_INT(ptr, option.length, 1);
+ i -= 8;
+ if (i < option.length) {
+ code = KG_BAD_LENGTH;
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ TREAD_STR(ptr, ptr2, option.length);
+ option.data = (char *)ptr2;
+
+ i -= option.length;
+
+ code = kg_process_extension(context, auth_context,
+ option_id, &option, exts);
+ if (code != 0) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
}
-#endif
}
}
+ if (exts->iakerb.conv && !exts->iakerb.verified) {
+ major_status = GSS_S_BAD_SIG;
+ goto fail;
+ }
+
/* only DCE_STYLE clients are allowed to send raw AP-REQs */
if (no_encap != ((gss_flags & GSS_C_DCE_STYLE) != 0)) {
major_status = GSS_S_DEFECTIVE_TOKEN;
@@ -831,6 +867,7 @@ kg_accept_krb5(minor_status, context_handle,
}
memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
+ ctx->magic = KG_CONTEXT;
ctx->mech_used = (gss_OID) mech_used;
ctx->auth_context = auth_context;
ctx->initiate = 0;
@@ -1248,22 +1285,19 @@ done:
#endif /* LEAN_CLIENT */
OM_uint32
-krb5_gss_accept_sec_context(minor_status, context_handle,
- verifier_cred_handle, input_token,
- input_chan_bindings, src_name, mech_type,
- output_token, ret_flags, time_rec,
- delegated_cred_handle)
- OM_uint32 *minor_status;
- gss_ctx_id_t *context_handle;
- gss_cred_id_t verifier_cred_handle;
- gss_buffer_t input_token;
- gss_channel_bindings_t input_chan_bindings;
- gss_name_t *src_name;
- gss_OID *mech_type;
- gss_buffer_t output_token;
- OM_uint32 *ret_flags;
- OM_uint32 *time_rec;
- gss_cred_id_t *delegated_cred_handle;
+krb5_gss_accept_sec_context_ext(
+ OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t verifier_cred_handle,
+ gss_buffer_t input_token,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle,
+ krb5_gss_ctx_ext_t exts)
{
krb5_gss_ctx_id_rec *ctx = (krb5_gss_ctx_id_rec *)*context_handle;
@@ -1291,5 +1325,42 @@ krb5_gss_accept_sec_context(minor_status, context_handle,
verifier_cred_handle, input_token,
input_chan_bindings, src_name, mech_type,
output_token, ret_flags, time_rec,
- delegated_cred_handle);
+ delegated_cred_handle, exts);
+}
+
+OM_uint32
+krb5_gss_accept_sec_context(minor_status, context_handle,
+ verifier_cred_handle, input_token,
+ input_chan_bindings, src_name, mech_type,
+ output_token, ret_flags, time_rec,
+ delegated_cred_handle)
+ OM_uint32 *minor_status;
+ gss_ctx_id_t *context_handle;
+ gss_cred_id_t verifier_cred_handle;
+ gss_buffer_t input_token;
+ gss_channel_bindings_t input_chan_bindings;
+ gss_name_t *src_name;
+ gss_OID *mech_type;
+ gss_buffer_t output_token;
+ OM_uint32 *ret_flags;
+ OM_uint32 *time_rec;
+ gss_cred_id_t *delegated_cred_handle;
+{
+ krb5_gss_ctx_ext_rec exts;
+
+ memset(&exts, 0, sizeof(exts));
+
+ return krb5_gss_accept_sec_context_ext(minor_status,
+ context_handle,
+ verifier_cred_handle,
+ input_token,
+ input_chan_bindings,
+ src_name,
+ mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ delegated_cred_handle,
+ &exts);
}
+
diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
index 2ccf7dbf3..c3e84818d 100644
--- a/src/lib/gssapi/krb5/acquire_cred.c
+++ b/src/lib/gssapi/krb5/acquire_cred.c
@@ -222,16 +222,17 @@ acquire_init_cred(krb5_context context,
OM_uint32 *minor_status,
krb5_gss_name_t desired_name,
krb5_gss_name_t *output_name,
+ gss_buffer_t password,
krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
krb5_ccache ccache;
- krb5_principal princ, tmp_princ;
- krb5_flags flags;
+ krb5_principal princ = NULL, tmp_princ;
krb5_cc_cursor cur;
krb5_creds creds;
int got_endtime;
int caller_provided_ccache_name = 0;
+ krb5_data password_data;
cred->ccache = NULL;
@@ -335,24 +336,30 @@ acquire_init_cred(krb5_context context,
}
/* turn off OPENCLOSE mode while extensive frobbing is going on */
-
- flags = 0; /* turns off OPENCLOSE mode */
- if ((code = krb5_cc_set_flags(context, ccache, flags))) {
+ code = krb5_cc_set_flags(context, ccache, 0);
+ if (code == KRB5_FCC_NOFILE &&
+ password != GSS_C_NO_BUFFER && desired_name != NULL) {
+ /* We will get initial creds later. */
+ code = krb5_cc_initialize(context, ccache, desired_name->princ);
+ if (code == 0)
+ code = krb5_cc_set_flags(context, ccache, 0);
+ }
+ if (code != 0) {
(void)krb5_cc_close(context, ccache);
*minor_status = code;
return(GSS_S_CRED_UNAVAIL);
}
/* get out the principal name and see if it matches */
-
- if ((code = krb5_cc_get_principal(context, ccache, &princ))) {
+ code = krb5_cc_get_principal(context, ccache, &princ);
+ if (code != 0) {
(void)krb5_cc_close(context, ccache);
*minor_status = code;
return(GSS_S_FAILURE);
}
- if (desired_name != (krb5_gss_name_t)NULL) {
- if (! krb5_principal_compare(context, princ, desired_name->princ)) {
+ if (desired_name != NULL) {
+ if (!krb5_principal_compare(context, princ, desired_name->princ)) {
(void)krb5_free_principal(context, princ);
(void)krb5_cc_close(context, ccache);
*minor_status = KG_CCACHE_NOMATCH;
@@ -369,6 +376,32 @@ acquire_init_cred(krb5_context context,
*minor_status = code;
return(GSS_S_FAILURE);
}
+ /* princ is now owned by output_name, it need not be freed here */
+ }
+
+ if (password != GSS_C_NO_BUFFER) {
+ /* stash the password for later */
+ password_data.length = password->length;
+ password_data.data = (char *)password->value;
+
+ code = krb5int_copy_data_contents_add0(context, &password_data,
+ &cred->password);
+ if (code != 0) {
+ (void)krb5_cc_close(context, ccache);
+ *minor_status = code;
+ return GSS_S_FAILURE;
+ }
+
+ /* restore the OPENCLOSE flag */
+ code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE);
+ if (code != 0) {
+ (void)krb5_cc_close(context, ccache);
+ *minor_status = code;
+ return GSS_S_FAILURE;
+ }
+
+ cred->ccache = ccache;
+ return GSS_S_COMPLETE;
}
/* iterate over the ccache, find the tgt */
@@ -388,7 +421,7 @@ acquire_init_cred(krb5_context context,
code = krb5_build_principal_ext(context, &tmp_princ,
krb5_princ_realm(context, princ)->length,
krb5_princ_realm(context, princ)->data,
- 6, "krbtgt",
+ KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
krb5_princ_realm(context, princ)->length,
krb5_princ_realm(context, princ)->data,
0);
@@ -433,8 +466,7 @@ acquire_init_cred(krb5_context context,
*minor_status = code;
return(GSS_S_FAILURE);
}
- flags = KRB5_TC_OPENCLOSE; /* turns on OPENCLOSE mode */
- if ((code = krb5_cc_set_flags(context, ccache, flags))) {
+ if ((code = krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE))) {
(void)krb5_cc_close(context, ccache);
*minor_status = code;
return(GSS_S_FAILURE);
@@ -449,38 +481,36 @@ acquire_init_cred(krb5_context context,
}
/*ARGSUSED*/
-OM_uint32
-krb5_gss_acquire_cred(minor_status, desired_name, time_req,
- desired_mechs, cred_usage, output_cred_handle,
- actual_mechs, time_rec)
+static OM_uint32
+acquire_cred(minor_status, desired_name, password, time_req,
+ desired_mechs, cred_usage, output_cred_handle,
+ actual_mechs, time_rec, req_iakerb)
OM_uint32 *minor_status;
- gss_name_t desired_name;
+ const gss_name_t desired_name;
+ const gss_buffer_t password;
OM_uint32 time_req;
- gss_OID_set desired_mechs;
+ const gss_OID_set desired_mechs;
gss_cred_usage_t cred_usage;
gss_cred_id_t *output_cred_handle;
gss_OID_set *actual_mechs;
OM_uint32 *time_rec;
+ int req_iakerb;
{
- krb5_context context;
+ krb5_context context = NULL;
size_t i;
- krb5_gss_cred_id_t cred;
- gss_OID_set ret_mechs = NULL;
+ krb5_gss_cred_id_t cred = NULL;
+ gss_OID_set ret_mechs = GSS_C_NO_OID_SET;
int req_old, req_new;
OM_uint32 ret;
- krb5_error_code code;
+ krb5_error_code code = 0;
code = gss_krb5int_initialize_library();
- if (code) {
- *minor_status = code;
- return GSS_S_FAILURE;
- }
+ if (code)
+ goto krb_error_out;
code = krb5_gss_init_context(&context);
- if (code) {
- *minor_status = code;
- return GSS_S_FAILURE;
- }
+ if (code)
+ goto krb_error_out;
/* make sure all outputs are valid */
@@ -495,9 +525,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
/*SUPPRESS 29*/
if ((desired_name != GSS_C_NO_NAME) &&
(! kg_validate_name(desired_name))) {
- *minor_status = (OM_uint32) G_VALIDATE_FAILED;
- krb5_free_context(context);
- return(GSS_S_CALL_BAD_STRUCTURE|GSS_S_BAD_NAME);
+ code = G_VALIDATE_FAILED;
+ goto krb_error_out;
}
/* verify that the requested mechanism set is the default, or
@@ -518,26 +547,21 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
}
if (!req_old && !req_new) {
- *minor_status = 0;
- krb5_free_context(context);
- return(GSS_S_BAD_MECH);
+ ret = GSS_S_BAD_MECH;
+ goto error_out;
}
}
/* create the gss cred structure */
-
- if ((cred =
- (krb5_gss_cred_id_t) xmalloc(sizeof(krb5_gss_cred_id_rec))) == NULL) {
- *minor_status = ENOMEM;
- krb5_free_context(context);
- return(GSS_S_FAILURE);
- }
- memset(cred, 0, sizeof(krb5_gss_cred_id_rec));
+ cred = k5alloc(sizeof(krb5_gss_cred_id_rec), &code);
+ if (code != 0)
+ goto krb_error_out;
cred->usage = cred_usage;
cred->name = NULL;
- cred->prerfc_mech = (req_old != 0);
- cred->rfc_mech = (req_new != 0);
+ cred->prerfc_mech = (req_old != 0) && (req_iakerb == 0);
+ cred->rfc_mech = (req_new != 0) && (req_iakerb == 0);
+ cred->iakerb_mech = req_iakerb;
cred->default_identity = (desired_name == GSS_C_NO_NAME);
#ifndef LEAN_CLIENT
@@ -546,11 +570,9 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
cred->ccache = NULL;
code = k5_mutex_init(&cred->lock);
- if (code) {
- *minor_status = code;
- krb5_free_context(context);
- return GSS_S_FAILURE;
- }
+ if (code)
+ goto krb_error_out;
+
/* Note that we don't need to lock this GSSAPI credential record
here, because no other thread can gain access to it until we
return it. */
@@ -558,11 +580,8 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
if ((cred_usage != GSS_C_INITIATE) &&
(cred_usage != GSS_C_ACCEPT) &&
(cred_usage != GSS_C_BOTH)) {
- k5_mutex_destroy(&cred->lock);
- xfree(cred);
*minor_status = (OM_uint32) G_BAD_USAGE;
- krb5_free_context(context);
- return(GSS_S_FAILURE);
+ goto error_out;
}
/* if requested, acquire credentials for accepting */
@@ -574,14 +593,7 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
(krb5_gss_name_t)desired_name,
&cred->name, cred))
!= GSS_S_COMPLETE) {
- if (cred->name)
- kg_release_name(context, 0, &cred->name);
- k5_mutex_destroy(&cred->lock);
- xfree(cred);
- /* minor_status set by acquire_accept_cred() */
- save_error_info(*minor_status, context);
- krb5_free_context(context);
- return(ret);
+ goto error_out;
}
#endif /* LEAN_CLIENT */
@@ -589,46 +601,24 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
/* this will fill in cred->name if it wasn't set above, and
the desired_name is not specified */
- if ((cred_usage == GSS_C_INITIATE) ||
- (cred_usage == GSS_C_BOTH))
- if ((ret =
- acquire_init_cred(context, minor_status,
- cred->name?cred->name:(krb5_gss_name_t)desired_name,
- &cred->name, cred))
- != GSS_S_COMPLETE) {
-#ifndef LEAN_CLIENT
- if (cred->keytab)
- krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
- if (cred->name)
- kg_release_name(context, 0, &cred->name);
- k5_mutex_destroy(&cred->lock);
- xfree(cred);
- /* minor_status set by acquire_init_cred() */
- save_error_info(*minor_status, context);
- krb5_free_context(context);
- return(ret);
- }
+ if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
+ ret = acquire_init_cred(context, minor_status,
+ cred->name ?
+ cred->name : (krb5_gss_name_t)desired_name,
+ &cred->name, password, cred);
+ if (ret != GSS_S_COMPLETE)
+ goto error_out;
+ }
/* if the princ wasn't filled in already, fill it in now */
- if (!cred->name && (desired_name != GSS_C_NO_NAME))
- if ((code = kg_duplicate_name(context,
- (krb5_gss_name_t)desired_name,
- 0, &cred->name))) {
- if (cred->ccache)
- (void)krb5_cc_close(context, cred->ccache);
-#ifndef LEAN_CLIENT
- if (cred->keytab)
- (void)krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
- k5_mutex_destroy(&cred->lock);
- xfree(cred);
- *minor_status = code;
- save_error_info(*minor_status, context);
- krb5_free_context(context);
- return(GSS_S_FAILURE);
- }
+ if (!cred->name && (desired_name != GSS_C_NO_NAME)) {
+ code = kg_duplicate_name(context,
+ (krb5_gss_name_t)desired_name,
+ 0, &cred->name);
+ if (code != 0)
+ goto krb_error_out;
+ }
/*** at this point, the cred structure has been completely created */
@@ -640,22 +630,9 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
} else {
krb5_timestamp now;
- if ((code = krb5_timeofday(context, &now))) {
- if (cred->ccache)
- (void)krb5_cc_close(context, cred->ccache);
-#ifndef LEAN_CLIENT
- if (cred->keytab)
- (void)krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
- if (cred->name)
- kg_release_name(context, 0, &cred->name);
- k5_mutex_destroy(&cred->lock);
- xfree(cred);
- *minor_status = code;
- save_error_info(*minor_status, context);
- krb5_free_context(context);
- return(GSS_S_FAILURE);
- }
+ code = krb5_timeofday(context, &now);
+ if (code != 0)
+ goto krb_error_out;
if (time_rec)
*time_rec = (cred->tgt_expire > now) ? (cred->tgt_expire - now) : 0;
@@ -673,44 +650,20 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
(cred->rfc_mech &&
GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
gss_mech_krb5,
+ &ret_mechs))) ||
+ (cred->iakerb_mech &&
+ GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+ gss_mech_iakerb,
&ret_mechs)))) {
- if (cred->ccache)
- (void)krb5_cc_close(context, cred->ccache);
-#ifndef LEAN_CLIENT
- if (cred->keytab)
- (void)krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
- if (cred->name)
- kg_release_name(context, 0, &cred->name);
- k5_mutex_destroy(&cred->lock);
- xfree(cred);
- /* *minor_status set above */
- krb5_free_context(context);
- return(ret);
+ goto error_out;
}
}
/* intern the credential handle */
if (! kg_save_cred_id((gss_cred_id_t) cred)) {
- if (ret_mechs) {
- free(ret_mechs->elements);
- free(ret_mechs);
- }
- if (cred->ccache)
- (void)krb5_cc_close(context, cred->ccache);
-#ifndef LEAN_CLIENT
- if (cred->keytab)
- (void)krb5_kt_close(context, cred->keytab);
-#endif /* LEAN_CLIENT */
- if (cred->name)
- kg_release_name(context, 0, &cred->name);
- k5_mutex_destroy(&cred->lock);
- xfree(cred);
- *minor_status = (OM_uint32) G_VALIDATE_FAILED;
- save_error_string(*minor_status, "error saving credentials");
- krb5_free_context(context);
- return(GSS_S_FAILURE);
+ ret = GSS_S_FAILURE;
+ goto error_out;
}
/* return success */
@@ -722,6 +675,29 @@ krb5_gss_acquire_cred(minor_status, desired_name, time_req,
krb5_free_context(context);
return(GSS_S_COMPLETE);
+
+krb_error_out:
+ *minor_status = code;
+ ret = GSS_S_FAILURE;
+
+error_out:
+ if (ret_mechs != GSS_C_NO_OID_SET) {
+ free(ret_mechs->elements);
+ free(ret_mechs);
+ }
+ if (cred->ccache)
+ (void)krb5_cc_close(context, cred->ccache);
+#ifndef LEAN_CLIENT
+ if (cred->keytab)
+ (void)krb5_kt_close(context, cred->keytab);
+#endif /* LEAN_CLIENT */
+ if (cred->name)
+ kg_release_name(context, 0, &cred->name);
+ k5_mutex_destroy(&cred->lock);
+ xfree(cred);
+ save_error_info(*minor_status, context);
+ krb5_free_context(context);
+ return ret;
}
OM_uint32
@@ -768,3 +744,76 @@ gss_krb5int_set_cred_rcache(OM_uint32 *minor_status,
*minor_status = 0;
return GSS_S_COMPLETE;
}
+
+OM_uint32
+krb5_gss_acquire_cred(minor_status, desired_name, time_req,
+ desired_mechs, cred_usage, output_cred_handle,
+ actual_mechs, time_rec)
+ OM_uint32 *minor_status;
+ gss_name_t desired_name;
+ OM_uint32 time_req;
+ gss_OID_set desired_mechs;
+ gss_cred_usage_t cred_usage;
+ gss_cred_id_t *output_cred_handle;
+ gss_OID_set *actual_mechs;
+ OM_uint32 *time_rec;
+{
+ return acquire_cred(minor_status, desired_name, GSS_C_NO_BUFFER,
+ time_req, desired_mechs,
+ cred_usage, output_cred_handle, actual_mechs,
+ time_rec, 0);
+}
+
+OM_uint32
+iakerb_gss_acquire_cred(minor_status, desired_name, time_req,
+ desired_mechs, cred_usage, output_cred_handle,
+ actual_mechs, time_rec)
+ OM_uint32 *minor_status;
+ gss_name_t desired_name;
+ OM_uint32 time_req;
+ gss_OID_set desired_mechs;
+ gss_cred_usage_t cred_usage;
+ gss_cred_id_t *output_cred_handle;
+ gss_OID_set *actual_mechs;
+ OM_uint32 *time_rec;
+{
+ return acquire_cred(minor_status, desired_name, GSS_C_NO_BUFFER,
+ time_req, desired_mechs,
+ cred_usage, output_cred_handle, actual_mechs,
+ time_rec, 1);
+}
+
+OM_uint32
+krb5_gss_acquire_cred_with_password(OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ const gss_buffer_t password,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ int cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec)
+{
+ return acquire_cred(minor_status, desired_name, password,
+ time_req, desired_mechs,
+ cred_usage, output_cred_handle, actual_mechs,
+ time_rec, 0);
+}
+
+OM_uint32
+iakerb_gss_acquire_cred_with_password(OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ const gss_buffer_t password,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ int cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec)
+{
+ return acquire_cred(minor_status, desired_name, password,
+ time_req, desired_mechs,
+ cred_usage, output_cred_handle, actual_mechs,
+ time_rec, 1);
+}
+
diff --git a/src/lib/gssapi/krb5/add_cred.c b/src/lib/gssapi/krb5/add_cred.c
index 6f6707c1a..489703655 100644
--- a/src/lib/gssapi/krb5/add_cred.c
+++ b/src/lib/gssapi/krb5/add_cred.c
@@ -145,7 +145,8 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
/* check that desired_mech isn't already in the credential */
if ((g_OID_equal(desired_mech, gss_mech_krb5_old) && cred->prerfc_mech) ||
- (g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech)) {
+ (g_OID_equal(desired_mech, gss_mech_krb5) && cred->rfc_mech) ||
+ (g_OID_equal(desired_mech, gss_mech_iakerb) && cred->iakerb_mech)) {
*minor_status = 0;
krb5_free_context(context);
return(GSS_S_DUPLICATE_ELEMENT);
@@ -197,6 +198,7 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
new_cred->usage = cred_usage;
new_cred->prerfc_mech = cred->prerfc_mech;
new_cred->rfc_mech = cred->rfc_mech;
+ new_cred->iakerb_mech = cred->iakerb_mech;
new_cred->tgt_expire = cred->tgt_expire;
if (cred->name)
@@ -359,6 +361,8 @@ krb5_gss_add_cred(minor_status, input_cred_handle,
cred->prerfc_mech = 1;
else if (g_OID_equal(desired_mech, gss_mech_krb5))
cred->rfc_mech = 1;
+ else if (g_OID_equal(desired_mech, gss_mech_iakerb))
+ cred->iakerb_mech = 1;
/* set the outputs */
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
index 2ac4b3ae2..1ec7be33f 100644
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
@@ -86,6 +86,9 @@
#define GSS_MECH_KRB5_WRONG_OID_LENGTH 9
#define GSS_MECH_KRB5_WRONG_OID "\052\206\110\202\367\022\001\002\002"
+/* IAKERB variant */
+#define GSS_MECH_IAKERB_OID_LENGTH 6
+#define GSS_MECH_IAKERB_OID "\053\006\001\005\002\005"
#define CKSUMTYPE_KG_CB 0x8003
@@ -100,6 +103,7 @@
#define KG2_TOK_MIC_MSG 0x0404
#define KG2_TOK_WRAP_MSG 0x0504
#define KG2_TOK_DEL_CTX 0x0405
+#define IAKERB_TOK_PROXY 0x0501
#define KRB5_GSS_FOR_CREDS_OPTION 1
@@ -170,6 +174,7 @@ typedef struct _krb5_gss_cred_id_rec {
unsigned int rfc_mech : 1;
unsigned int proxy_cred : 1;
unsigned int default_identity : 1;
+ unsigned int iakerb_mech : 1;
/* keytab (accept) data */
krb5_keytab keytab;
@@ -179,9 +184,18 @@ typedef struct _krb5_gss_cred_id_rec {
krb5_ccache ccache;
krb5_timestamp tgt_expire;
krb5_enctype *req_enctypes; /* limit negotiated enctypes to this list */
+ krb5_data password;
} krb5_gss_cred_id_rec, *krb5_gss_cred_id_t;
+typedef struct _krb5_gss_ctx_ext_rec {
+ struct {
+ krb5_data *conv;
+ int verified;
+ } iakerb;
+} krb5_gss_ctx_ext_rec, *krb5_gss_ctx_ext_t;
+
typedef struct _krb5_gss_ctx_id_rec {
+ krb5_magic magic;
unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */
unsigned int established : 1;
unsigned int big_endian : 1;
@@ -239,7 +253,9 @@ extern k5_mutex_t gssint_krb5_keytab_lock;
#define kg_validate_name(name) g_validate_name(&kg_vdb,name)
#define kg_validate_cred_id(cred) g_validate_cred_id(&kg_vdb,cred)
-#define kg_validate_ctx_id(ctx) g_validate_ctx_id(&kg_vdb,ctx)
+#define kg_validate_ctx_id(ctx) (g_validate_ctx_id(&kg_vdb,ctx) && \
+ ((krb5_gss_ctx_id_t)ctx)->magic == \
+ KG_CONTEXT)
#define kg_validate_lucidctx_id(lctx) g_validate_lucidctx_id(&kg_vdb,lctx)
#define kg_delete_name(name) g_delete_name(&kg_vdb,name)
@@ -492,7 +508,8 @@ kg_new_connection(
OM_uint32 *ret_flags,
OM_uint32 *time_rec,
krb5_context context,
- int default_mech);
+ int default_mech,
+ krb5_gss_ctx_ext_t exts);
/** declarations of internal name mechanism functions **/
@@ -507,6 +524,42 @@ OM_uint32 krb5_gss_acquire_cred
OM_uint32* /* time_rec */
);
+OM_uint32
+iakerb_gss_acquire_cred
+(OM_uint32*, /* minor_status */
+ gss_name_t, /* desired_name */
+ OM_uint32, /* time_req */
+ gss_OID_set, /* desired_mechs */
+ gss_cred_usage_t, /* cred_usage */
+ gss_cred_id_t*, /* output_cred_handle */
+ gss_OID_set*, /* actual_mechs */
+ OM_uint32* /* time_rec */
+);
+
+OM_uint32
+krb5_gss_acquire_cred_with_password(
+ OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ const gss_buffer_t password,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ int cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec);
+
+OM_uint32
+iakerb_gss_acquire_cred_with_password(
+ OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ const gss_buffer_t password,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ int cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec);
+
OM_uint32 krb5_gss_release_cred
(OM_uint32*, /* minor_status */
gss_cred_id_t* /* cred_handle */
@@ -529,6 +582,24 @@ OM_uint32 krb5_gss_init_sec_context
OM_uint32* /* time_rec */
);
+OM_uint32 krb5_gss_init_sec_context_ext
+(OM_uint32*, /* minor_status */
+ gss_cred_id_t, /* claimant_cred_handle */
+ gss_ctx_id_t*, /* context_handle */
+ gss_name_t, /* target_name */
+ gss_OID, /* mech_type */
+ OM_uint32, /* req_flags */
+ OM_uint32, /* time_req */
+ gss_channel_bindings_t,
+ /* input_chan_bindings */
+ gss_buffer_t, /* input_token */
+ gss_OID*, /* actual_mech_type */
+ gss_buffer_t, /* output_token */
+ OM_uint32*, /* ret_flags */
+ OM_uint32*, /* time_rec */
+ krb5_gss_ctx_ext_t /* exts */
+);
+
#ifndef LEAN_CLIENT
OM_uint32 krb5_gss_accept_sec_context
(OM_uint32*, /* minor_status */
@@ -544,6 +615,22 @@ OM_uint32 krb5_gss_accept_sec_context
OM_uint32*, /* time_rec */
gss_cred_id_t* /* delegated_cred_handle */
);
+
+OM_uint32 krb5_gss_accept_sec_context_ext
+(OM_uint32*, /* minor_status */
+ gss_ctx_id_t*, /* context_handle */
+ gss_cred_id_t, /* verifier_cred_handle */
+ gss_buffer_t, /* input_token_buffer */
+ gss_channel_bindings_t,
+ /* input_chan_bindings */
+ gss_name_t*, /* src_name */
+ gss_OID*, /* mech_type */
+ gss_buffer_t, /* output_token */
+ OM_uint32*, /* ret_flags */
+ OM_uint32*, /* time_rec */
+ gss_cred_id_t*, /* delegated_cred_handle */
+ krb5_gss_ctx_ext_t/*exts */
+);
#endif /* LEAN_CLIENT */
OM_uint32 krb5_gss_process_context_token
@@ -1084,4 +1171,53 @@ extern void krb5_gss_delete_error_info(void *p);
#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID_LENGTH 10
#define GSS_KRB5_SESSION_KEY_ENCTYPE_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x04"
+/* IAKERB */
+
+OM_uint32
+iakerb_gss_init_sec_context(OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec);
+
+OM_uint32
+iakerb_gss_accept_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handler,
+ gss_cred_id_t verifier_cred_handle,
+ gss_buffer_t input_token,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle);
+
+OM_uint32
+iakerb_gss_delete_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token);
+
+krb5_error_code
+iakerb_make_finished(krb5_context context,
+ krb5_key key,
+ const krb5_data *conv,
+ krb5_data **finished);
+
+krb5_error_code
+iakerb_verify_finished(krb5_context context,
+ krb5_key key,
+ const krb5_data *conv,
+ const krb5_data *finished);
+
+#define KRB5_GSS_EXTS_IAKERB_FINISHED 1
+
#endif /* _GSSAPIP_KRB5_H_ */
diff --git a/src/lib/gssapi/krb5/gssapi_err_krb5.et b/src/lib/gssapi/krb5/gssapi_err_krb5.et
index 4cfd68a00..6396d5902 100644
--- a/src/lib/gssapi/krb5/gssapi_err_krb5.et
+++ b/src/lib/gssapi/krb5/gssapi_err_krb5.et
@@ -38,4 +38,5 @@ error_code KG_EMPTY_CCACHE, "Credential cache is empty"
error_code KG_NO_CTYPES, "Acceptor and Initiator share no checksum types"
error_code KG_LUCID_VERSION, "Requested lucid context version not supported"
error_code KG_INPUT_TOO_LONG, "PRF input too long"
+error_code KG_IAKERB_CONTEXT, "Bad magic number for iakerb_ctx_id_t"
end
diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c
index 9e5ba76d9..c902c3dcc 100644
--- a/src/lib/gssapi/krb5/gssapi_krb5.c
+++ b/src/lib/gssapi/krb5/gssapi_krb5.c
@@ -127,34 +127,36 @@ const gss_OID_desc krb5_gss_oid_array[] = {
{GSS_MECH_KRB5_OLD_OID_LENGTH, GSS_MECH_KRB5_OLD_OID},
/* this is the unofficial, incorrect mech OID emitted by MS */
{GSS_MECH_KRB5_WRONG_OID_LENGTH, GSS_MECH_KRB5_WRONG_OID},
+ /* IAKERB OID */
+ {GSS_MECH_IAKERB_OID_LENGTH, GSS_MECH_IAKERB_OID},
/* this is the v2 assigned OID */
{9, "\052\206\110\206\367\022\001\002\003"},
/* these two are name type OID's */
-
/* 2.1.1. Kerberos Principal Name Form: (rfc 1964)
* This name form shall be represented by the Object Identifier {iso(1)
* member-body(2) United States(840) mit(113554) infosys(1) gssapi(2)
* krb5(2) krb5_name(1)}. The recommended symbolic name for this type
* is "GSS_KRB5_NT_PRINCIPAL_NAME". */
{10, "\052\206\110\206\367\022\001\002\002\001"},
-
/* gss_nt_krb5_principal. Object identifier for a krb5_principal. Do not use. */
{10, "\052\206\110\206\367\022\001\002\002\002"},
-
{ 0, 0 }
};
const gss_OID_desc * const gss_mech_krb5 = krb5_gss_oid_array+0;
const gss_OID_desc * const gss_mech_krb5_old = krb5_gss_oid_array+1;
const gss_OID_desc * const gss_mech_krb5_wrong = krb5_gss_oid_array+2;
-const gss_OID_desc * const gss_nt_krb5_name = krb5_gss_oid_array+4;
-const gss_OID_desc * const gss_nt_krb5_principal = krb5_gss_oid_array+5;
-const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+4;
+const gss_OID_desc * const gss_mech_iakerb = krb5_gss_oid_array+3;
+
+
+const gss_OID_desc * const gss_nt_krb5_name = krb5_gss_oid_array+5;
+const gss_OID_desc * const gss_nt_krb5_principal = krb5_gss_oid_array+6;
+const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME = krb5_gss_oid_array+5;
static const gss_OID_set_desc oidsets[] = {
- {1, (gss_OID) krb5_gss_oid_array+0},
- {1, (gss_OID) krb5_gss_oid_array+1},
- {3, (gss_OID) krb5_gss_oid_array+0},
+ {1, (gss_OID) krb5_gss_oid_array+0}, /* RFC OID */
+ {1, (gss_OID) krb5_gss_oid_array+1}, /* pre-RFC OID */
+ {4, (gss_OID) krb5_gss_oid_array+0}, /* includes wrong OID & IAKERB */
{1, (gss_OID) krb5_gss_oid_array+2},
{3, (gss_OID) krb5_gss_oid_array+0},
};
@@ -697,18 +699,48 @@ static struct gss_config krb5_mechanism = {
NULL, /* set_neg_mechs */
};
+static struct gss_config_ext krb5_mechanism_ext = {
+ krb5_gss_acquire_cred_with_password,
+};
+
+static struct gss_config_ext iakerb_mechanism_ext = {
+ iakerb_gss_acquire_cred_with_password,
+};
#ifdef _GSS_STATIC_LINK
#include "mglueP.h"
+static int gss_iakerbmechglue_init(void)
+{
+ struct gss_mech_config mech_iakerb;
+ struct gss_config iakerb_mechanism = krb5_mechanism;
+
+ /* IAKERB mechanism mirrors krb5, but with different context SPIs */
+ iakerb_mechanism.gss_accept_sec_context = iakerb_gss_accept_sec_context;
+ iakerb_mechanism.gss_init_sec_context = iakerb_gss_init_sec_context;
+ iakerb_mechanism.gss_delete_sec_context = iakerb_gss_delete_sec_context;
+ iakerb_mechanism.gss_acquire_cred = iakerb_gss_acquire_cred;
+
+ memset(&mech_iakerb, 0, sizeof(mech_iakerb));
+ mech_iakerb.mech = &iakerb_mechanism;
+ mech_iakerb.mech_ext = &iakerb_mechanism_ext;
+
+ mech_iakerb.mechNameStr = "iakerb";
+ mech_iakerb.mech_type = (gss_OID)gss_mech_iakerb;
+ gssint_register_mechinfo(&mech_iakerb);
+
+ return 0;
+}
+
static int gss_krb5mechglue_init(void)
{
struct gss_mech_config mech_krb5;
memset(&mech_krb5, 0, sizeof(mech_krb5));
mech_krb5.mech = &krb5_mechanism;
+ mech_krb5.mech_ext = &krb5_mechanism_ext;
+
mech_krb5.mechNameStr = "kerberos_v5";
mech_krb5.mech_type = (gss_OID)gss_mech_krb5;
-
gssint_register_mechinfo(&mech_krb5);
mech_krb5.mechNameStr = "kerberos_v5_old";
@@ -769,6 +801,9 @@ int gss_krb5int_lib_init(void)
err = gss_krb5mechglue_init();
if (err)
return err;
+ err = gss_iakerbmechglue_init();
+ if (err)
+ return err;
#endif
return 0;
diff --git a/src/lib/gssapi/krb5/gssapi_krb5.hin b/src/lib/gssapi/krb5/gssapi_krb5.hin
index 783387a52..ce96454fe 100644
--- a/src/lib/gssapi/krb5/gssapi_krb5.hin
+++ b/src/lib/gssapi/krb5/gssapi_krb5.hin
@@ -75,6 +75,7 @@ GSS_DLLIMP extern const gss_OID_desc * const GSS_KRB5_NT_PRINCIPAL_NAME;
GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5;
GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5_old;
GSS_DLLIMP extern const gss_OID_desc * const gss_mech_krb5_wrong;
+GSS_DLLIMP extern const gss_OID_desc * const gss_mech_iakerb;
GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5;
GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5_old;
GSS_DLLIMP extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c
new file mode 100644
index 000000000..3463a7f71
--- /dev/null
+++ b/src/lib/gssapi/krb5/iakerb.c
@@ -0,0 +1,1046 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+#include "k5-int.h"
+#include "gssapiP_krb5.h"
+
+/*
+ * IAKERB implementation
+ */
+
+extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*);
+
+enum iakerb_state {
+ IAKERB_AS_REQ, /* acquiring ticket with initial creds */
+ IAKERB_TGS_REQ, /* acquiring ticket with TGT */
+ IAKERB_AP_REQ /* hand-off to normal GSS AP-REQ exchange */
+};
+
+struct _iakerb_ctx_id_rec {
+ krb5_magic magic; /* KG_IAKERB_CONTEXT */
+ krb5_context k5c;
+ enum iakerb_state state; /* Initiator only */
+ krb5_init_creds_context icc; /* Initiator only */
+ krb5_tkt_creds_context tcc; /* Initiator only */
+ gss_ctx_id_t gssc;
+ krb5_data conv; /* conversation for checksumming */
+ unsigned int count; /* number of round trips */
+ krb5_get_init_creds_opt *gic_opts;
+};
+
+#define IAKERB_MAX_HOPS ( 16 /* MAX_IN_TKT_LOOPS */ + KRB5_REFERRAL_MAXHOPS )
+
+typedef struct _iakerb_ctx_id_rec iakerb_ctx_id_rec;
+typedef iakerb_ctx_id_rec *iakerb_ctx_id_t;
+
+/*
+ * Release an IAKERB context
+ */
+static void
+iakerb_release_context(iakerb_ctx_id_t ctx)
+{
+ OM_uint32 tmp;
+
+ if (ctx == NULL)
+ return;
+
+ krb5_init_creds_free(ctx->k5c, ctx->icc);
+ krb5_tkt_creds_free(ctx->k5c, ctx->tcc);
+ krb5_gss_delete_sec_context(&tmp, &ctx->gssc, NULL);
+ krb5_free_data_contents(ctx->k5c, &ctx->conv);
+ krb5_get_init_creds_opt_free(ctx->k5c, ctx->gic_opts);
+ krb5_free_context(ctx->k5c);
+ free(ctx);
+}
+
+/*
+ * Create a IAKERB-FINISHED structure containing a checksum of
+ * the entire IAKERB exchange.
+ */
+krb5_error_code
+iakerb_make_finished(krb5_context context,
+ krb5_key key,
+ const krb5_data *conv,
+ krb5_data **finished)
+{
+ krb5_error_code code;
+ krb5_iakerb_finished iaf;
+
+ *finished = NULL;
+
+ memset(&iaf, 0, sizeof(iaf));
+
+ if (key == NULL)
+ return KRB5KDC_ERR_NULL_KEY;
+
+ code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_IAKERB_FINISHED,
+ conv, &iaf.checksum);
+ if (code != 0)
+ return code;
+
+ code = encode_krb5_iakerb_finished(&iaf, finished);
+
+ krb5_free_checksum_contents(context, &iaf.checksum);
+
+ return code;
+}
+
+/*
+ * Verify a IAKERB-FINISHED structure submitted by the initiator
+ */
+krb5_error_code
+iakerb_verify_finished(krb5_context context,
+ krb5_key key,
+ const krb5_data *conv,
+ const krb5_data *finished)
+{
+ krb5_error_code code;
+ krb5_iakerb_finished *iaf;
+ krb5_boolean valid = FALSE;
+
+ if (key == NULL)
+ return KRB5KDC_ERR_NULL_KEY;
+
+ code = decode_krb5_iakerb_finished(finished, &iaf);
+ if (code != 0)
+ return code;
+
+ code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_IAKERB_FINISHED,
+ conv, &iaf->checksum, &valid);
+ if (code == 0 && valid == FALSE)
+ code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+
+ krb5_free_iakerb_finished(context, iaf);
+
+ return code;
+}
+
+/*
+ * Save a token for future checksumming.
+ */
+static krb5_error_code
+iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token)
+{
+ char *p;
+
+ p = realloc(ctx->conv.data, ctx->conv.length + token->length);
+ if (p == NULL)
+ return ENOMEM;
+
+ memcpy(p + ctx->conv.length, token->value, token->length);
+ ctx->conv.data = p;
+ ctx->conv.length += token->length;
+
+ return 0;
+}
+
+/*
+ * Parse a token into IAKERB-HEADER and KRB-KDC-REQ/REP
+ */
+static krb5_error_code
+iakerb_parse_token(iakerb_ctx_id_t ctx,
+ int initialContextToken,
+ const gss_buffer_t token,
+ krb5_data *realm,
+ krb5_data **cookie,
+ krb5_data *request)
+{
+ krb5_error_code code;
+ krb5_iakerb_header *iah = NULL;
+ unsigned int bodysize, lenlen;
+ int length;
+ unsigned char *ptr;
+ int flags = 0;
+ krb5_data data;
+
+ if (token == GSS_C_NO_BUFFER || token->length == 0) {
+ code = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+
+ if (initialContextToken)
+ flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED;
+
+ ptr = token->value;
+
+ code = g_verify_token_header(gss_mech_iakerb,
+ &bodysize, &ptr,
+ IAKERB_TOK_PROXY,
+ token->length, flags);
+ if (code != 0)
+ goto cleanup;
+
+ data.data = (char *)ptr;
+
+ if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) {
+ code = ASN1_BAD_ID;
+ goto cleanup;
+ }
+
+ length = gssint_get_der_length(&ptr, bodysize, &lenlen);
+ if (length < 0 || bodysize - lenlen < (unsigned int)length) {
+ code = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+ data.length = 1 /* SEQUENCE */ + lenlen + length;
+
+ ptr += length;
+ bodysize -= (lenlen + length);
+
+ code = decode_krb5_iakerb_header(&data, &iah);
+ if (code != 0)
+ goto cleanup;
+
+ if (realm != NULL) {
+ *realm = iah->target_realm;
+ iah->target_realm.data = NULL;
+ }
+
+ if (cookie != NULL) {
+ *cookie = iah->cookie;
+ iah->cookie = NULL;
+ }
+
+ request->data = (char *)ptr;
+ request->length = bodysize;
+
+ assert(request->data + request->length ==
+ (char *)token->value + token->length);
+
+cleanup:
+ krb5_free_iakerb_header(ctx->k5c, iah);
+
+ return code;
+}
+
+/*
+ * Create a token from IAKERB-HEADER and KRB-KDC-REQ/REP
+ */
+static krb5_error_code
+iakerb_make_token(iakerb_ctx_id_t ctx,
+ krb5_data *realm,
+ krb5_data *cookie,
+ krb5_data *request,
+ int initialContextToken,
+ gss_buffer_t token)
+{
+ krb5_error_code code;
+ krb5_iakerb_header iah;
+ krb5_data *data = NULL;
+ char *p;
+ unsigned int tokenSize;
+ unsigned char *q;
+
+ token->value = NULL;
+ token->length = 0;
+
+ /*
+ * Assemble the IAKERB-HEADER from the realm and cookie
+ */
+ memset(&iah, 0, sizeof(iah));
+ iah.target_realm = *realm;
+ iah.cookie = cookie;
+
+ code = encode_krb5_iakerb_header(&iah, &data);
+ if (code != 0)
+ goto cleanup;
+
+ /*
+ * Concatenate Kerberos request.
+ */
+ p = realloc(data->data, data->length + request->length);
+ if (p == NULL) {
+ code = ENOMEM;
+ goto cleanup;
+ }
+ data->data = p;
+
+ memcpy(data->data + data->length, request->data, request->length);
+ data->length += request->length;
+
+ if (initialContextToken)
+ tokenSize = g_token_size(gss_mech_iakerb, data->length);
+ else
+ tokenSize = 2 + data->length;
+
+ token->value = q = k5alloc(tokenSize, &code);
+ if (code != 0)
+ goto cleanup;
+ token->length = tokenSize;
+
+ if (initialContextToken) {
+ g_make_token_header(gss_mech_iakerb, data->length, &q,
+ IAKERB_TOK_PROXY);
+ } else {
+ store_16_be(IAKERB_TOK_PROXY, q);
+ q += 2;
+ }
+ memcpy(q, data->data, data->length);
+ q += data->length;
+
+ assert(q == (unsigned char *)token->value + token->length);
+
+cleanup:
+ krb5_free_data(ctx->k5c, data);
+
+ return code;
+}
+
+/*
+ * Parse the IAKERB token in input_token and send the contained KDC
+ * request to the KDC for the realm.
+ *
+ * Wrap the KDC reply in output_token.
+ */
+static krb5_error_code
+iakerb_acceptor_step(iakerb_ctx_id_t ctx,
+ int initialContextToken,
+ const gss_buffer_t input_token,
+ gss_buffer_t output_token)
+{
+ krb5_error_code code;
+ krb5_data request = empty_data(), reply = empty_data();
+ krb5_data realm = empty_data();
+ OM_uint32 tmp;
+ int tcp_only, use_master;
+ krb5_ui_4 kdc_code;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ if (ctx->count >= IAKERB_MAX_HOPS) {
+ code = KRB5_KDC_UNREACH;
+ goto cleanup;
+ }
+
+ code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm,
+ NULL, &request);
+ if (code != 0)
+ goto cleanup;
+
+ if (realm.length == 0 || request.length == 0) {
+ code = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
+
+ code = iakerb_save_token(ctx, input_token);
+ if (code != 0)
+ goto cleanup;
+
+ for (tcp_only = 0; tcp_only <= 1; tcp_only++) {
+ use_master = 0;
+ code = krb5_sendto_kdc(ctx->k5c, &request, &realm,
+ &reply, &use_master, tcp_only);
+ if (code == 0 && krb5_is_krb_error(&reply)) {
+ krb5_error *error;
+
+ code = decode_krb5_error(&reply, &error);
+ if (code != 0)
+ goto cleanup;
+ kdc_code = error->error;
+ krb5_free_error(ctx->k5c, error);
+ if (kdc_code == KRB_ERR_RESPONSE_TOO_BIG) {
+ krb5_free_data_contents(ctx->k5c, &reply);
+ reply = empty_data();
+ continue;
+ }
+ }
+ break;
+ }
+
+ if (code == KRB5_KDC_UNREACH || code == KRB5_REALM_UNKNOWN) {
+ krb5_error error;
+
+ memset(&error, 0, sizeof(error));
+ if (code == KRB5_KDC_UNREACH)
+ error.error = KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE;
+ else if (code == KRB5_REALM_UNKNOWN)
+ error.error = KRB_AP_ERR_IAKERB_KDC_NOT_FOUND;
+
+ code = krb5_mk_error(ctx->k5c, &error, &reply);
+ if (code != 0)
+ goto cleanup;
+ } else if (code != 0)
+ goto cleanup;
+
+ code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token);
+ if (code != 0)
+ goto cleanup;
+
+ code = iakerb_save_token(ctx, output_token);
+ if (code != 0)
+ goto cleanup;
+
+ ctx->count++;
+
+cleanup:
+ if (code != 0)
+ gss_release_buffer(&tmp, output_token);
+ /* request is a pointer into input_token, no need to free */
+ krb5_free_data_contents(ctx->k5c, &realm);
+ krb5_free_data_contents(ctx->k5c, &reply);
+
+ return code;
+}
+
+/*
+ * Initialise the krb5_init_creds context for the IAKERB context
+ */
+static krb5_error_code
+iakerb_init_creds_ctx(iakerb_ctx_id_t ctx,
+ krb5_gss_cred_id_t cred,
+ OM_uint32 time_req)
+{
+ krb5_error_code code;
+
+ if (cred->iakerb_mech == 0 || cred->password.data == NULL) {
+ code = EINVAL;
+ goto cleanup;
+ }
+
+ assert(cred->name != NULL);
+ assert(cred->name->princ != NULL);
+
+ code = krb5_get_init_creds_opt_alloc(ctx->k5c, &ctx->gic_opts);
+ if (code != 0)
+ goto cleanup;
+
+ if (time_req != 0 && time_req != GSS_C_INDEFINITE)
+ krb5_get_init_creds_opt_set_tkt_life(ctx->gic_opts, time_req);
+
+ code = krb5_get_init_creds_opt_set_out_ccache(ctx->k5c, ctx->gic_opts,
+ cred->ccache);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5_init_creds_init(ctx->k5c,
+ cred->name->princ,
+ NULL, /* prompter */
+ NULL, /* data */
+ 0, /* start_time */
+ ctx->gic_opts,
+ &ctx->icc);
+ if (code != 0)
+ goto cleanup;
+
+ code = krb5_init_creds_set_password(ctx->k5c, ctx->icc,
+ cred->password.data);
+ if (code != 0)
+ goto cleanup;
+
+cleanup:
+ return code;
+}
+
+/*
+ * Initialise the krb5_tkt_creds context for the IAKERB context
+ */
+static krb5_error_code
+iakerb_tkt_creds_ctx(iakerb_ctx_id_t ctx,
+ krb5_gss_cred_id_t cred,
+ krb5_gss_name_t name,
+ OM_uint32 time_req)
+
+{
+ krb5_error_code code;
+ krb5_creds creds;
+ krb5_timestamp now;
+
+ assert(cred->name != NULL);
+ assert(cred->name->princ != NULL);
+
+ memset(&creds, 0, sizeof(creds));
+
+ creds.client = cred->name->princ;
+ creds.server = name->princ;
+
+ if (time_req != 0 && time_req != GSS_C_INDEFINITE) {
+ code = krb5_timeofday(ctx->k5c, &now);
+ if (code != 0)
+ goto cleanup;
+
+ creds.times.endtime = now + time_req;
+ }
+
+ if (cred->name->ad_context != NULL) {
+ code = krb5_authdata_export_authdata(ctx->k5c,
+ cred->name->ad_context,
+ AD_USAGE_TGS_REQ,
+ &creds.authdata);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ code = krb5_tkt_creds_init(ctx->k5c, cred->ccache, &creds, 0, &ctx->tcc);
+ if (code != 0)
+ goto cleanup;
+
+cleanup:
+ krb5_free_authdata(ctx->k5c, creds.authdata);
+
+ return code;
+}
+
+/*
+ * Parse the IAKERB token in input_token and process the KDC
+ * response.
+ *
+ * Emit the next KDC request, if any, in output_token.
+ */
+static krb5_error_code
+iakerb_initiator_step(iakerb_ctx_id_t ctx,
+ krb5_gss_cred_id_t cred,
+ krb5_gss_name_t name,
+ OM_uint32 time_req,
+ const gss_buffer_t input_token,
+ gss_buffer_t output_token)
+{
+ krb5_error_code code;
+ krb5_data in, out, realm, *cookie = NULL;
+ OM_uint32 tmp;
+ int initialContextToken = (input_token == GSS_C_NO_BUFFER);
+ unsigned int flags = 0;
+ krb5_ticket_times times;
+
+ output_token->length = 0;
+ output_token->value = NULL;
+
+ in.data = NULL;
+ in.length = 0;
+ out.data = NULL;
+ out.length = 0;
+ realm.data = NULL;
+ realm.length = 0;
+
+ if (initialContextToken) {
+ in.data = NULL;
+ in.length = 0;
+ } else {
+ code = iakerb_parse_token(ctx,
+ 0,
+ input_token,
+ NULL,
+ &cookie,
+ &in);
+ if (code != 0)
+ goto cleanup;
+
+ code = iakerb_save_token(ctx, input_token);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ switch (ctx->state) {
+ case IAKERB_AS_REQ:
+ if (ctx->icc == NULL) {
+ code = iakerb_init_creds_ctx(ctx, cred, time_req);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ code = krb5_init_creds_step(ctx->k5c, ctx->icc, &in, &out, &realm,
+ &flags);
+ if (code != 0)
+ goto cleanup;
+ if (!(flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) {
+ krb5_init_creds_get_times(ctx->k5c, ctx->icc, &times);
+ cred->tgt_expire = times.endtime;
+
+ krb5_init_creds_free(ctx->k5c, ctx->icc);
+ ctx->icc = NULL;
+
+ ctx->state = IAKERB_TGS_REQ;
+ } else
+ break;
+ in = empty_data();
+ case IAKERB_TGS_REQ:
+ if (ctx->tcc == NULL) {
+ code = iakerb_tkt_creds_ctx(ctx, cred, name, time_req);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ code = krb5_tkt_creds_step(ctx->k5c, ctx->tcc, &in, &out, &realm,
+ &flags);
+ if (code != 0)
+ goto cleanup;
+ if (!(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE)) {
+ krb5_tkt_creds_get_times(ctx->k5c, ctx->tcc, &times);
+ cred->tgt_expire = times.endtime;
+
+ krb5_tkt_creds_free(ctx->k5c, ctx->tcc);
+ ctx->tcc = NULL;
+
+ ctx->state = IAKERB_AP_REQ;
+ } else
+ break;
+ case IAKERB_AP_REQ:
+ break;
+ }
+
+ if (out.length != 0) {
+ assert(ctx->state != IAKERB_AP_REQ);
+
+ code = iakerb_make_token(ctx, &realm, cookie, &out,
+ (input_token == GSS_C_NO_BUFFER),
+ output_token);
+ if (code != 0)
+ goto cleanup;
+
+ /* Save the token for generating a future checksum */
+ code = iakerb_save_token(ctx, output_token);
+ if (code != 0)
+ goto cleanup;
+
+ ctx->count++;
+ }
+
+cleanup:
+ if (code != 0)
+ gss_release_buffer(&tmp, output_token);
+ krb5_free_data(ctx->k5c, cookie);
+ krb5_free_data_contents(ctx->k5c, &out);
+ krb5_free_data_contents(ctx->k5c, &realm);
+
+ return code;
+}
+
+/*
+ * Determine the starting IAKERB state for a context. If we already
+ * have a ticket, we may not need to do IAKERB at all.
+ */
+static krb5_error_code
+iakerb_get_initial_state(iakerb_ctx_id_t ctx,
+ krb5_gss_cred_id_t cred,
+ krb5_gss_name_t target,
+ OM_uint32 time_req,
+ enum iakerb_state *state)
+{
+ krb5_creds in_creds, *out_creds = NULL;
+ krb5_error_code code;
+
+ memset(&in_creds, 0, sizeof(in_creds));
+
+ in_creds.client = cred->name->princ;
+ in_creds.server = target->princ;
+
+ if (cred->name->ad_context != NULL) {
+ code = krb5_authdata_export_authdata(ctx->k5c,
+ cred->name->ad_context,
+ AD_USAGE_TGS_REQ,
+ &in_creds.authdata);
+ if (code != 0)
+ goto cleanup;
+ }
+
+ if (time_req != 0 && time_req != GSS_C_INDEFINITE) {
+ krb5_timestamp now;
+
+ code = krb5_timeofday(ctx->k5c, &now);
+ if (code != 0)
+ goto cleanup;
+
+ in_creds.times.endtime = now + time_req;
+ }
+
+ code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED,
+ cred->ccache,
+ &in_creds, &out_creds);
+ if (code == KRB5_CC_NOTFOUND || code == KRB5_CC_NOT_KTYPE) {
+ krb5_principal tgs;
+ krb5_data *realm = krb5_princ_realm(ctx->k5c, in_creds.client);
+
+ /* If we have a TGT for the client realm, can proceed to TGS-REQ. */
+ code = krb5_build_principal_ext(ctx->k5c,
+ &tgs,
+ realm->length,
+ realm->data,
+ KRB5_TGS_NAME_SIZE,
+ KRB5_TGS_NAME,
+ realm->length,
+ realm->data,
+ NULL);
+ if (code != 0)
+ goto cleanup;
+
+ in_creds.server = tgs;
+
+ /* It would be nice if we could return KRB5KRB_AP_ERR_TKT_EXPIRED if
+ * the TGT is expired, for consistency with the krb5 mech. As it
+ * stands, we won't see the expired TGT and will return
+ * KRB5_CC_NOTFOUND. */
+ code = krb5_get_credentials(ctx->k5c, KRB5_GC_CACHED,
+ cred->ccache,
+ &in_creds, &out_creds);
+ if (code == KRB5_CC_NOTFOUND && cred->password.data != NULL) {
+ *state = IAKERB_AS_REQ;
+ code = 0;
+ } else if (code == 0) {
+ *state = IAKERB_TGS_REQ;
+ krb5_free_creds(ctx->k5c, out_creds);
+ }
+ krb5_free_principal(ctx->k5c, tgs);
+ } else if (code == 0) {
+ *state = IAKERB_AP_REQ;
+ krb5_free_creds(ctx->k5c, out_creds);
+ }
+
+cleanup:
+ krb5_free_authdata(ctx->k5c, in_creds.authdata);
+
+ return code;
+}
+
+/*
+ * Allocate and initialise an IAKERB context
+ */
+static krb5_error_code
+iakerb_alloc_context(iakerb_ctx_id_t *pctx)
+{
+ iakerb_ctx_id_t ctx;
+ krb5_error_code code;
+
+ *pctx = NULL;
+
+ ctx = k5alloc(sizeof(*ctx), &code);
+ if (ctx == NULL)
+ goto cleanup;
+ ctx->magic = KG_IAKERB_CONTEXT;
+ ctx->state = IAKERB_AS_REQ;
+ ctx->count = 0;
+
+ code = krb5_gss_init_context(&ctx->k5c);
+ if (code != 0)
+ goto cleanup;
+
+ *pctx = ctx;
+
+cleanup:
+ if (code != 0)
+ iakerb_release_context(ctx);
+
+ return code;
+}
+
+/*
+ * Delete an IAKERB context. This can also accept Kerberos context
+ * handles. The heuristic is similar to SPNEGO's delete_sec_context.
+ */
+OM_uint32
+iakerb_gss_delete_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token)
+{
+ OM_uint32 major_status = GSS_S_COMPLETE;
+
+ if (output_token != GSS_C_NO_BUFFER) {
+ output_token->length = 0;
+ output_token->value = NULL;
+ }
+
+ *minor_status = 0;
+
+ if (*context_handle != GSS_C_NO_CONTEXT) {
+ iakerb_ctx_id_t iakerb_ctx = (iakerb_ctx_id_t)*context_handle;
+
+ if (iakerb_ctx->magic == KG_IAKERB_CONTEXT) {
+ iakerb_release_context(iakerb_ctx);
+ *context_handle = GSS_C_NO_CONTEXT;
+ } else {
+ assert(iakerb_ctx->magic == KG_CONTEXT);
+
+ major_status = krb5_gss_delete_sec_context(minor_status,
+ context_handle,
+ output_token);
+ }
+ }
+
+ return major_status;
+}
+
+static krb5_boolean
+iakerb_is_iakerb_token(const gss_buffer_t token)
+{
+ krb5_error_code code;
+ unsigned int bodysize = token->length;
+ unsigned char *ptr = token->value;
+
+ code = g_verify_token_header(gss_mech_iakerb,
+ &bodysize, &ptr,
+ IAKERB_TOK_PROXY,
+ token->length, 0);
+
+ return (code == 0);
+}
+
+static void
+iakerb_make_exts(iakerb_ctx_id_t ctx, krb5_gss_ctx_ext_rec *exts)
+{
+ memset(exts, 0, sizeof(*exts));
+
+ if (ctx->conv.length != 0)
+ exts->iakerb.conv = &ctx->conv;
+}
+
+/*
+ *
+ */
+OM_uint32
+iakerb_gss_accept_sec_context(OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t verifier_cred_handle,
+ gss_buffer_t input_token,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,
+ gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ OM_uint32 major_status = GSS_S_FAILURE;
+ OM_uint32 code;
+ iakerb_ctx_id_t ctx;
+ int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT);
+
+ if (initialContextToken) {
+ code = iakerb_alloc_context(&ctx);
+ if (code != 0)
+ goto cleanup;
+
+ } else
+ ctx = (iakerb_ctx_id_t)*context_handle;
+
+ if (iakerb_is_iakerb_token(input_token)) {
+ if (ctx->gssc != GSS_C_NO_CONTEXT) {
+ /* We shouldn't get an IAKERB token now. */
+ code = G_WRONG_TOKID;
+ major_status = GSS_S_DEFECTIVE_TOKEN;
+ goto cleanup;
+ }
+ code = iakerb_acceptor_step(ctx, initialContextToken,
+ input_token, output_token);
+ if (code == (OM_uint32)KRB5_BAD_MSIZE)
+ major_status = GSS_S_DEFECTIVE_TOKEN;
+ if (code != 0)
+ goto cleanup;
+ if (initialContextToken) {
+ *context_handle = (gss_ctx_id_t)ctx;
+ ctx = NULL;
+ }
+ if (src_name != NULL)
+ *src_name = GSS_C_NO_NAME;
+ if (mech_type != NULL)
+ *mech_type = (gss_OID)gss_mech_iakerb;
+ if (ret_flags != NULL)
+ *ret_flags = 0;
+ if (time_rec != NULL)
+ *time_rec = 0;
+ if (delegated_cred_handle != NULL)
+ *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
+ major_status = GSS_S_CONTINUE_NEEDED;
+ } else {
+ krb5_gss_ctx_ext_rec exts;
+
+ iakerb_make_exts(ctx, &exts);
+
+ major_status = krb5_gss_accept_sec_context_ext(&code,
+ &ctx->gssc,
+ verifier_cred_handle,
+ input_token,
+ input_chan_bindings,
+ src_name,
+ mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ delegated_cred_handle,
+ &exts);
+ if (major_status == GSS_S_COMPLETE) {
+ *context_handle = ctx->gssc;
+ ctx->gssc = NULL;
+ iakerb_release_context(ctx);
+ }
+ }
+
+cleanup:
+ if (initialContextToken && GSS_ERROR(major_status)) {
+ iakerb_release_context(ctx);
+ *context_handle = GSS_C_NO_CONTEXT;
+ }
+
+ *minor_status = code;
+ return major_status;
+}
+
+OM_uint32
+iakerb_gss_init_sec_context(OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 major_status = GSS_S_FAILURE;
+ OM_uint32 tmpmin;
+ krb5_error_code code;
+ iakerb_ctx_id_t ctx;
+ gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
+ krb5_gss_cred_id_t kcred;
+ krb5_gss_name_t kname;
+ int credLocked = 0;
+ int initialContextToken = (*context_handle == GSS_C_NO_CONTEXT);
+
+ if (initialContextToken) {
+ code = iakerb_alloc_context(&ctx);
+ if (code != 0) {
+ *minor_status = code;
+ goto cleanup;
+ }
+ } else
+ ctx = (iakerb_ctx_id_t)*context_handle;
+
+ if (!kg_validate_name(target_name)) {
+ *minor_status = G_VALIDATE_FAILED;
+ major_status = GSS_S_CALL_BAD_STRUCTURE | GSS_S_BAD_NAME;
+ goto cleanup;
+ }
+
+ kname = (krb5_gss_name_t)target_name;
+
+ if (claimant_cred_handle != GSS_C_NO_CREDENTIAL) {
+ major_status = krb5_gss_validate_cred_1(minor_status,
+ claimant_cred_handle,
+ ctx->k5c);
+ if (GSS_ERROR(major_status))
+ goto cleanup;
+
+ credLocked = 1;
+ kcred = (krb5_gss_cred_id_t)claimant_cred_handle;
+ } else {
+ major_status = iakerb_gss_acquire_cred(minor_status, NULL,
+ GSS_C_INDEFINITE,
+ GSS_C_NULL_OID_SET,
+ GSS_C_INITIATE,
+ &defcred, NULL, NULL);
+ if (GSS_ERROR(major_status))
+ goto cleanup;
+ kcred = (krb5_gss_cred_id_t)defcred;
+ }
+
+ major_status = GSS_S_FAILURE;
+
+ if (initialContextToken) {
+ code = iakerb_get_initial_state(ctx, kcred, kname, time_req,
+ &ctx->state);
+ if (code != 0) {
+ *minor_status = code;
+ goto cleanup;
+ }
+ *context_handle = (gss_ctx_id_t)ctx;
+ }
+
+ if (ctx->state != IAKERB_AP_REQ) {
+ /* We need to do IAKERB. */
+ code = iakerb_initiator_step(ctx,
+ kcred,
+ kname,
+ time_req,
+ input_token,
+ output_token);
+ if (code == KRB5_BAD_MSIZE)
+ major_status = GSS_S_DEFECTIVE_TOKEN;
+ if (code != 0) {
+ *minor_status = code;
+ goto cleanup;
+ }
+ }
+
+ if (ctx->state == IAKERB_AP_REQ) {
+ krb5_gss_ctx_ext_rec exts;
+
+ /* Ensure cred is marked as usable for Kerberos mechanism */
+ if (kcred != NULL) {
+ if (kcred->iakerb_mech)
+ kcred->rfc_mech = 1;
+
+ k5_mutex_unlock(&kcred->lock);
+ credLocked = 0;
+ }
+
+ iakerb_make_exts(ctx, &exts);
+
+ if (ctx->gssc == GSS_C_NO_CONTEXT)
+ input_token = GSS_C_NO_BUFFER;
+
+ /* IAKERB is finished, or we skipped to Kerberos directly. */
+ major_status = krb5_gss_init_sec_context_ext(minor_status,
+ (gss_cred_id_t) kcred,
+ &ctx->gssc,
+ target_name,
+ GSS_C_NULL_OID,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ &exts);
+ if (major_status == GSS_S_COMPLETE) {
+ *context_handle = ctx->gssc;
+ ctx->gssc = GSS_C_NO_CONTEXT;
+ iakerb_release_context(ctx);
+ }
+ } else {
+ if (actual_mech_type != NULL)
+ *actual_mech_type = (gss_OID)gss_mech_iakerb;
+ if (ret_flags != NULL)
+ *ret_flags = 0;
+ if (time_rec != NULL)
+ *time_rec = 0;
+ major_status = GSS_S_CONTINUE_NEEDED;
+ }
+
+cleanup:
+ if (credLocked)
+ k5_mutex_unlock(&kcred->lock);
+ if (initialContextToken && GSS_ERROR(major_status)) {
+ iakerb_release_context(ctx);
+ *context_handle = GSS_C_NO_CONTEXT;
+ }
+ krb5_gss_release_cred(&tmpmin, &defcred);
+
+ return major_status;
+}
+
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index b81d5b312..a7bff2f5d 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -197,8 +197,38 @@ static krb5_error_code get_credentials(context, cred, server, now,
goto cleanup;
}
+ /* Don't go out over the network if we used IAKERB */
+ if (cred->iakerb_mech)
+ flags |= KRB5_GC_CACHED;
+
code = krb5_get_credentials(context, flags, cred->ccache,
&in_creds, &result_creds);
+ if (code == KRB5_CC_NOTFOUND && cred->password.data != NULL &&
+ !cred->iakerb_mech) {
+ krb5_creds tgt_creds;
+
+ memset(&tgt_creds, 0, sizeof(tgt_creds));
+
+ /* No TGT in the ccache, but we can get one with the password. */
+ code = krb5_get_init_creds_password(context, &tgt_creds,
+ in_creds.client,
+ cred->password.data,
+ NULL, NULL,
+ 0, NULL, NULL);
+ if (code)
+ goto cleanup;
+
+ code = krb5_cc_store_cred(context, cred->ccache, &tgt_creds);
+ if (code) {
+ krb5_free_cred_contents(context, &tgt_creds);
+ goto cleanup;
+ }
+ cred->tgt_expire = tgt_creds.times.endtime;
+ krb5_free_cred_contents(context, &tgt_creds);
+
+ code = krb5_get_credentials(context, flags, cred->ccache,
+ &in_creds, &result_creds);
+ }
if (code)
goto cleanup;
@@ -237,6 +267,7 @@ struct gss_checksum_data {
krb5_gss_cred_id_t cred;
krb5_checksum md5;
krb5_data checksum_data;
+ krb5_gss_ctx_ext_t exts;
};
#ifdef CFX_EXERCISE
@@ -252,6 +283,7 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
struct gss_checksum_data *data = cksum_data;
krb5_data credmsg;
unsigned int junk;
+ krb5_data *finished = NULL;
data->checksum_data.data = 0;
credmsg.data = 0;
@@ -284,8 +316,8 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
data->checksum_data.length = 24;
} else {
if (credmsg.length+28 > KRB5_INT16_MAX) {
- krb5_free_data_contents(context, &credmsg);
- return(KRB5KRB_ERR_FIELD_TOOLONG);
+ code = KRB5KRB_ERR_FIELD_TOOLONG;
+ goto cleanup;
}
data->checksum_data.length = 28+credmsg.length;
@@ -307,6 +339,26 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
junk = 0;
#endif
+ assert(data->exts != NULL);
+
+ if (data->exts->iakerb.conv) {
+ krb5_key key;
+
+ code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key);
+ if (code != 0)
+ goto cleanup;
+
+ code = iakerb_make_finished(context, key, data->exts->iakerb.conv,
+ &finished);
+ if (code != 0) {
+ krb5_k_free_key(context, key);
+ goto cleanup;
+ }
+
+ krb5_k_free_key(context, key);
+ data->checksum_data.length += 8 + finished->length;
+ }
+
data->checksum_data.length += junk;
/* now allocate a buffer to hold the checksum data and
@@ -314,9 +366,8 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
if ((data->checksum_data.data =
(char *) xmalloc(data->checksum_data.length)) == NULL) {
- if (credmsg.data)
- krb5_free_data_contents(context, &credmsg);
- return(ENOMEM);
+ code = ENOMEM;
+ goto cleanup;
}
ptr = (unsigned char *)data->checksum_data.data;
@@ -332,19 +383,25 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
TWRITE_INT16(ptr, KRB5_GSS_FOR_CREDS_OPTION, 0);
TWRITE_INT16(ptr, credmsg.length, 0);
TWRITE_STR(ptr, credmsg.data, credmsg.length);
-
- /* free credmsg data */
- krb5_free_data_contents(context, &credmsg);
+ }
+ if (data->exts->iakerb.conv) {
+ TWRITE_INT(ptr, KRB5_GSS_EXTS_IAKERB_FINISHED, 1);
+ TWRITE_INT(ptr, finished->length, 1);
+ TWRITE_STR(ptr, finished->data, finished->length);
}
if (junk)
memset(ptr, 'i', junk);
*out = &data->checksum_data;
- return 0;
+ code = 0;
+cleanup:
+ krb5_free_data_contents(context, &credmsg);
+ krb5_free_data(context, finished);
+ return code;
}
static krb5_error_code
make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
- chan_bindings, mech_type, token)
+ chan_bindings, mech_type, token, exts)
krb5_context context;
krb5_gss_ctx_id_rec *ctx;
krb5_gss_cred_id_t cred;
@@ -353,13 +410,13 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
gss_channel_bindings_t chan_bindings;
gss_OID mech_type;
gss_buffer_t token;
+ krb5_gss_ctx_ext_t exts;
{
krb5_flags mk_req_flags = 0;
krb5_error_code code;
struct gss_checksum_data cksum_struct;
krb5_checksum md5;
krb5_data ap_req;
- krb5_data *checksum_data = NULL;
unsigned char *ptr;
unsigned char *t;
unsigned int tlen;
@@ -378,22 +435,9 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
cksum_struct.ctx = ctx;
cksum_struct.cred = cred;
cksum_struct.checksum_data.data = NULL;
- switch (k_cred->keyblock.enctype) {
- case ENCTYPE_DES_CBC_CRC:
- case ENCTYPE_DES_CBC_MD4:
- case ENCTYPE_DES_CBC_MD5:
- case ENCTYPE_DES3_CBC_SHA1:
- code = make_gss_checksum(context, ctx->auth_context, &cksum_struct,
- &checksum_data);
- if (code)
- goto cleanup;
- break;
- default:
- krb5_auth_con_set_checksum_func(context, ctx->auth_context,
- make_gss_checksum, &cksum_struct);
- break;
- }
-
+ cksum_struct.exts = exts;
+ krb5_auth_con_set_checksum_func(context, ctx->auth_context,
+ make_gss_checksum, &cksum_struct);
/* call mk_req. subkey and ap_req need to be used or destroyed */
@@ -404,7 +448,7 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context);
code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
- checksum_data, k_cred, &ap_req);
+ NULL, k_cred, &ap_req);
krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL);
krb5_free_data_contents(context, &cksum_struct.checksum_data);
if (code)
@@ -450,8 +494,6 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
code = 0;
cleanup:
- if (checksum_data && checksum_data->data)
- krb5_free_data_contents(context, checksum_data);
if (ap_req.data)
krb5_free_data_contents(context, &ap_req);
@@ -479,7 +521,8 @@ kg_new_connection(
OM_uint32 *ret_flags,
OM_uint32 *time_rec,
krb5_context context,
- int default_mech)
+ int default_mech,
+ krb5_gss_ctx_ext_t exts)
{
OM_uint32 major_status;
krb5_error_code code;
@@ -519,6 +562,7 @@ kg_new_connection(
/* fill in the ctx */
memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
+ ctx->magic = KG_CONTEXT;
ctx_free = ctx;
if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
goto fail;
@@ -597,7 +641,7 @@ kg_new_connection(
if ((code = make_ap_req_v1(context, ctx,
cred, k_cred, ctx->here->ad_context,
input_chan_bindings,
- mech_type, &token))) {
+ mech_type, &token, exts))) {
if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
(code == KG_EMPTY_CCACHE))
major_status = GSS_S_NO_CRED;
@@ -882,24 +926,21 @@ fail:
}
OM_uint32
-krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
- context_handle, target_name, mech_type,
- req_flags, time_req, input_chan_bindings,
- input_token, actual_mech_type, output_token,
- ret_flags, time_rec)
- OM_uint32 *minor_status;
- gss_cred_id_t claimant_cred_handle;
- gss_ctx_id_t *context_handle;
- gss_name_t target_name;
- gss_OID mech_type;
- OM_uint32 req_flags;
- OM_uint32 time_req;
- gss_channel_bindings_t input_chan_bindings;
- gss_buffer_t input_token;
- gss_OID *actual_mech_type;
- gss_buffer_t output_token;
- OM_uint32 *ret_flags;
- OM_uint32 *time_rec;
+krb5_gss_init_sec_context_ext(
+ OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,
+ gss_OID mech_type,
+ OM_uint32 req_flags,
+ OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,
+ OM_uint32 *time_rec,
+ krb5_gss_ctx_ext_t exts)
{
krb5_context context;
krb5_gss_cred_id_t cred;
@@ -1017,7 +1058,7 @@ krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
time_req, input_chan_bindings,
input_token, actual_mech_type,
output_token, ret_flags, time_rec,
- context, default_mech);
+ context, default_mech, exts);
k5_mutex_unlock(&cred->lock);
if (*context_handle == GSS_C_NO_CONTEXT) {
save_error_info (*minor_status, context);
@@ -1097,3 +1138,44 @@ krb5int_gss_use_kdc_context(OM_uint32 *minor_status,
return GSS_S_COMPLETE;
}
#endif
+
+OM_uint32
+krb5_gss_init_sec_context(minor_status, claimant_cred_handle,
+ context_handle, target_name, mech_type,
+ req_flags, time_req, input_chan_bindings,
+ input_token, actual_mech_type, output_token,
+ ret_flags, time_rec)
+ OM_uint32 *minor_status;
+ gss_cred_id_t claimant_cred_handle;
+ gss_ctx_id_t *context_handle;
+ gss_name_t target_name;
+ gss_OID mech_type;
+ OM_uint32 req_flags;
+ OM_uint32 time_req;
+ gss_channel_bindings_t input_chan_bindings;
+ gss_buffer_t input_token;
+ gss_OID *actual_mech_type;
+ gss_buffer_t output_token;
+ OM_uint32 *ret_flags;
+ OM_uint32 *time_rec;
+{
+ krb5_gss_ctx_ext_rec exts;
+
+ memset(&exts, 0, sizeof(exts));
+
+ return krb5_gss_init_sec_context_ext(minor_status,
+ claimant_cred_handle,
+ context_handle,
+ target_name,
+ mech_type,
+ req_flags,
+ time_req,
+ input_chan_bindings,
+ input_token,
+ actual_mech_type,
+ output_token,
+ ret_flags,
+ time_rec,
+ &exts);
+}
+
diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c
index 9af0e4e85..8083b27cc 100644
--- a/src/lib/gssapi/krb5/inq_cred.c
+++ b/src/lib/gssapi/krb5/inq_cred.c
@@ -166,6 +166,10 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
(cred->rfc_mech &&
GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
gss_mech_krb5,
+ &mechs))) ||
+ (cred->iakerb_mech &&
+ GSS_ERROR(ret = generic_gss_add_oid_set_member(minor_status,
+ gss_mech_iakerb,
&mechs)))) {
k5_mutex_unlock(&cred->lock);
if (ret_name)
diff --git a/src/lib/gssapi/krb5/inq_names.c b/src/lib/gssapi/krb5/inq_names.c
index a3de420cb..19c477f83 100644
--- a/src/lib/gssapi/krb5/inq_names.c
+++ b/src/lib/gssapi/krb5/inq_names.c
@@ -44,7 +44,8 @@ krb5_gss_inquire_names_for_mech(minor_status, mechanism, name_types)
*/
if ((mechanism != GSS_C_NULL_OID) &&
!g_OID_equal(gss_mech_krb5, mechanism) &&
- !g_OID_equal(gss_mech_krb5_old, mechanism)) {
+ !g_OID_equal(gss_mech_krb5_old, mechanism) &&
+ !g_OID_equal(gss_mech_iakerb, mechanism)) {
*minor_status = 0;
return(GSS_S_BAD_MECH);
}
diff --git a/src/lib/gssapi/krb5/rel_cred.c b/src/lib/gssapi/krb5/rel_cred.c
index 05e24b23d..d1c571a2f 100644
--- a/src/lib/gssapi/krb5/rel_cred.c
+++ b/src/lib/gssapi/krb5/rel_cred.c
@@ -77,6 +77,11 @@ krb5_gss_release_cred(minor_status, cred_handle)
if (cred->req_enctypes)
free(cred->req_enctypes);
+ if (cred->password.data) {
+ zap(cred->password.data, cred->password.length);
+ krb5_free_data_contents(context, &cred->password);
+ }
+
xfree(cred);
*cred_handle = NULL;
diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c
index f5c1081cc..63a7bd82a 100644
--- a/src/lib/gssapi/krb5/ser_sctx.c
+++ b/src/lib/gssapi/krb5/ser_sctx.c
@@ -610,6 +610,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
xmalloc(sizeof(krb5_gss_ctx_id_rec)))) {
memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
+ ctx->magic = ibuf;
ctx->k5_context = kcontext;
/* Get static data */
diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports
index e038f5076..5b00fc218 100644
--- a/src/lib/gssapi/libgssapi_krb5.exports
+++ b/src/lib/gssapi/libgssapi_krb5.exports
@@ -9,6 +9,7 @@ GSS_C_NT_USER_NAME
GSS_KRB5_NT_PRINCIPAL_NAME
gss_accept_sec_context
gss_acquire_cred
+gss_acquire_cred_with_password
gss_acquire_cred_impersonate_name
gss_add_buffer_set_member
gss_add_cred
@@ -54,6 +55,7 @@ gss_krb5int_unseal_token_v3
gsskrb5_extract_authtime_from_sec_context
gsskrb5_extract_authz_data_from_sec_context
gss_map_name_to_any
+gss_mech_iakerb
gss_mech_krb5
gss_mech_krb5_old
gss_mech_set_krb5
diff --git a/src/lib/gssapi/mechglue/Makefile.in b/src/lib/gssapi/mechglue/Makefile.in
index f43571145..92cd6c99c 100644
--- a/src/lib/gssapi/mechglue/Makefile.in
+++ b/src/lib/gssapi/mechglue/Makefile.in
@@ -12,6 +12,7 @@ DEFS=-D_GSS_STATIC_LINK=1
SRCS = \
$(srcdir)/g_accept_sec_context.c \
$(srcdir)/g_acquire_cred.c \
+ $(srcdir)/g_acquire_cred_with_pw.c \
$(srcdir)/g_acquire_cred_imp_name.c \
$(srcdir)/g_buffer_set.c \
$(srcdir)/g_canon_name.c \
@@ -67,6 +68,7 @@ SRCS = \
OBJS = \
$(OUTPRE)g_accept_sec_context.$(OBJEXT) \
$(OUTPRE)g_acquire_cred.$(OBJEXT) \
+ $(OUTPRE)g_acquire_cred_with_pw.$(OBJEXT) \
$(OUTPRE)g_acquire_cred_imp_name.$(OBJEXT) \
$(OUTPRE)g_buffer_set.$(OBJEXT) \
$(OUTPRE)g_canon_name.$(OBJEXT) \
@@ -122,6 +124,7 @@ OBJS = \
STLIBOBJS = \
g_accept_sec_context.o \
g_acquire_cred.o \
+ g_acquire_cred_with_pw.o \
g_acquire_cred_imp_name.o \
g_buffer_set.o \
g_canon_name.o \
diff --git a/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c b/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c
new file mode 100644
index 000000000..f86630382
--- /dev/null
+++ b/src/lib/gssapi/mechglue/g_acquire_cred_with_pw.c
@@ -0,0 +1,544 @@
+/* #pragma ident "@(#)g_acquire_cred.c 1.22 04/02/23 SMI" */
+
+/*
+ * Copyright 1996 by Sun Microsystems, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appears in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of Sun Microsystems not be used
+ * in advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. Sun Microsystems makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * glue routine for gss_acquire_cred_with_password
+ */
+
+#include "mglueP.h"
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+static OM_uint32
+val_acq_cred_pw_args(
+ OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ const gss_buffer_t password,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ int cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec)
+{
+
+ /* Initialize outputs. */
+
+ if (minor_status != NULL)
+ *minor_status = 0;
+
+ if (output_cred_handle != NULL)
+ *output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ if (actual_mechs != NULL)
+ *actual_mechs = GSS_C_NULL_OID_SET;
+
+ if (time_rec != NULL)
+ *time_rec = 0;
+
+ /* Validate arguments. */
+
+ if (minor_status == NULL)
+ return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+ if (output_cred_handle == NULL)
+ return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+ if (cred_usage != GSS_C_ACCEPT
+ && cred_usage != GSS_C_INITIATE
+ && cred_usage != GSS_C_BOTH) {
+ if (minor_status) {
+ *minor_status = EINVAL;
+ map_errcode(minor_status);
+ }
+ return GSS_S_FAILURE;
+ }
+
+ if (password == GSS_C_NO_BUFFER ||
+ password->length == 0 ||
+ password->value == NULL) {
+ if (minor_status) {
+ *minor_status = EINVAL;
+ map_errcode(minor_status);
+ }
+ return GSS_S_FAILURE;
+ }
+
+ return (GSS_S_COMPLETE);
+}
+
+
+OM_uint32 KRB5_CALLCONV
+gss_acquire_cred_with_password(
+ minor_status,
+ desired_name,
+ password,
+ time_req,
+ desired_mechs,
+ cred_usage,
+ output_cred_handle,
+ actual_mechs,
+ time_rec)
+
+OM_uint32 * minor_status;
+const gss_name_t desired_name;
+const gss_buffer_t password;
+OM_uint32 time_req;
+const gss_OID_set desired_mechs;
+int cred_usage;
+gss_cred_id_t * output_cred_handle;
+gss_OID_set * actual_mechs;
+OM_uint32 * time_rec;
+{
+ OM_uint32 major = GSS_S_FAILURE;
+ OM_uint32 initTimeOut, acceptTimeOut, outTime = GSS_C_INDEFINITE;
+ gss_OID_set_desc default_OID_set;
+ gss_OID_set mechs;
+ gss_OID_desc default_OID;
+ gss_mechanism mech;
+ unsigned int i;
+ gss_union_cred_t creds;
+
+ major = val_acq_cred_pw_args(minor_status,
+ desired_name,
+ password,
+ time_req,
+ desired_mechs,
+ cred_usage,
+ output_cred_handle,
+ actual_mechs,
+ time_rec);
+ if (major != GSS_S_COMPLETE)
+ return (major);
+
+ /* Initial value needed below. */
+ major = GSS_S_FAILURE;
+
+ /*
+ * if desired_mechs equals GSS_C_NULL_OID_SET, then pick an
+ * appropriate default. We use the first mechanism in the
+ * mechansim list as the default. This set is created with
+ * statics thus needs not be freed
+ */
+ if(desired_mechs == GSS_C_NULL_OID_SET) {
+ mech = gssint_get_mechanism(NULL);
+ if (mech == NULL)
+ return (GSS_S_BAD_MECH);
+
+ mechs = &default_OID_set;
+ default_OID_set.count = 1;
+ default_OID_set.elements = &default_OID;
+ default_OID.length = mech->mech_type.length;
+ default_OID.elements = mech->mech_type.elements;
+ } else
+ mechs = desired_mechs;
+
+ if (mechs->count == 0)
+ return (GSS_S_BAD_MECH);
+
+ /* allocate the output credential structure */
+ creds = (gss_union_cred_t)malloc(sizeof (gss_union_cred_desc));
+ if (creds == NULL)
+ return (GSS_S_FAILURE);
+
+ /* initialize to 0s */
+ (void) memset(creds, 0, sizeof (gss_union_cred_desc));
+ creds->loopback = creds;
+
+ /* for each requested mech attempt to obtain a credential */
+ for (i = 0; i < mechs->count; i++) {
+ major = gss_add_cred_with_password(minor_status, (gss_cred_id_t)creds,
+ desired_name,
+ &mechs->elements[i],
+ password,
+ cred_usage, time_req, time_req, NULL,
+ NULL, &initTimeOut, &acceptTimeOut);
+ if (major == GSS_S_COMPLETE) {
+ /* update the credential's time */
+ if (cred_usage == GSS_C_ACCEPT) {
+ if (outTime > acceptTimeOut)
+ outTime = acceptTimeOut;
+ } else if (cred_usage == GSS_C_INITIATE) {
+ if (outTime > initTimeOut)
+ outTime = initTimeOut;
+ } else {
+ /*
+ * time_rec is the lesser of the
+ * init/accept times
+ */
+ if (initTimeOut > acceptTimeOut)
+ outTime = (outTime > acceptTimeOut) ?
+ acceptTimeOut : outTime;
+ else
+ outTime = (outTime > initTimeOut) ?
+ initTimeOut : outTime;
+ }
+ }
+ } /* for */
+
+ /* ensure that we have at least one credential element */
+ if (creds->count < 1) {
+ free(creds);
+ return (major);
+ }
+
+ /*
+ * fill in output parameters
+ * setup the actual mechs output parameter
+ */
+ if (actual_mechs != NULL) {
+ gss_OID_set_desc oids;
+
+ oids.count = creds->count;
+ oids.elements = creds->mechs_array;
+
+ major = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
+ if (GSS_ERROR(major)) {
+ (void) gss_release_cred(minor_status,
+ (gss_cred_id_t *)&creds);
+ return (major);
+ }
+ }
+
+ if (time_rec)
+ *time_rec = outTime;
+
+
+ creds->loopback = creds;
+ *output_cred_handle = (gss_cred_id_t)creds;
+ return (GSS_S_COMPLETE);
+}
+
+static OM_uint32
+val_add_cred_pw_args(
+ OM_uint32 *minor_status,
+ gss_cred_id_t input_cred_handle,
+ const gss_name_t desired_name,
+ const gss_OID desired_mech,
+ const gss_buffer_t password,
+ gss_cred_usage_t cred_usage,
+ OM_uint32 initiator_time_req,
+ OM_uint32 acceptor_time_req,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *initiator_time_rec,
+ OM_uint32 *acceptor_time_rec)
+{
+
+ /* Initialize outputs. */
+
+ if (minor_status != NULL)
+ *minor_status = 0;
+
+ if (output_cred_handle != NULL)
+ *output_cred_handle = GSS_C_NO_CREDENTIAL;
+
+ if (actual_mechs != NULL)
+ *actual_mechs = GSS_C_NO_OID_SET;
+
+ if (acceptor_time_rec != NULL)
+ *acceptor_time_rec = 0;
+
+ if (initiator_time_rec != NULL)
+ *initiator_time_rec = 0;
+
+ /* Validate arguments. */
+
+ if (minor_status == NULL)
+ return (GSS_S_CALL_INACCESSIBLE_WRITE);
+
+ if (input_cred_handle == GSS_C_NO_CREDENTIAL &&
+ output_cred_handle == NULL)
+ return (GSS_S_CALL_INACCESSIBLE_WRITE | GSS_S_NO_CRED);
+
+ if (cred_usage != GSS_C_ACCEPT
+ && cred_usage != GSS_C_INITIATE
+ && cred_usage != GSS_C_BOTH) {
+ if (minor_status) {
+ *minor_status = EINVAL;
+ map_errcode(minor_status);
+ }
+ return GSS_S_FAILURE;
+ }
+
+ if (password == GSS_C_NO_BUFFER ||
+ password->length == 0 ||
+ password->value == NULL) {
+ if (minor_status) {
+ *minor_status = EINVAL;
+ map_errcode(minor_status);
+ }
+ return GSS_S_FAILURE;
+ }
+
+
+ return (GSS_S_COMPLETE);
+}
+
+
+/* V2 KRB5_CALLCONV */
+OM_uint32 KRB5_CALLCONV
+gss_add_cred_with_password(minor_status, input_cred_handle,
+ desired_name, desired_mech, password, cred_usage,
+ initiator_time_req, acceptor_time_req,
+ output_cred_handle, actual_mechs,
+ initiator_time_rec, acceptor_time_rec)
+ OM_uint32 *minor_status;
+ const gss_cred_id_t input_cred_handle;
+ const gss_name_t desired_name;
+ const gss_OID desired_mech;
+ const gss_buffer_t password;
+ gss_cred_usage_t cred_usage;
+ OM_uint32 initiator_time_req;
+ OM_uint32 acceptor_time_req;
+ gss_cred_id_t *output_cred_handle;
+ gss_OID_set *actual_mechs;
+ OM_uint32 *initiator_time_rec;
+ OM_uint32 *acceptor_time_rec;
+{
+ OM_uint32 status, temp_minor_status;
+ OM_uint32 time_req, time_rec;
+ gss_union_name_t union_name;
+ gss_union_cred_t new_union_cred, union_cred;
+ gss_name_t internal_name = GSS_C_NO_NAME;
+ gss_name_t allocated_name = GSS_C_NO_NAME;
+ gss_mechanism mech;
+ gss_mechanism_ext mech_ext;
+ gss_cred_id_t cred = NULL;
+ gss_OID new_mechs_array = NULL;
+ gss_cred_id_t * new_cred_array = NULL;
+
+ status = val_add_cred_pw_args(minor_status,
+ input_cred_handle,
+ desired_name,
+ desired_mech,
+ password,
+ cred_usage,
+ initiator_time_req,
+ acceptor_time_req,
+ output_cred_handle,
+ actual_mechs,
+ initiator_time_rec,
+ acceptor_time_rec);
+ if (status != GSS_S_COMPLETE)
+ return (status);
+
+ mech = gssint_get_mechanism(desired_mech);
+ if (!mech)
+ return GSS_S_BAD_MECH;
+
+ mech_ext = gssint_get_mechanism_ext(desired_mech);
+ if (!mech_ext || !mech_ext->gssspi_acquire_cred_with_password)
+ return GSS_S_UNAVAILABLE;
+
+ if (input_cred_handle == GSS_C_NO_CREDENTIAL) {
+ union_cred = malloc(sizeof (gss_union_cred_desc));
+ if (union_cred == NULL)
+ return (GSS_S_FAILURE);
+
+ (void) memset(union_cred, 0, sizeof (gss_union_cred_desc));
+
+ /* for default credentials we will use GSS_C_NO_NAME */
+ internal_name = GSS_C_NO_NAME;
+ } else {
+ union_cred = (gss_union_cred_t)input_cred_handle;
+ if (gssint_get_mechanism_cred(union_cred, desired_mech) !=
+ GSS_C_NO_CREDENTIAL)
+ return (GSS_S_DUPLICATE_ELEMENT);
+
+ /* may need to create a mechanism specific name */
+ if (desired_name) {
+ union_name = (gss_union_name_t)desired_name;
+ if (union_name->mech_type &&
+ g_OID_equal(union_name->mech_type,
+ &mech->mech_type))
+ internal_name = union_name->mech_name;
+ else {
+ if (gssint_import_internal_name(minor_status,
+ &mech->mech_type, union_name,
+ &allocated_name) != GSS_S_COMPLETE)
+ return (GSS_S_BAD_NAME);
+ internal_name = allocated_name;
+ }
+ }
+ }
+
+
+ if (cred_usage == GSS_C_ACCEPT)
+ time_req = acceptor_time_req;
+ else if (cred_usage == GSS_C_INITIATE)
+ time_req = initiator_time_req;
+ else if (cred_usage == GSS_C_BOTH)
+ time_req = (acceptor_time_req > initiator_time_req) ?
+ acceptor_time_req : initiator_time_req;
+ else
+ time_req = 0;
+
+ status = mech_ext->gssspi_acquire_cred_with_password(minor_status,
+ internal_name,
+ password,
+ time_req,
+ GSS_C_NULL_OID_SET,
+ cred_usage,
+ &cred,
+ NULL,
+ &time_rec);
+ if (status != GSS_S_COMPLETE) {
+ map_error(minor_status, mech);
+ goto errout;
+ }
+
+ /* may need to set credential auxinfo strucutre */
+ if (union_cred->auxinfo.creation_time == 0) {
+ union_cred->auxinfo.creation_time = time(NULL);
+ union_cred->auxinfo.time_rec = time_rec;
+ union_cred->auxinfo.cred_usage = cred_usage;
+
+ /*
+ * we must set the name; if name is not supplied
+ * we must do inquire cred to get it
+ */
+ if (internal_name == NULL) {
+ if (mech->gss_inquire_cred == NULL ||
+ ((status = mech->gss_inquire_cred(
+ &temp_minor_status, cred,
+ &allocated_name, NULL, NULL,
+ NULL)) != GSS_S_COMPLETE))
+ goto errout;
+ internal_name = allocated_name;
+ }
+
+ if (internal_name != GSS_C_NO_NAME) {
+ status = mech->gss_display_name(&temp_minor_status, internal_name,
+ &union_cred->auxinfo.name,
+ &union_cred->auxinfo.name_type);
+
+ if (status != GSS_S_COMPLETE)
+ goto errout;
+ }
+ }
+
+ /* now add the new credential elements */
+ new_mechs_array = (gss_OID)
+ malloc(sizeof (gss_OID_desc) * (union_cred->count+1));
+
+ new_cred_array = (gss_cred_id_t *)
+ malloc(sizeof (gss_cred_id_t) * (union_cred->count+1));
+
+ if (!new_mechs_array || !new_cred_array) {
+ status = GSS_S_FAILURE;
+ goto errout;
+ }
+
+ if (acceptor_time_rec)
+ if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH)
+ *acceptor_time_rec = time_rec;
+ if (initiator_time_rec)
+ if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH)
+ *initiator_time_rec = time_rec;
+
+ /*
+ * OK, expand the mechanism array and the credential array
+ */
+ (void) memcpy(new_mechs_array, union_cred->mechs_array,
+ sizeof (gss_OID_desc) * union_cred->count);
+ (void) memcpy(new_cred_array, union_cred->cred_array,
+ sizeof (gss_cred_id_t) * union_cred->count);
+
+ new_cred_array[union_cred->count] = cred;
+ if ((new_mechs_array[union_cred->count].elements =
+ malloc(mech->mech_type.length)) == NULL)
+ goto errout;
+
+ g_OID_copy(&new_mechs_array[union_cred->count],
+ &mech->mech_type);
+
+ if (actual_mechs != NULL) {
+ gss_OID_set_desc oids;
+
+ oids.count = union_cred->count + 1;
+ oids.elements = new_mechs_array;
+
+ status = generic_gss_copy_oid_set(minor_status, &oids, actual_mechs);
+ if (GSS_ERROR(status)) {
+ free(new_mechs_array[union_cred->count].elements);
+ goto errout;
+ }
+ }
+
+ if (output_cred_handle == NULL) {
+ free(union_cred->mechs_array);
+ free(union_cred->cred_array);
+ new_union_cred = union_cred;
+ } else {
+ new_union_cred = malloc(sizeof (gss_union_cred_desc));
+ if (new_union_cred == NULL) {
+ free(new_mechs_array[union_cred->count].elements);
+ goto errout;
+ }
+ *new_union_cred = *union_cred;
+ *output_cred_handle = (gss_cred_id_t)new_union_cred;
+ }
+
+ new_union_cred->mechs_array = new_mechs_array;
+ new_union_cred->cred_array = new_cred_array;
+ new_union_cred->count++;
+ new_union_cred->loopback = new_union_cred;
+
+ /* We're done with the internal name. Free it if we allocated it. */
+
+ if (allocated_name)
+ (void) gssint_release_internal_name(&temp_minor_status,
+ &mech->mech_type,
+ &allocated_name);
+
+ return (GSS_S_COMPLETE);
+
+errout:
+ if (new_mechs_array)
+ free(new_mechs_array);
+ if (new_cred_array)
+ free(new_cred_array);
+
+ if (cred != NULL && mech->gss_release_cred)
+ mech->gss_release_cred(&temp_minor_status, &cred);
+
+ if (allocated_name)
+ (void) gssint_release_internal_name(&temp_minor_status,
+ &mech->mech_type,
+ &allocated_name);
+
+ if (input_cred_handle == GSS_C_NO_CREDENTIAL && union_cred) {
+ if (union_cred->auxinfo.name.value)
+ free(union_cred->auxinfo.name.value);
+ free(union_cred);
+ }
+
+ return (status);
+}
diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c
index 70c220391..a393a5309 100644
--- a/src/lib/gssapi/mechglue/g_initialize.c
+++ b/src/lib/gssapi/mechglue/g_initialize.c
@@ -609,6 +609,10 @@ releaseMechInfo(gss_mech_info *pCf)
memset(cf->mech, 0, sizeof(*cf->mech));
free(cf->mech);
}
+ if (cf->mech_ext != NULL) {
+ memset(cf->mech_ext, 0, sizeof(*cf->mech_ext));
+ free(cf->mech_ext);
+ }
if (cf->dl_handle != NULL)
krb5int_close_plugin(cf->dl_handle);
@@ -646,6 +650,16 @@ gssint_register_mechinfo(gss_mech_info template)
new_cf->freeMech = 1;
new_cf->next = NULL;
+ if (template->mech_ext != NULL) {
+ new_cf->mech_ext = (gss_mechanism_ext)calloc(1,
+ sizeof(struct gss_config_ext));
+ if (new_cf->mech_ext == NULL) {
+ releaseMechInfo(&new_cf);
+ return ENOMEM;
+ }
+ *new_cf->mech_ext = *template->mech_ext;
+ }
+
if (template->kmodName != NULL) {
new_cf->kmodName = strdup(template->kmodName);
if (new_cf->kmodName == NULL) {
@@ -785,6 +799,21 @@ build_dynamicMech(void *dl, const gss_OID mech_type)
return mech;
}
+static gss_mechanism_ext
+build_dynamicMechExt(void *dl, const gss_OID mech_type)
+{
+ gss_mechanism_ext mech_ext;
+
+ mech_ext = (gss_mechanism_ext)calloc(1, sizeof(*mech_ext));
+ if (mech_ext == NULL) {
+ return NULL;
+ }
+
+ GSS_ADD_DYNAMIC_METHOD(dl, mech_ext, gssspi_acquire_cred_with_password);
+
+ return mech_ext;
+}
+
static void
freeMechList(void)
{
@@ -906,59 +935,50 @@ gssint_get_mechanism_ext(oid)
const gss_OID oid;
{
gss_mech_info aMech;
- gss_mechanism_ext mech_ext;
if (gssint_mechglue_initialize_library() != 0)
return (NULL);
+ if (k5_mutex_lock(&g_mechListLock) != 0)
+ return NULL;
/* check if the mechanism is already loaded */
- if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext != NULL)
+ if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext) {
+ (void) k5_mutex_unlock(&g_mechListLock);
return (aMech->mech_ext);
+ }
- if (gssint_get_mechanism(oid) == NULL)
- return (NULL);
-
- if (aMech->dl_handle == NULL)
- return (NULL);
-
- /* Load the gss_config_ext struct for this mech */
-
- mech_ext = (gss_mechanism_ext)malloc(sizeof (struct gss_config_ext));
-
- if (mech_ext == NULL)
- return (NULL);
-
-#if 0
/*
- * dlsym() the mech's 'method' functions for the extended APIs
- *
- * NOTE: Until the void *context argument is removed from the
- * SPI method functions' signatures it will be necessary to have
- * different function pointer typedefs and function names for
- * the SPI methods than for the API. When this argument is
- * removed it will be possible to rename gss_*_sfct to gss_*_fct
- * and and gssspi_* to gss_*.
+ * might need to re-read the configuration file before loading
+ * the mechanism to ensure we have the latest info.
*/
- mech_ext->gss_acquire_cred_with_password =
- (gss_acquire_cred_with_password_sfct)dlsym(aMech->dl_handle,
- "gssspi_acquire_cred_with_password");
-#endif
+ updateMechList();
+
+ aMech = searchMechList(oid);
+
+ /* is the mechanism present in the list ? */
+ if (aMech == NULL || aMech->dl_handle == NULL) {
+ (void) k5_mutex_unlock(&g_mechListLock);
+ return ((gss_mechanism_ext)NULL);
+ }
- /* Set aMech->mech_ext */
- (void) k5_mutex_lock(&g_mechListLock);
+ /* has another thread loaded the mech */
+ if (aMech->mech_ext) {
+ (void) k5_mutex_unlock(&g_mechListLock);
+ return (aMech->mech_ext);
+ }
- if (aMech->mech_ext == NULL)
- aMech->mech_ext = mech_ext;
- else
- free(mech_ext); /* we raced and lost; don't leak */
+ /* Try dynamic dispatch table */
+ aMech->mech_ext = build_dynamicMechExt(aMech->dl_handle,
+ aMech->mech_type);
+ if (aMech->mech_ext == NULL) {
+ (void) k5_mutex_unlock(&g_mechListLock);
+ return ((gss_mechanism_ext)NULL);
+ }
(void) k5_mutex_unlock(&g_mechListLock);
-
return (aMech->mech_ext);
-
} /* gssint_get_mechanism_ext */
-
/*
* this routine is used for searching the list of mechanism data.
*
diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h
index 3769caf87..f21929015 100644
--- a/src/lib/gssapi/mechglue/mglueP.h
+++ b/src/lib/gssapi/mechglue/mglueP.h
@@ -77,7 +77,6 @@ typedef struct gss_cred_id_struct {
} gss_union_cred_desc, *gss_union_cred_t;
typedef OM_uint32 (*gss_acquire_cred_with_password_sfct)(
- void *, /* context */
OM_uint32 *, /* minor_status */
const gss_name_t, /* desired_name */
const gss_buffer_t, /* password */
@@ -593,7 +592,7 @@ typedef struct gss_config {
/* This structure MUST NOT be used by any code outside libgss */
typedef struct gss_config_ext {
- gss_acquire_cred_with_password_sfct gss_acquire_cred_with_password;
+ gss_acquire_cred_with_password_sfct gssspi_acquire_cred_with_password;
} *gss_mechanism_ext;
/*
diff --git a/src/lib/gssapi/spnego/gssapiP_spnego.h b/src/lib/gssapi/spnego/gssapiP_spnego.h
index a1cd2b4a6..d72c85da7 100644
--- a/src/lib/gssapi/spnego/gssapiP_spnego.h
+++ b/src/lib/gssapi/spnego/gssapiP_spnego.h
@@ -458,6 +458,18 @@ spnego_gss_acquire_cred_impersonate_name(
OM_uint32 *); /* time_rec */
OM_uint32
+spnego_gss_acquire_cred_with_password(
+ OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ const gss_buffer_t password,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec);
+
+OM_uint32
spnego_gss_display_name_ext
(
OM_uint32 *minor_status,
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index 86ba89a39..c9cf441e0 100644
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -274,6 +274,11 @@ static struct gss_config spnego_mechanism =
spnego_gss_set_neg_mechs,
};
+static struct gss_config_ext spnego_mechanism_ext =
+{
+ spnego_gss_acquire_cred_with_password
+};
+
#ifdef _GSS_STATIC_LINK
#include "mglueP.h"
@@ -283,6 +288,7 @@ static int gss_spnegomechglue_init(void)
memset(&mech_spnego, 0, sizeof(mech_spnego));
mech_spnego.mech = &spnego_mechanism;
+ mech_spnego.mech_ext = &spnego_mechanism_ext;
mech_spnego.mechNameStr = "spnego";
mech_spnego.mech_type = GSS_C_NO_OID;
@@ -2447,6 +2453,64 @@ spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
}
OM_uint32
+spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
+ const gss_name_t desired_name,
+ const gss_buffer_t password,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 status, tmpmin;
+ gss_OID_set amechs = GSS_C_NULL_OID_SET, dmechs;
+ gss_cred_id_t mcred = NULL;
+ spnego_gss_cred_id_t spcred = NULL;
+
+ dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
+
+ if (actual_mechs)
+ *actual_mechs = NULL;
+
+ if (time_rec)
+ *time_rec = 0;
+
+ dmechs = desired_mechs;
+ if (desired_mechs == GSS_C_NULL_OID_SET) {
+ status = get_available_mechs(minor_status, desired_name,
+ cred_usage, NULL, &amechs);
+ dmechs = amechs;
+ }
+
+ status = gss_acquire_cred_with_password(minor_status, desired_name,
+ password, time_req, dmechs,
+ cred_usage, &mcred,
+ actual_mechs, time_rec);
+ if (status != GSS_S_COMPLETE)
+ goto cleanup;
+
+ spcred = malloc(sizeof(spnego_gss_cred_id_rec));
+ if (spcred == NULL) {
+ *minor_status = ENOMEM;
+ status = GSS_S_FAILURE;
+ goto cleanup;
+ }
+ spcred->neg_mechs = GSS_C_NULL_OID_SET;
+ spcred->mcred = mcred;
+ mcred = GSS_C_NO_CREDENTIAL;
+ *output_cred_handle = (gss_cred_id_t)spcred;
+
+cleanup:
+
+ (void) gss_release_oid_set(&tmpmin, &amechs);
+ (void) gss_release_cred(&tmpmin, &mcred);
+
+ dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
+ return (status);
+}
+
+OM_uint32
spnego_gss_display_name_ext(OM_uint32 *minor_status,
gss_name_t name,
gss_OID display_as_name_type,
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c
index 60d9455a1..c2dd5f6d4 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.c
+++ b/src/lib/krb5/asn.1/asn1_k_decode.c
@@ -1899,6 +1899,47 @@ error_out:
return retval;
}
+asn1_error_code asn1_decode_iakerb_header
+(asn1buf *buf, krb5_iakerb_header *val)
+{
+ setup();
+ val->target_realm.data = NULL;
+ val->target_realm.length = 0;
+ val->cookie = NULL;
+ {
+ begin_structure();
+ get_lenfield(val->target_realm.length, val->target_realm.data,
+ 1, asn1_decode_charstring);
+ if (tagnum == 2) {
+ alloc_data(val->cookie);
+ get_lenfield(val->cookie->length, val->cookie->data,
+ 2, asn1_decode_charstring);
+ }
+ end_structure();
+ }
+ return 0;
+error_out:
+ krb5_free_data_contents(NULL, &val->target_realm);
+ krb5_free_data(NULL, val->cookie);
+ return retval;
+}
+
+asn1_error_code asn1_decode_iakerb_finished
+(asn1buf *buf, krb5_iakerb_finished *val)
+{
+ setup();
+ val->checksum.contents = NULL;
+ {
+ begin_structure();
+ get_field(val->checksum, 1, asn1_decode_checksum);
+ end_structure();
+ }
+ return 0;
+error_out:
+ krb5_free_checksum_contents(NULL, &val->checksum);
+ return retval;
+}
+
#ifndef DISABLE_PKINIT
/* PKINIT */
diff --git a/src/lib/krb5/asn.1/asn1_k_decode.h b/src/lib/krb5/asn.1/asn1_k_decode.h
index 1b0aa750b..79a4a05e4 100644
--- a/src/lib/krb5/asn.1/asn1_k_decode.h
+++ b/src/lib/krb5/asn.1/asn1_k_decode.h
@@ -271,4 +271,10 @@ asn1_error_code asn1_decode_ad_kdcissued_ptr(asn1buf *buf,
asn1_error_code asn1_decode_ad_signedpath(asn1buf *buf,
krb5_ad_signedpath *val);
+asn1_error_code asn1_decode_iakerb_header(asn1buf *buf,
+ krb5_iakerb_header *val);
+
+asn1_error_code asn1_decode_iakerb_finished(asn1buf *buf,
+ krb5_iakerb_finished *val);
+
#endif
diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index a35f561e6..d334ae632 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -1394,6 +1394,35 @@ static unsigned int ad_signedpath_optional(const void *p)
DEFSEQTYPE(ad_signedpath, krb5_ad_signedpath, ad_signedpath_fields, ad_signedpath_optional);
+static const struct field_info iakerb_header_fields[] = {
+ FIELDOF_NORM(krb5_iakerb_header, ostring_data, target_realm, 1),
+ FIELDOF_OPT(krb5_iakerb_header, ostring_data_ptr, cookie, 2, 2),
+};
+
+static unsigned int iakerb_header_optional(const void *p)
+{
+ unsigned int optional = 0;
+ const krb5_iakerb_header *val = p;
+ if (val->cookie && val->cookie->data)
+ optional |= (1u << 2);
+ return optional;
+}
+
+DEFSEQTYPE(iakerb_header, krb5_iakerb_header, iakerb_header_fields, iakerb_header_optional);
+
+static const struct field_info iakerb_finished_fields[] = {
+ FIELDOF_NORM(krb5_iakerb_finished, checksum, checksum, 1),
+};
+
+static unsigned int iakerb_finished_optional(const void *p)
+{
+ unsigned int optional = 0;
+ return optional;
+}
+
+DEFSEQTYPE(iakerb_finished, krb5_iakerb_finished, iakerb_finished_fields,
+iakerb_finished_optional);
+
/* Exported complete encoders -- these produce a krb5_data with
the encoding in the correct byte order. */
@@ -1472,6 +1501,8 @@ MAKE_FULL_ENCODER(encode_krb5_fast_response, fast_response);
MAKE_FULL_ENCODER(encode_krb5_ad_kdcissued, ad_kdc_issued);
MAKE_FULL_ENCODER(encode_krb5_ad_signedpath_data, ad_signedpath_data);
MAKE_FULL_ENCODER(encode_krb5_ad_signedpath, ad_signedpath);
+MAKE_FULL_ENCODER(encode_krb5_iakerb_header, iakerb_header);
+MAKE_FULL_ENCODER(encode_krb5_iakerb_finished, iakerb_finished);
diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c
index 542a626da..7aeb6bfe5 100644
--- a/src/lib/krb5/asn.1/krb5_decode.c
+++ b/src/lib/krb5/asn.1/krb5_decode.c
@@ -1218,6 +1218,30 @@ decode_krb5_ad_signedpath(const krb5_data *code, krb5_ad_signedpath **repptr)
cleanup(free);
}
+krb5_error_code decode_krb5_iakerb_header
+(const krb5_data *code, krb5_iakerb_header **repptr)
+{
+ setup_buf_only(krb5_iakerb_header *);
+ alloc_field(rep);
+
+ retval = asn1_decode_iakerb_header(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
+krb5_error_code decode_krb5_iakerb_finished
+(const krb5_data *code, krb5_iakerb_finished **repptr)
+{
+ setup_buf_only(krb5_iakerb_finished *);
+ alloc_field(rep);
+
+ retval = asn1_decode_iakerb_finished(&buf, rep);
+ if (retval) clean_return(retval);
+
+ cleanup(free);
+}
+
krb5_error_code
krb5int_get_authdata_containee_types(krb5_context context,
const krb5_authdata *authdata,
diff --git a/src/lib/krb5/error_tables/krb5_err.et b/src/lib/krb5/error_tables/krb5_err.et
index bf3404de1..56434ad47 100644
--- a/src/lib/krb5/error_tables/krb5_err.et
+++ b/src/lib/krb5/error_tables/krb5_err.et
@@ -126,8 +126,8 @@ error_code KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encrypti
error_code KRB5PLACEHOLD_82, "KRB5 error code 82"
error_code KRB5PLACEHOLD_83, "KRB5 error code 83"
error_code KRB5PLACEHOLD_84, "KRB5 error code 84"
-error_code KRB5PLACEHOLD_85, "KRB5 error code 85"
-error_code KRB5PLACEHOLD_86, "KRB5 error code 86"
+error_code KRB5KRB_AP_ERR_IAKERB_KDC_NOT_FOUND, "The IAKERB proxy could not find a KDC"
+error_code KRB5KRB_AP_ERR_IAKERB_KDC_NO_RESPONSE, "The KDC did not respond to the IAKERB proxy"
error_code KRB5PLACEHOLD_87, "KRB5 error code 87"
error_code KRB5PLACEHOLD_88, "KRB5 error code 88"
error_code KRB5PLACEHOLD_89, "KRB5 error code 89"
diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c
index 2adaa4101..6a3e6b291 100644
--- a/src/lib/krb5/krb/kfree.c
+++ b/src/lib/krb5/krb/kfree.c
@@ -913,3 +913,22 @@ krb5_free_ad_signedpath(krb5_context context, krb5_ad_signedpath *val)
krb5_free_pa_data(context, val->method_data);
free(val);
}
+
+void KRB5_CALLCONV
+krb5_free_iakerb_header(krb5_context context, krb5_iakerb_header *val)
+{
+ if (val == NULL)
+ return ;
+
+ krb5_free_data_contents(context, &val->target_realm);
+ krb5_free_data(context, val->cookie);
+}
+
+void KRB5_CALLCONV
+krb5_free_iakerb_finished(krb5_context context, krb5_iakerb_finished *val)
+{
+ if (val == NULL)
+ return ;
+
+ krb5_free_checksum_contents(context, &val->checksum);
+}
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 58e4ddac7..5c517c89c 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -22,6 +22,8 @@ decode_krb5_error
decode_krb5_etype_info
decode_krb5_etype_info2
decode_krb5_fast_req
+decode_krb5_iakerb_finished
+decode_krb5_iakerb_header
decode_krb5_kdc_req_body
decode_krb5_pa_enc_ts
decode_krb5_pa_for_user
@@ -67,6 +69,8 @@ encode_krb5_error
encode_krb5_etype_info
encode_krb5_etype_info2
encode_krb5_fast_response
+encode_krb5_iakerb_finished
+encode_krb5_iakerb_header
encode_krb5_kdc_req_body
encode_krb5_pa_enc_ts
encode_krb5_pa_for_user
@@ -266,6 +270,8 @@ krb5_free_etype_info
krb5_free_fast_armored_req
krb5_free_fast_req
krb5_free_host_realm
+krb5_free_iakerb_finished
+krb5_free_iakerb_header
krb5_free_kdc_rep
krb5_free_kdc_req
krb5_free_keyblock
diff --git a/src/tests/asn.1/krb5_decode_leak.c b/src/tests/asn.1/krb5_decode_leak.c
index ab46fb946..ac7f5bfc8 100644
--- a/src/tests/asn.1/krb5_decode_leak.c
+++ b/src/tests/asn.1/krb5_decode_leak.c
@@ -704,6 +704,28 @@ main(int argc, char **argv)
krb5_free_ad_signedpath);
ktest_empty_ad_signedpath(&sp);
}
+ /****************************************************************/
+ /* encode_krb5_iakerb_header */
+ {
+ krb5_iakerb_header ih, *tmp;
+ setup(ih, "iakerb_header",
+ ktest_make_sample_iakerb_header);
+ leak_test(ih, encode_krb5_iakerb_header,
+ decode_krb5_iakerb_header,
+ krb5_free_iakerb_header);
+ ktest_empty_iakerb_header(&ih);
+ }
+ /****************************************************************/
+ /* encode_krb5_iakerb_finished */
+ {
+ krb5_iakerb_finished ih, *tmp;
+ setup(ih, "iakerb_finished",
+ ktest_make_sample_iakerb_finished);
+ leak_test(ih, encode_krb5_iakerb_finished,
+ decode_krb5_iakerb_finished,
+ krb5_free_iakerb_finished);
+ ktest_empty_iakerb_finished(&ih);
+ }
krb5_free_context(test_context);
return 0;
}
diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c
index 2ef70cfe6..b3480a63d 100644
--- a/src/tests/asn.1/krb5_decode_test.c
+++ b/src/tests/asn.1/krb5_decode_test.c
@@ -916,6 +916,22 @@ int main(argc, argv)
ktest_empty_ad_signedpath(&ref);
}
+ /****************************************************************/
+ /* decode_iakerb_header */
+ {
+ setup(krb5_iakerb_header,"krb5_iakerb_header",ktest_make_sample_iakerb_header);
+ decode_run("iakerb_header","","30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61",decode_krb5_iakerb_header,ktest_equal_iakerb_header,krb5_free_iakerb_header);
+ ktest_empty_iakerb_header(&ref);
+ }
+
+ /****************************************************************/
+ /* decode_iakerb_finished */
+ {
+ setup(krb5_iakerb_finished,"krb5_iakerb_finished",ktest_make_sample_iakerb_finished);
+ decode_run("iakerb_finished","","30 11 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34",decode_krb5_iakerb_finished,ktest_equal_iakerb_finished,krb5_free_iakerb_finished);
+ ktest_empty_iakerb_finished(&ref);
+ }
+
#ifdef ENABLE_LDAP
/* ldap sequence_of_keys */
{
diff --git a/src/tests/asn.1/krb5_encode_test.c b/src/tests/asn.1/krb5_encode_test.c
index 9694746fa..784d20329 100644
--- a/src/tests/asn.1/krb5_encode_test.c
+++ b/src/tests/asn.1/krb5_encode_test.c
@@ -740,6 +740,28 @@ main(argc, argv)
encode_krb5_ad_signedpath);
ktest_empty_ad_signedpath(&sp);
}
+ /****************************************************************/
+ /* encode_krb5_iakerb_header */
+ {
+ krb5_iakerb_header ih;
+ setup(ih,krb5_ad_signedpath,"iakerb_header",
+ ktest_make_sample_iakerb_header);
+ encode_run(ih,krb5_iakerb_header,
+ "iakerb_header","",
+ encode_krb5_iakerb_header);
+ ktest_empty_iakerb_header(&ih);
+ }
+ /****************************************************************/
+ /* encode_krb5_iakerb_finished */
+ {
+ krb5_iakerb_finished ih;
+ setup(ih,krb5_ad_signedpath,"iakerb_finished",
+ ktest_make_sample_iakerb_finished);
+ encode_run(ih,krb5_iakerb_finished,
+ "iakerb_finished","",
+ encode_krb5_iakerb_finished);
+ ktest_empty_iakerb_finished(&ih);
+ }
#ifdef ENABLE_LDAP
{
ldap_seqof_key_data skd;
diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c
index eefbec9d5..0746d81b8 100644
--- a/src/tests/asn.1/ktest.c
+++ b/src/tests/asn.1/ktest.c
@@ -890,6 +890,28 @@ krb5_error_code ktest_make_sample_ad_signedpath(p)
return retval;
}
+krb5_error_code ktest_make_sample_iakerb_header(ih)
+ krb5_iakerb_header *ih;
+{
+ krb5_error_code retval;
+ retval = ktest_make_sample_data(&(ih->target_realm));
+ if (retval) return retval;
+ ih->cookie = k5alloc(sizeof(krb5_data), &retval);
+ if (retval) return retval;
+ retval = ktest_make_sample_data(ih->cookie);
+ if (retval) return retval;
+ return retval;
+}
+
+krb5_error_code ktest_make_sample_iakerb_finished(ih)
+ krb5_iakerb_finished *ih;
+{
+ krb5_error_code retval;
+ retval = ktest_make_sample_checksum(&ih->checksum);
+ if (retval) return retval;
+ return retval;
+}
+
#ifdef ENABLE_LDAP
static krb5_error_code ktest_make_sample_key_data(krb5_key_data *p, int i)
{
@@ -1532,6 +1554,19 @@ void ktest_empty_ad_signedpath(p)
ktest_destroy_pa_data_array(&p->method_data);
}
+void ktest_empty_iakerb_header(p)
+ krb5_iakerb_header *p;
+{
+ krb5_free_data_contents(NULL, &p->target_realm);
+ krb5_free_data(NULL, p->cookie);
+}
+
+void ktest_empty_iakerb_finished(p)
+ krb5_iakerb_finished *p;
+{
+ krb5_free_checksum_contents(NULL, &p->checksum);
+}
+
#ifdef ENABLE_LDAP
void ktest_empty_ldap_seqof_key_data(ctx, p)
krb5_context ctx;
diff --git a/src/tests/asn.1/ktest.h b/src/tests/asn.1/ktest.h
index 5f9b5ca38..c4059a7bb 100644
--- a/src/tests/asn.1/ktest.h
+++ b/src/tests/asn.1/ktest.h
@@ -109,6 +109,8 @@ krb5_error_code ktest_make_sample_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p);
krb5_error_code ktest_make_sample_ad_kdcissued(krb5_ad_kdcissued *p);
krb5_error_code ktest_make_sample_ad_signedpath_data(krb5_ad_signedpath_data *p);
krb5_error_code ktest_make_sample_ad_signedpath(krb5_ad_signedpath *p);
+krb5_error_code ktest_make_sample_iakerb_header(krb5_iakerb_header *p);
+krb5_error_code ktest_make_sample_iakerb_finished(krb5_iakerb_finished *p);
#ifdef ENABLE_LDAP
krb5_error_code ktest_make_sample_ldap_seqof_key_data(ldap_seqof_key_data * p);
@@ -221,6 +223,8 @@ void ktest_empty_pa_s4u_x509_user(krb5_pa_s4u_x509_user *p);
void ktest_empty_ad_kdcissued(krb5_ad_kdcissued *p);
void ktest_empty_ad_signedpath_data(krb5_ad_signedpath_data *p);
void ktest_empty_ad_signedpath(krb5_ad_signedpath *p);
+void ktest_empty_iakerb_header(krb5_iakerb_header *p);
+void ktest_empty_iakerb_finished(krb5_iakerb_finished *p);
#ifdef ENABLE_LDAP
void ktest_empty_ldap_seqof_key_data(krb5_context, ldap_seqof_key_data *p);
diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c
index f84357b94..0a9280446 100644
--- a/src/tests/asn.1/ktest_equal.c
+++ b/src/tests/asn.1/ktest_equal.c
@@ -600,6 +600,29 @@ int ktest_equal_ad_signedpath(ref, var)
return p;
}
+int ktest_equal_iakerb_header(ref, var)
+ krb5_iakerb_header *ref;
+ krb5_iakerb_header *var;
+{
+ int p = TRUE;
+ if (ref == var) return TRUE;
+ else if (ref == NULL || var == NULL) return FALSE;
+ p=p&&struct_equal(target_realm,ktest_equal_data);
+ p=p&&ptr_equal(cookie,ktest_equal_data);
+ return p;
+}
+
+int ktest_equal_iakerb_finished(ref, var)
+ krb5_iakerb_finished *ref;
+ krb5_iakerb_finished *var;
+{
+ int p = TRUE;
+ if (ref == var) return TRUE;
+ else if (ref == NULL || var == NULL) return FALSE;
+ p=p&&struct_equal(checksum,ktest_equal_checksum);
+ return p;
+}
+
#ifdef ENABLE_LDAP
static int equal_key_data(ref, var)
krb5_key_data *ref;
diff --git a/src/tests/asn.1/ktest_equal.h b/src/tests/asn.1/ktest_equal.h
index 80c38b639..4b55e2333 100644
--- a/src/tests/asn.1/ktest_equal.h
+++ b/src/tests/asn.1/ktest_equal.h
@@ -106,6 +106,12 @@ int ktest_equal_ad_signedpath_data
int ktest_equal_ad_signedpath
(krb5_ad_signedpath *ref,
krb5_ad_signedpath *var);
+int ktest_equal_iakerb_header
+ (krb5_iakerb_header *ref,
+ krb5_iakerb_header *var);
+int ktest_equal_iakerb_finished
+ (krb5_iakerb_finished *ref,
+ krb5_iakerb_finished *var);
int ktest_equal_ldap_sequence_of_keys(ldap_seqof_key_data *ref,
ldap_seqof_key_data *var);
diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out
index c6bcf619c..92d21b1a9 100644
--- a/src/tests/asn.1/reference_encode.out
+++ b/src/tests/asn.1/reference_encode.out
@@ -60,3 +60,5 @@ encode_krb5_pa_s4u_x509_user: 30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30
encode_krb5_ad_kdcissued: 30 65 A0 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72
encode_krb5_ad_signedpath_data: 30 81 C7 A0 30 30 2E A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A1 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A2 32 30 30 30 2E A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 A4 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72
encode_krb5_ad_signedpath: 30 3E A0 03 02 01 01 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61
+encode_krb5_iakerb_header: 30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61
+encode_krb5_iakerb_finished: 30 11 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34
diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out
index 38e5b99ad..cc1daf3a2 100644
--- a/src/tests/asn.1/trval_reference.out
+++ b/src/tests/asn.1/trval_reference.out
@@ -1332,3 +1332,16 @@ encode_krb5_ad_signedpath:
. . . [1] [Integer] 13
. . . [2] [Octet String] "pa-data"
+encode_krb5_iakerb_header:
+
+[Sequence/Sequence Of]
+. [1] [Octet String] "krb5data"
+. [2] [Octet String] "krb5data"
+
+encode_krb5_iakerb_finished:
+
+[Sequence/Sequence Of]
+. [1] [Sequence/Sequence Of]
+. . [0] [Integer] 1
+. . [1] [Octet String] "1234"
+