+2003-12-13 Ken Raeburn <raeburn@mit.edu>
+
+ * gssapiP_generic.h: Include k5-platform.h.
+ (gssint_uint64): New typedef.
+ (g_order_init, g_order_check): Update decls.
+ * util_ordering.c (struct _queue): Change sequence number fields
+ to gssint_uint64. Add mask field.
+ (queue_insert): Change sequence number to gssint_uint64.
+ (g_order_init): Change sequence numbers to gssint_uint64. Add
+ "wide_nums" argument; initialize the queue mask field based on
+ it; all callers changed. Store a -1 as the first element.
+ (g_order_check): Store and check elements as offsets from
+ firstnum. Mask to 32 bits if desired.
+ * util_token.c (g_verify_token_header): Add new argument
+ indicating whether the pseudo-ASN.1 wrapper is required; all
+ callers changed.
+
2003-07-17 Ken Raeburn <raeburn@mit.edu>
* Makefile.in (LIBNAME) [##WIN16##]: Don't define.
#include "gssapi_err_generic.h"
#include <errno.h>
+#include "k5-platform.h"
+typedef UINT64_TYPE gssint_uint64;
+
/** helper macros **/
#define g_OID_equal(o1,o2) \
unsigned char **buf, int tok_type);
gss_int32 g_verify_token_header (gss_OID mech, unsigned int *body_size,
- unsigned char **buf, int tok_type,
- unsigned int toksize_in);
+ unsigned char **buf, int tok_type,
+ unsigned int toksize_in,
+ int wrapper_required);
OM_uint32 g_display_major_status (OM_uint32 *minor_status,
OM_uint32 status_value,
OM_uint32 status_value,
gss_buffer_t status_string);
-gss_int32 g_order_init (void **queue, OM_uint32 seqnum,
- int do_replay, int do_sequence);
+gss_int32 g_order_init (void **queue, gssint_uint64 seqnum,
+ int do_replay, int do_sequence, int wide);
-gss_int32 g_order_check (void **queue, OM_uint32 seqnum);
+gss_int32 g_order_check (void **queue, gssint_uint64 seqnum);
void g_order_free (void **queue);
int do_sequence;
int start;
int length;
- unsigned int firstnum;
- unsigned int elem[QUEUE_LENGTH];
+ gssint_uint64 firstnum;
+ /* Stored as deltas from firstnum. This way, the high bit won't
+ overflow unless we've actually gone through 2**n messages, or
+ gotten something *way* out of sequence. */
+ gssint_uint64 elem[QUEUE_LENGTH];
+ /* All ones for 64-bit sequence numbers; 32 ones for 32-bit
+ sequence numbers. */
+ gssint_uint64 mask;
} queue;
/* rep invariant:
#define QELEM(q,i) ((q)->elem[(i)%QSIZE(q)])
static void
-queue_insert(queue *q, int after, unsigned int seqnum)
+queue_insert(queue *q, int after, gssint_uint64 seqnum)
{
/* insert. this is not the fastest way, but it's easy, and it's
optimized for insert at end, which is the common case */
q->length++;
}
}
-
+
gss_int32
-g_order_init(void **vqueue, OM_uint32 seqnum,
- int do_replay, int do_sequence)
+g_order_init(void **vqueue, gssint_uint64 seqnum,
+ int do_replay, int do_sequence, int wide_nums)
{
queue *q;
q->do_replay = do_replay;
q->do_sequence = do_sequence;
+ q->mask = wide_nums ? ~(gssint_uint64)0 : 0xffffffffUL;
q->start = 0;
q->length = 1;
q->firstnum = seqnum;
- q->elem[q->start] = seqnum-1;
+ q->elem[q->start] = ((gssint_uint64)0 - 1) & q->mask;
*vqueue = (void *) q;
return(0);
}
gss_int32
-g_order_check(void **vqueue, OM_uint32 seqnum)
+g_order_check(void **vqueue, gssint_uint64 seqnum)
{
queue *q;
int i;
-
+ gssint_uint64 expected;
+
q = (queue *) (*vqueue);
if (!q->do_replay && !q->do_sequence)
return(GSS_S_COMPLETE);
+ /* All checks are done relative to the initial sequence number, to
+ avoid (or at least put off) the pain of wrapping. */
+ seqnum -= q->firstnum;
+ /* If we're only doing 32-bit values, adjust for that again.
+
+ Note that this will probably be the wrong thing to if we get
+ 2**32 messages sent with 32-bit sequence numbers. */
+ seqnum &= q->mask;
+
/* rule 1: expected sequence number */
- if (seqnum == QELEM(q,q->start+q->length-1)+1) {
+ expected = (QELEM(q,q->start+q->length-1)+1) & q->mask;
+ if (seqnum == expected) {
queue_insert(q, q->start+q->length-1, seqnum);
return(GSS_S_COMPLETE);
}
/* rule 2: > expected sequence number */
- if ((seqnum > QELEM(q,q->start+q->length-1)+1) ||
- (seqnum < q->firstnum)) {
+ if ((seqnum > expected)) {
queue_insert(q, q->start+q->length-1, seqnum);
if (q->do_replay && !q->do_sequence)
return(GSS_S_COMPLETE);
/* rule 3: seqnum < seqnum(first) */
if ((seqnum < QELEM(q,q->start)) &&
- (seqnum >= q->firstnum)) {
+ /* Is top bit of whatever width we're using set?
+
+ We used to check for greater than or equal to firstnum, but
+ (1) we've since switched to compute values relative to
+ firstnum, so the lowest we can have is 0, and (2) the effect
+ of the original scheme was highly dependent on whether
+ firstnum was close to either side of 0. (Consider
+ firstnum==0xFFFFFFFE and we miss three packets; the next
+ packet is *new* but would look old.)
+
+ This check should give us 2**31 or 2**63 messages "new", and
+ just as many "old". That's not quite right either. */
+ (seqnum & (1 + (q->mask >> 1)))
+ ) {
if (q->do_replay && !q->do_sequence)
return(GSS_S_OLD_TOKEN);
else
* mechanism in the token does not match the mech argument. buf and
* *body_size are left unmodified on error.
*/
-gss_int32 g_verify_token_header(mech, body_size, buf_in, tok_type, toksize_in)
+
+gss_int32 g_verify_token_header(mech, body_size, buf_in, tok_type, toksize_in,
+ wrapper_required)
gss_OID mech;
unsigned int *body_size;
unsigned char **buf_in;
int tok_type;
unsigned int toksize_in;
+ int wrapper_required;
{
unsigned char *buf = *buf_in;
int seqsize;
if ((toksize-=1) < 0)
return(G_BAD_TOK_HEADER);
- if (*buf++ != 0x60)
- return(G_BAD_TOK_HEADER);
+ if (*buf++ != 0x60) {
+ if (wrapper_required)
+ return(G_BAD_TOK_HEADER);
+ buf--;
+ toksize++;
+ goto skip_wrapper;
+ }
if ((seqsize = der_read_length(&buf, &toksize)) < 0)
return(G_BAD_TOK_HEADER);
if (! g_OID_equal(&toid, mech))
return G_WRONG_MECH;
+skip_wrapper:
if (tok_type != -1) {
if ((toksize-=2) < 0)
return(G_BAD_TOK_HEADER);
if ((*buf++ != ((tok_type>>8)&0xff)) ||
- (*buf++ != (tok_type&0xff)))
+ (*buf++ != (tok_type&0xff)))
return(G_WRONG_TOKID);
}
- *buf_in = buf;
- *body_size = toksize;
+ *buf_in = buf;
+ *body_size = toksize;
- return 0;
- }
+ return 0;
+}
+2003-12-13 Ken Raeburn <raeburn@mit.edu>
+ Sam Hartman <hartmans@avalanche-breakdown.mit.edu>
+
+ * k5sealv3.c: New file, implements Wrap and MIC tokens for CFX
+ extensions.
+ * gssapiP_krb5.h (struct _krb5_gss_ctx_id_rec): Added acceptor
+ subkey, 64-bit sequence numbers, checksum type, and hooks for
+ sending a bogus initial token for CFX testing. Changed some flags
+ into bitfields.
+ (gss_krb5int_make_seal_token_v3): Declare.
+ * Makefile.in (SRCS, OBJS, STLIBOBJS): Build it.
+ * accept_sec_context.c (krb5_gss_accept_sec_context): Add CFX
+ support. For G_WRONG_TOKID, send back an error token with
+ AP_ERR_MSG_TYPE code and return a CONTINUE_NEEDED indication.
+ Initialize new fields in context.
+ * delete_sec_context.c (krb5_gss_delete_sec_context): Free
+ acceptor subkey field.
+ * init_sec_context.c (get_credentials): Drop enctypes argument;
+ callers changed.
+ (get_requested_enctypes): Deleted.
+ (setup_enc): Combine some common sections. Do CFX initialization
+ for newer enctypes.
+ (new_connection) [CFX_EXERCISE]: If doing CFX, send a bogus
+ token. Delete the enctype list manipulation.
+ (mutual_auth): If CFX, save acceptor's subkey.
+ * k5seal.c (make_seal_token_v1): Sequence number is now 64 bits.
+ (kg_seal): Call out to _v3 code for CFX.
+ * k5unseal.c (kg_unseal): For CFX, adjust token id numbers and
+ call out to _v3 code.
+ * wrap_size_limit.c (krb5_gss_wrap_size_limit): Implement CFX
+ support.
+
+ * gssapiP_krb5.h (struct _krb5_gss_ctx_id_rec): Deleted fields
+ ctypes and nctypes.
+ * delete_sec_context.c, init_sec_context.c, ser_sctx.c: Removed
+ references.
+
2003-12-11 Alexandra Ellwood <lxs@mit.edu>
* acquire_cred.c, gssapi_krb5.c, gssapiP_krb5.h, set_ccache.c:
$(srcdir)/inq_cred.c \
$(srcdir)/inq_names.c \
$(srcdir)/k5seal.c \
+ $(srcdir)/k5sealv3.c \
$(srcdir)/k5unseal.c \
$(srcdir)/krb5_gss_glue.c \
$(srcdir)/process_context_token.c \
$(OUTPRE)inq_cred.$(OBJEXT) \
$(OUTPRE)inq_names.$(OBJEXT) \
$(OUTPRE)k5seal.$(OBJEXT) \
+ $(OUTPRE)k5sealv3.$(OBJEXT) \
$(OUTPRE)k5unseal.$(OBJEXT) \
$(OUTPRE)krb5_gss_glue.$(OBJEXT) \
$(OUTPRE)process_context_token.$(OBJEXT) \
inq_cred.o \
inq_names.o \
k5seal.o \
+ k5sealv3.o \
k5unseal.o \
krb5_gss_glue.o \
process_context_token.o \
#endif
#include <assert.h>
+#ifdef CFX_EXERCISE
+#define CFX_ACCEPTOR_SUBKEY (time(0) & 1)
+#else
+#define CFX_ACCEPTOR_SUBKEY 1
+#endif
+
/* Decode, decrypt and store the forwarded creds in the local ccache. */
static krb5_error_code
rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
if ((retval = krb5_auth_con_init(context, &new_auth_ctx)))
goto cleanup;
krb5_auth_con_setflags(context, new_auth_ctx, 0);
- if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf, &creds, NULL)))
+ if ((retval = krb5_rd_cred(context, new_auth_ctx, inbuf,
+ &creds, NULL)))
goto cleanup;
}
if (!(code = g_verify_token_header((gss_OID) gss_mech_krb5,
&(ap_req.length),
&ptr, KG_TOK_CTX_AP_REQ,
- input_token->length))) {
+ input_token->length, 1))) {
mech_used = gss_mech_krb5;
} else if ((code == G_WRONG_MECH) &&
!(code = g_verify_token_header((gss_OID) gss_mech_krb5_old,
&(ap_req.length),
&ptr, KG_TOK_CTX_AP_REQ,
- input_token->length))) {
+ input_token->length, 1))) {
/*
* Previous versions of this library used the old mech_id
* and some broken behavior (wrong IV on checksum
* old behavior.
*/
mech_used = gss_mech_krb5_old;
+ } else if (code == G_WRONG_TOKID) {
+ major_status = GSS_S_CONTINUE_NEEDED;
+ code = KRB5KRB_AP_ERR_MSG_TYPE;
+ mech_used = gss_mech_krb5;
+ goto fail;
} else {
major_status = GSS_S_DEFECTIVE_TOKEN;
goto fail;
goto fail;
}
+ ctx->proto = 0;
switch(ctx->subkey->enctype) {
case ENCTYPE_DES_CBC_MD5:
case ENCTYPE_DES_CBC_CRC:
/*SUPPRESS 113*/
ctx->enc->contents[i] ^= 0xf0;
- if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
- major_status = GSS_S_FAILURE;
- goto fail;
- }
-
- break;
+ goto copy_subkey_to_seq;
case ENCTYPE_DES3_CBC_SHA1:
ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
ctx->sealalg = SEAL_ALG_DES3KD;
/* fill in the encryption descriptors */
-
+ copy_subkey:
if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->enc))) {
major_status = GSS_S_FAILURE;
goto fail;
}
-
+ copy_subkey_to_seq:
if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq))) {
major_status = GSS_S_FAILURE;
goto fail;
}
-
break;
- case ENCTYPE_ARCFOUR_HMAC:
- ctx->signalg = SGN_ALG_HMAC_MD5 ;
- ctx->cksum_size = 8;
- ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
-
- code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
- if (code)
- goto fail;
- code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
- if (code) {
- krb5_free_keyblock (context, ctx->enc);
- goto fail;
- }
- break;
+
+ case ENCTYPE_ARCFOUR_HMAC:
+ ctx->signalg = SGN_ALG_HMAC_MD5 ;
+ ctx->cksum_size = 8;
+ ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
+ goto copy_subkey;
default:
- code = KRB5_BAD_ENCTYPE;
- goto fail;
+ ctx->signalg = -1;
+ ctx->sealalg = -1;
+ ctx->proto = 1;
+ code = krb5int_c_mandatory_cksumtype(context, ctx->subkey->enctype,
+ &ctx->cksumtype);
+ if (code)
+ goto fail;
+ code = krb5_c_checksum_length(context, ctx->cksumtype,
+ &ctx->cksum_size);
+ if (code)
+ goto fail;
+ ctx->have_acceptor_subkey = 0;
+ goto copy_subkey;
}
ctx->endtime = ticket->enc_part2->times.endtime;
krb5_free_ticket(context, ticket); /* Done with ticket */
- krb5_auth_con_getremoteseqnumber(context, auth_context, &ctx->seq_recv);
+ {
+ krb5_ui_4 seq_temp;
+ krb5_auth_con_getremoteseqnumber(context, auth_context, &seq_temp);
+ ctx->seq_recv = seq_temp;
+ }
if ((code = krb5_timeofday(context, &now))) {
major_status = GSS_S_FAILURE;
g_order_init(&(ctx->seqstate), ctx->seq_recv,
(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
- (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0);
+ (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
/* at this point, the entire context structure is filled in,
so it can be released. */
if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
unsigned char * ptr3;
+ krb5_ui_4 seq_temp;
+ int cfx_generate_subkey;
+
+ if (ctx->proto == 1)
+ cfx_generate_subkey = CFX_ACCEPTOR_SUBKEY;
+ else
+ cfx_generate_subkey = 0;
+
+ if (cfx_generate_subkey) {
+ krb5_int32 acflags;
+ code = krb5_auth_con_getflags(context, auth_context, &acflags);
+ if (code == 0) {
+ acflags |= KRB5_AUTH_CONTEXT_USE_SUBKEY;
+ code = krb5_auth_con_setflags(context, auth_context, acflags);
+ }
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+
if ((code = krb5_mk_rep(context, auth_context, &ap_rep))) {
major_status = GSS_S_FAILURE;
goto fail;
}
- krb5_auth_con_getlocalseqnumber(context, auth_context,
- &ctx->seq_send);
+ krb5_auth_con_getlocalseqnumber(context, auth_context, &seq_temp);
+ ctx->seq_send = seq_temp & 0xffffffffL;
+
+ if (cfx_generate_subkey) {
+ /* Get the new acceptor subkey. With the code above, there
+ should always be one if we make it to this point. */
+ code = krb5_auth_con_getsendsubkey(context, auth_context,
+ &ctx->acceptor_subkey);
+ if (code != 0) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ code = krb5int_c_mandatory_cksumtype(context,
+ ctx->acceptor_subkey->enctype,
+ &ctx->acceptor_subkey_cksumtype);
+ if (code) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ ctx->have_acceptor_subkey = 1;
+ }
/* the reply token hasn't been sent yet, but that's ok. */
ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
if (ap_rep.data)
krb5_free_data_contents(context, &ap_rep);
- if (!GSS_ERROR(major_status))
+ if (!GSS_ERROR(major_status) && major_status != GSS_S_CONTINUE_NEEDED)
return(major_status);
/* from here on is the real "fail" code */
krb5_free_ap_req(context, request);
}
- if (cred && (gss_flags & GSS_C_MUTUAL_FLAG)) {
+ if (cred
+ && ((gss_flags & GSS_C_MUTUAL_FLAG)
+ || (major_status == GSS_S_CONTINUE_NEEDED))) {
unsigned int tmsglen;
int toktype;
*/
memset(&krb_error_data, 0, sizeof(krb_error_data));
- code -= ERROR_TABLE_BASE_krb5;
+ code -= ERROR_TABLE_BASE_krb5;
if (code < 0 || code > 128)
code = 60 /* KRB_ERR_GENERIC */;
(void) krb5_us_timeofday(context, &krb_error_data.stime,
&krb_error_data.susec);
krb_error_data.server = cred->princ;
-
+
code = krb5_mk_error(context, &krb_error_data, &scratch);
if (code)
return (major_status);
krb5_free_principal(context, ctx->there);
if (ctx->subkey)
krb5_free_keyblock(context, ctx->subkey);
+ if (ctx->acceptor_subkey)
+ krb5_free_keyblock(context, ctx->acceptor_subkey);
if (ctx->auth_context) {
(void)krb5_auth_con_setrcache(context, ctx->auth_context, NULL);
if (ctx->mech_used)
gss_release_oid(minor_status, &ctx->mech_used);
- if (ctx->ctypes)
- xfree(ctx->ctypes);
-
/* Zero out context */
memset(ctx, 0, sizeof(*ctx));
xfree(ctx);
#include "gssapi_krb5.h"
#include "gssapi_err_krb5.h"
+/* for debugging */
+#undef CFX_EXERCISE
+
/** constants **/
#define CKSUMTYPE_KG_CB 0x8003
SEAL_ALG_DES3KD = 0x0002
};
+/* for 3DES */
#define KG_USAGE_SEAL 22
#define KG_USAGE_SIGN 23
#define KG_USAGE_SEQ 24
+/* for draft-ietf-krb-wg-gssapi-cfx-01 */
+#define KG_USAGE_ACCEPTOR_SEAL 22
+#define KG_USAGE_ACCEPTOR_SIGN 23
+#define KG_USAGE_INITIATOR_SEAL 24
+#define KG_USAGE_INITIATOR_SIGN 25
+
enum qop {
GSS_KRB5_INTEG_C_QOP_MD5 = 0x0001, /* *partial* MD5 = "MD2.5" */
GSS_KRB5_INTEG_C_QOP_DES_MD5 = 0x0002,
} krb5_gss_cred_id_rec, *krb5_gss_cred_id_t;
typedef struct _krb5_gss_ctx_id_rec {
- int initiate; /* nonzero if initiating, zero if accepting */
+ unsigned int initiate : 1; /* nonzero if initiating, zero if accepting */
+ unsigned int established : 1;
+ unsigned int big_endian : 1;
+ unsigned int have_acceptor_subkey : 1;
+ unsigned int seed_init : 1; /* XXX tested but never actually set */
+#ifdef CFX_EXERCISE
+ unsigned int testing_unknown_tokid : 1; /* for testing only */
+#endif
OM_uint32 gss_flags;
- int seed_init;
unsigned char seed[16];
krb5_principal here;
krb5_principal there;
krb5_keyblock *subkey;
int signalg;
- int cksum_size;
+ size_t cksum_size;
int sealalg;
krb5_keyblock *enc;
krb5_keyblock *seq;
/* XXX these used to be signed. the old spec is inspecific, and
the new spec specifies unsigned. I don't believe that the change
affects the wire encoding. */
- krb5_ui_4 seq_send;
- krb5_ui_4 seq_recv;
+ gssint_uint64 seq_send;
+ gssint_uint64 seq_recv;
void *seqstate;
- int established;
- int big_endian;
krb5_auth_context auth_context;
gss_OID_desc *mech_used;
- int nctypes;
- krb5_cksumtype *ctypes;
+ /* Protocol spec revision
+ 0 => RFC 1964 with 3DES and RC4 enhancements
+ 1 => draft-ietf-krb-wg-gssapi-cfx-01
+ No others defined so far. */
+ int proto;
+ krb5_cksumtype cksumtype; /* for "main" subkey */
+ krb5_keyblock *acceptor_subkey; /* CFX only */
+ krb5_cksumtype acceptor_subkey_cksumtype;
+#ifdef CFX_EXERCISE
+ gss_buffer_desc init_token;
+#endif
} krb5_gss_ctx_id_rec, *krb5_gss_ctx_id_t;
extern void *kg_vdb;
(gss_OID oid
);
+krb5_error_code gss_krb5int_make_seal_token_v3(krb5_context,
+ krb5_gss_ctx_id_rec *,
+ const gss_buffer_desc *,
+ gss_buffer_t,
+ int, int);
+
#endif /* _GSSAPIP_KRB5_H_ */
* ccache.
*/
static krb5_error_code get_credentials(context, cred, server, now,
- endtime, enctypes, out_creds)
+ endtime, out_creds)
krb5_context context;
krb5_gss_cred_id_t cred;
krb5_principal server;
krb5_timestamp now;
krb5_timestamp endtime;
- const krb5_enctype *enctypes;
krb5_creds **out_creds;
{
krb5_error_code code;
in_creds.keyblock.enctype = 0;
- code = krb5_set_default_tgs_enctypes (context, enctypes);
- if (code)
- goto cleanup;
- code = krb5_get_credentials(context, 0, cred->ccache,
+ code = krb5_get_credentials(context, 0, cred->ccache,
&in_creds, out_creds);
if (code)
goto cleanup;
return (code);
}
-/*
- * get_requested_enctypes
- *
- * Filter the krb5 library's default enctype list with the set of
- * enctypes we support for GSSAPI.
- */
-static krb5_error_code
-get_requested_enctypes(
- krb5_context context,
- krb5_enctype **ret_enctypes)
-{
- krb5_error_code code;
- int i, j, k;
- int is_duplicate_enctype;
- int is_wanted_enctype;
- static const krb5_enctype wanted_enctypes[] = {
-#if 1
- ENCTYPE_DES3_CBC_SHA1,
-#endif
- ENCTYPE_ARCFOUR_HMAC,
- ENCTYPE_DES_CBC_CRC,
- ENCTYPE_DES_CBC_MD5, ENCTYPE_DES_CBC_MD4,
- };
-#define N_WANTED_ENCTYPES (sizeof(wanted_enctypes)/sizeof(wanted_enctypes[0]))
- krb5_enctype *default_enctypes = 0;
- krb5_enctype *requested_enctypes;
-
- *ret_enctypes = malloc((N_WANTED_ENCTYPES + 1) * sizeof(krb5_enctype));
- if (*ret_enctypes == NULL)
- return ENOMEM;
-
- code = krb5_get_tgs_ktypes (context, 0, &default_enctypes);
- if (code) {
- free(*ret_enctypes);
- *ret_enctypes = NULL;
- return code;
- }
- requested_enctypes = *ret_enctypes;
-
- /* "i" denotes *next* slot to fill. Don't forget to save room
- for a trailing zero. */
- i = 0;
- for (j = 0;
- (default_enctypes[j] != 0
- /* This part should be redundant, but let's be paranoid. */
- && i < N_WANTED_ENCTYPES);
- j++) {
-
- krb5_enctype e = default_enctypes[j];
-
- /* Is this enctype one of the ones we want for GSSAPI? */
- is_wanted_enctype = 0;
- for (k = 0; k < N_WANTED_ENCTYPES; k++) {
- if (wanted_enctypes[k] == e) {
- is_wanted_enctype = 1;
- break;
- }
- }
- /* If unwanted, go to the next one. */
- if (!is_wanted_enctype)
- continue;
-
- /* Is this enctype already in the list of enctypes to
- request? (Is it a duplicate?) */
- is_duplicate_enctype = 0;
- for (k = 0; k < i; k++) {
- if (requested_enctypes[k] == e) {
- is_duplicate_enctype = 1;
- break;
- }
- }
- /* If it is not a duplicate, add it. */
- if (!is_duplicate_enctype)
- requested_enctypes[i++] = e;
- }
- krb5_free_ktypes(context, default_enctypes);
- requested_enctypes[i++] = 0;
- return GSS_S_COMPLETE;
-}
-
/*
* setup_enc
*
krb5_error_code code;
int i;
+ ctx->have_acceptor_subkey = 0;
+ ctx->proto = 0;
+ ctx->cksumtype = 0;
switch(ctx->subkey->enctype) {
case ENCTYPE_DES_CBC_MD5:
case ENCTYPE_DES_CBC_MD4:
goto fail;
for (i=0; i<ctx->enc->length; i++)
- /*SUPPRESS 113*/
ctx->enc->contents[i] ^= 0xf0;
- if ((code = krb5_copy_keyblock(context, ctx->subkey, &ctx->seq)))
- goto fail;
-
- break;
+ goto copy_subkey_to_seq;
case ENCTYPE_DES3_CBC_SHA1:
+ /* MIT extension */
ctx->subkey->enctype = ENCTYPE_DES3_CBC_RAW;
ctx->signalg = SGN_ALG_HMAC_SHA1_DES3_KD;
ctx->cksum_size = 20;
ctx->sealalg = SEAL_ALG_DES3KD;
+ copy_subkey:
code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
if (code)
goto fail;
+ copy_subkey_to_seq:
code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
if (code) {
krb5_free_keyblock (context, ctx->enc);
goto fail;
}
break;
+
case ENCTYPE_ARCFOUR_HMAC:
+ /* Microsoft extension */
ctx->signalg = SGN_ALG_HMAC_MD5 ;
ctx->cksum_size = 8;
ctx->sealalg = SEAL_ALG_MICROSOFT_RC4 ;
- code = krb5_copy_keyblock (context, ctx->subkey, &ctx->enc);
- if (code)
- goto fail;
- code = krb5_copy_keyblock (context, ctx->subkey, &ctx->seq);
- if (code) {
- krb5_free_keyblock (context, ctx->enc);
- goto fail;
- }
- break;
-#if 0
- case ENCTYPE_DES3_CBC_MD5:
- enctype = ENCTYPE_DES3_CBC_RAW;
- ctx->signalg = 3;
- ctx->cksum_size = 16;
- ctx->sealalg = 1;
- break;
-#endif
+ goto copy_subkey;
+
default:
- *minor_status = KRB5_BAD_ENCTYPE;
- return GSS_S_FAILURE;
+ /* Fill some fields we shouldn't be using on this path
+ with garbage. */
+ ctx->signalg = -10;
+ ctx->sealalg = -10;
+
+ ctx->proto = 1;
+ code = krb5int_c_mandatory_cksumtype(context, ctx->subkey->enctype,
+ &ctx->cksumtype);
+ if (code)
+ goto fail;
+ code = krb5_c_checksum_length(context, ctx->cksumtype,
+ &ctx->cksum_size);
+ if (code)
+ goto fail;
+ goto copy_subkey;
}
fail:
*minor_status = code;
/* complain if the input token is non-null */
if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
- *minor_status = 0;
- return(GSS_S_DEFECTIVE_TOKEN);
+#ifdef CFX_EXERCISE
+ if (*context_handle != GSS_C_NO_CONTEXT
+ && ((krb5_gss_ctx_id_t)*context_handle)->testing_unknown_tokid) {
+ /* XXX Should check for a KRB_ERROR message that we can
+ parse, and which contains the expected error code. */
+ ctx = (krb5_gss_ctx_id_t)*context_handle;
+ goto resume_after_testing;
+ }
+#endif
+ *minor_status = 0;
+ return(GSS_S_DEFECTIVE_TOKEN);
}
/* create the ctx */
ctx->seed_init = 0;
ctx->big_endian = 0; /* all initiators do little-endian, as per spec */
ctx->seqstate = 0;
- ctx->nctypes = 0;
- ctx->ctypes = 0;
if ((code = krb5_timeofday(context, &now)))
goto fail;
&ctx->there)))
goto fail;
- code = get_requested_enctypes(context, &requested_enctypes);
- if (code)
- goto fail;
-
code = get_credentials(context, cred, ctx->there, now,
- ctx->endtime, requested_enctypes, &k_cred);
- free(requested_enctypes);
+ ctx->endtime, &k_cred);
if (code)
goto fail;
{
/* gsskrb5 v1 */
+ krb5_ui_4 seq_temp;
if ((code = make_ap_req_v1(context, ctx,
cred, k_cred, input_chan_bindings,
mech_type, &token))) {
goto fail;
}
- krb5_auth_con_getlocalseqnumber(context, ctx->auth_context,
- &ctx->seq_send);
+ krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_temp);
+ ctx->seq_send = seq_temp;
krb5_auth_con_getsendsubkey(context, ctx->auth_context,
&ctx->subkey);
}
*context_handle = (gss_ctx_id_t) ctx;
ctx_free = 0;
+#ifdef CFX_EXERCISE
+ if (ctx->proto == 1
+ /* I think the RPC code may be broken. Don't mess around
+ if we're authenticating to "kadmin/whatever". */
+ && ctx->there->data[0].data[0] != 'k'
+ /* I *know* the FTP server code is broken. */
+ && ctx->there->data[0].data[0] != 'f'
+ ) {
+ /* Create a bogus token and return it, with status
+ GSS_S_CONTINUE_NEEDED. Save enough data that we can resume
+ on the next call. */
+ static const unsigned char hack_token[20] = {
+ 0x60, 0x12, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x12, 0x01, 0x02, 0x02, 0x12, 0x34, 0x68,
+ 0x65, 0x6c, 0x6c, 0x6f
+ };
+ ctx->testing_unknown_tokid = 1;
+ ctx->init_token = token;
+ token.value = malloc(20);
+ token.length = 20;
+ if (token.value == NULL) {
+ /* Skip testing. We'll probably die soon enough, but let's
+ not do it because we couldn't exercise this code
+ path. */
+ goto resume_after_testing;
+ }
+ memcpy(token.value, hack_token, sizeof(hack_token));
+ /* Can just fall through into the normal return path, because
+ it'll always return GSS_S_CONTINUE_NEEDED because we're
+ doing mutual authentication. */
+ }
+ if (0) {
+ resume_after_testing:
+ token = ctx->init_token;
+ ctx->init_token.value = 0;
+ ctx->init_token.length = 0;
+ ctx->testing_unknown_tokid = 0;
+ ctx_free = 0;
+ }
+#endif
+
/* compute time_rec */
if (time_rec) {
if ((code = krb5_timeofday(context, &now)))
ctx->seq_recv = ctx->seq_send;
g_order_init(&(ctx->seqstate), ctx->seq_recv,
(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
- (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0);
+ (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0, ctx->proto);
ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
ctx->established = 1;
return(GSS_S_COMPLETE);
krb5_free_principal(context, ctx_free->there);
if (ctx_free->subkey)
krb5_free_keyblock(context, ctx_free->subkey);
- if (ctx_free->ctypes)
- krb5_free_cksumtypes(context, ctx_free->ctypes);
xfree(ctx_free);
} else
(void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
if (g_verify_token_header((gss_OID) ctx->mech_used,
&(ap_rep.length),
&ptr, KG_TOK_CTX_AP_REP,
- input_token->length)) {
+ input_token->length, 1)) {
if (g_verify_token_header((gss_OID) ctx->mech_used,
&(ap_rep.length),
&ptr, KG_TOK_CTX_ERROR,
- input_token->length) == 0) {
+ input_token->length, 1) == 0) {
/* Handle a KRB_ERROR message from the server */
ctx->seq_recv = ap_rep_data->seq_number;
g_order_init(&(ctx->seqstate), ctx->seq_recv,
(ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
- (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0);
+ (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) !=0, ctx->proto);
+
+ if (ctx->proto == 1 && ap_rep_data->subkey) {
+ /* Keep acceptor's subkey. */
+ ctx->have_acceptor_subkey = 1;
+ code = krb5_copy_keyblock(context, ap_rep_data->subkey,
+ &ctx->acceptor_subkey);
+ if (code)
+ goto fail;
+ code = krb5int_c_mandatory_cksumtype(context,
+ ctx->acceptor_subkey->enctype,
+ &ctx->acceptor_subkey_cksumtype);
+ if (code)
+ goto fail;
+ }
/* free the ap_rep_data */
krb5_free_ap_rep_enc_part(context, ap_rep_data);
/* is this a new connection or not? */
/*SUPPRESS 29*/
- if (*context_handle == GSS_C_NO_CONTEXT) {
+ if (*context_handle == GSS_C_NO_CONTEXT
+#ifdef CFX_EXERCISE
+ || ((krb5_gss_ctx_id_t)*context_handle)->testing_unknown_tokid
+#endif
+ ) {
major_status = new_connection(minor_status, cred, context_handle,
target_name, mech_type, req_flags,
time_req, input_chan_bindings,
make_seal_token_v1 (krb5_context context,
krb5_keyblock *enc,
krb5_keyblock *seq,
- krb5_ui_4 *seqnum,
+ gssint_uint64 *seqnum,
int direction,
gss_buffer_t text,
gss_buffer_t token,
/* that's it. return the token */
(*seqnum)++;
+ *seqnum &= 0xffffffffL;
token->length = tlen;
token->value = (void *) t;
output_message_buffer->length = 0;
output_message_buffer->value = NULL;
- /* only default qop or matching established cryptosystem is allowed */
-
-#if 0
- switch (qop_req & GSS_KRB5_CONF_C_QOP_MASK) {
- case GSS_C_QOP_DEFAULT:
- break;
- default:
- unknown_qop:
- *minor_status = (OM_uint32) G_UNKNOWN_QOP;
- return GSS_S_FAILURE;
- case GSS_KRB5_CONF_C_QOP_DES:
- if (ctx->sealalg != SEAL_ALG_DES) {
- bad_qop:
- *minor_status = (OM_uint32) G_BAD_QOP;
- return GSS_S_FAILURE;
- }
- break;
- case GSS_KRB5_CONF_C_QOP_DES3:
- if (ctx->sealalg != SEAL_ALG_DES3)
- goto bad_qop;
- break;
- }
- switch (qop_req & GSS_KRB5_INTEG_C_QOP_MASK) {
- case GSS_C_QOP_DEFAULT:
- break;
- default:
- goto unknown_qop;
- case GSS_KRB5_INTEG_C_QOP_MD5:
- case GSS_KRB5_INTEG_C_QOP_DES_MD5:
- case GSS_KRB5_INTEG_C_QOP_DES_MAC:
- if (ctx->sealalg != SEAL_ALG_DES)
- goto bad_qop;
- break;
- case GSS_KRB5_INTEG_C_QOP_HMAC_SHA1:
- if (ctx->sealalg != SEAL_ALG_DES3KD)
- goto bad_qop;
- break;
- }
-#else
+ /* Only default qop or matching established cryptosystem is allowed.
+
+ There are NO EXTENSIONS to this set for AES and friends! The
+ new spec says "just use 0". The old spec plus extensions would
+ actually allow for certain non-zero values. Fix this to handle
+ them later. */
if (qop_req != 0) {
*minor_status = (OM_uint32) G_UNKNOWN_QOP;
return GSS_S_FAILURE;
}
-#endif
/* validate the context handle */
if (! kg_validate_ctx_id(context_handle)) {
return(GSS_S_FAILURE);
}
- code = make_seal_token_v1(context, ctx->enc, ctx->seq,
- &ctx->seq_send, ctx->initiate,
- input_message_buffer, output_message_buffer,
- ctx->signalg, ctx->cksum_size, ctx->sealalg,
- conf_req_flag, toktype, ctx->big_endian,
- ctx->mech_used);
+ switch (ctx->proto)
+ {
+ case 0:
+ code = make_seal_token_v1(context, ctx->enc, ctx->seq,
+ &ctx->seq_send, ctx->initiate,
+ input_message_buffer, output_message_buffer,
+ ctx->signalg, ctx->cksum_size, ctx->sealalg,
+ conf_req_flag, toktype, ctx->big_endian,
+ ctx->mech_used);
+ break;
+ case 1:
+ code = gss_krb5int_make_seal_token_v3(context, ctx,
+ input_message_buffer,
+ output_message_buffer,
+ conf_req_flag, toktype);
+ break;
+ default:
+ code = G_UNKNOWN_QOP; /* XXX */
+ break;
+ }
if (code) {
*minor_status = code;
--- /dev/null
+/* Copyright 2003 MIT. All rights reserved. */
+/* draft-ietf-krb-wg-gssapi-cfx-02 plus discussed changes */
+
+#include <assert.h>
+#include "k5-platform.h" /* for 64-bit support */
+#include "k5-int.h" /* for zap() */
+#include "gssapiP_krb5.h"
+#include <stdarg.h>
+
+static int
+rotate_left (void *ptr, size_t bufsiz, size_t rc)
+{
+ /* Optimize for receiving. After some debugging is done, the MIT
+ implementation won't do any rotates on sending, and while
+ debugging, they'll be randomly chosen.
+
+ Return 1 for success, 0 for failure (ENOMEM). */
+ void *tbuf;
+
+ if (bufsiz == 0)
+ return 1;
+ rc = rc % bufsiz;
+ if (rc == 0)
+ return 1;
+
+ tbuf = malloc(rc);
+ if (tbuf == 0)
+ return 0;
+ memcpy(tbuf, ptr, rc);
+ memmove(ptr, (char *)ptr + rc, bufsiz - rc);
+ memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
+ free(tbuf);
+ return 1;
+}
+
+static const gss_buffer_desc empty_message = { 0, 0 };
+
+#define FLAG_SENDER_IS_ACCEPTOR 0x01
+#define FLAG_WRAP_CONFIDENTIAL 0x02
+#define FLAG_ACCEPTOR_SUBKEY 0x04
+
+krb5_error_code
+gss_krb5int_make_seal_token_v3 (krb5_context context,
+ krb5_gss_ctx_id_rec *ctx,
+ const gss_buffer_desc * message,
+ gss_buffer_t token,
+ int conf_req_flag, int toktype)
+{
+ size_t bufsize = 16;
+ unsigned char *outbuf = 0;
+ krb5_error_code err;
+ int key_usage;
+ unsigned char acceptor_flag;
+ const gss_buffer_desc *message2 = message;
+ size_t rrc, ec;
+ unsigned short tok_id;
+ krb5_checksum sum;
+ krb5_keyblock *key;
+
+ assert(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
+ assert(ctx->big_endian == 0);
+
+ acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
+ key_usage = (toktype == KG_TOK_WRAP_MSG
+ ? (ctx->initiate
+ ? KG_USAGE_INITIATOR_SEAL
+ : KG_USAGE_ACCEPTOR_SEAL)
+ : (ctx->initiate
+ ? KG_USAGE_INITIATOR_SIGN
+ : KG_USAGE_ACCEPTOR_SIGN));
+ if (ctx->have_acceptor_subkey) {
+ key = ctx->acceptor_subkey;
+ } else {
+ key = ctx->enc;
+ }
+
+#ifdef CFX_EXERCISE
+ {
+ static int initialized = 0;
+ if (!initialized) {
+ srand(time(0));
+ initialized = 1;
+ }
+ }
+#endif
+
+ if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
+ krb5_data plain;
+ krb5_enc_data cipher;
+ size_t ec_max;
+
+ /* 300: Adds some slop. */
+ if (SIZE_MAX - 300 < message->length)
+ return ENOMEM;
+ ec_max = SIZE_MAX - message->length - 300;
+ if (ec_max > 0xffff)
+ ec_max = 0xffff;
+#ifdef CFX_EXERCISE
+ /* For testing only. For performance, always set ec = 0. */
+ ec = ec_max & rand();
+#else
+ ec = 0;
+#endif
+ plain.length = message->length + 16 + ec;
+ plain.data = malloc(message->length + 16 + ec);
+ if (plain.data == NULL)
+ return ENOMEM;
+
+ /* Get size of ciphertext. */
+ bufsize = 16 + krb5_encrypt_size (plain.length, ctx->enc->enctype);
+ /* Allocate space for header plus encrypted data. */
+ outbuf = malloc(bufsize);
+ if (outbuf == NULL) {
+ free(plain.data);
+ return ENOMEM;
+ }
+
+ /* TOK_ID */
+ store_16_be(0x0504, outbuf);
+ /* flags */
+ outbuf[2] = (acceptor_flag
+ | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
+ | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
+ /* filler */
+ outbuf[3] = 0xff;
+ /* EC */
+ store_16_be(ec, outbuf+4);
+ /* RRC */
+ store_16_be(0, outbuf+6);
+ store_64_be(ctx->seq_send, outbuf+8);
+
+ memcpy(plain.data, message->value, message->length);
+ memset(plain.data + message->length, 'x', ec);
+ memcpy(plain.data + message->length + ec, outbuf, 16);
+
+ cipher.ciphertext.data = outbuf + 16;
+ cipher.ciphertext.length = bufsize - 16;
+ cipher.enctype = key->enctype;
+ err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher);
+ zap(plain.data, plain.length);
+ free(plain.data);
+ plain.data = 0;
+ if (err)
+ goto error;
+
+ /* Now that we know we're returning a valid token.... */
+ ctx->seq_send++;
+
+#ifdef CFX_EXERCISE
+ rrc = rand() & 0xffff;
+ if (rotate_left(outbuf+16, bufsize-16,
+ (bufsize-16) - (rrc % (bufsize - 16))))
+ store_16_be(rrc, outbuf+6);
+ /* If the rotate fails, don't worry about it. */
+#endif
+ } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
+ krb5_data plain;
+
+ /* Here, message is the application-supplied data; message2 is
+ what goes into the output token. They may be the same, or
+ message2 may be empty (for MIC). */
+
+ tok_id = 0x0504;
+
+ wrap_with_checksum:
+ plain.length = message->length + 16;
+ plain.data = malloc(message->length + 16);
+ if (plain.data == NULL)
+ return ENOMEM;
+
+ if (ctx->cksum_size > 0xffff)
+ abort();
+
+ bufsize = 16 + message2->length + ctx->cksum_size;
+ outbuf = malloc(bufsize);
+ if (outbuf == NULL) {
+ free(plain.data);
+ plain.data = 0;
+ err = ENOMEM;
+ goto error;
+ }
+
+ /* TOK_ID */
+ store_16_be(tok_id, outbuf);
+ /* flags */
+ outbuf[2] = (acceptor_flag
+ | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
+ /* filler */
+ outbuf[3] = 0xff;
+ if (toktype == KG_TOK_WRAP_MSG) {
+ /* Use 0 for checksum calculation, substitute
+ checksum length later. */
+ /* EC */
+ store_16_be(0, outbuf+4);
+ /* RRC */
+ store_16_be(0, outbuf+6);
+ } else {
+ /* MIC and DEL store 0xFF in EC and RRC. */
+ store_16_be(0xffff, outbuf+4);
+ store_16_be(0xffff, outbuf+6);
+ }
+ store_64_be(ctx->seq_send, outbuf+8);
+
+ memcpy(plain.data, message->value, message->length);
+ memcpy(plain.data + message->length, outbuf, 16);
+
+ /* Fill in the output token -- data contents, if any, and
+ space for the checksum. */
+ if (message2->length)
+ memcpy(outbuf + 16, message2->value, message2->length);
+
+ sum.contents = outbuf + 16 + message2->length;
+ sum.length = ctx->cksum_size;
+
+ err = krb5_c_make_checksum(context, ctx->cksumtype, key,
+ key_usage, &plain, &sum);
+ zap(plain.data, plain.length);
+ free(plain.data);
+ plain.data = 0;
+ if (err) {
+ zap(outbuf,bufsize);
+ free(outbuf);
+ goto error;
+ }
+ if (sum.length != ctx->cksum_size)
+ abort();
+ memcpy(outbuf + 16 + message2->length, sum.contents, ctx->cksum_size);
+ krb5_free_checksum_contents(context, &sum);
+ sum.contents = 0;
+ /* Now that we know we're actually generating the token... */
+ ctx->seq_send++;
+
+ if (toktype == KG_TOK_WRAP_MSG) {
+#ifdef CFX_EXERCISE
+ rrc = rand() & 0xffff;
+ /* If the rotate fails, don't worry about it. */
+ if (rotate_left(outbuf+16, bufsize-16,
+ (bufsize-16) - (rrc % (bufsize - 16))))
+ store_16_be(rrc, outbuf+6);
+#endif
+ /* Fix up EC field. */
+ store_16_be(ctx->cksum_size, outbuf+4);
+ } else {
+ store_16_be(0xffff, outbuf+6);
+ }
+ } else if (toktype == KG_TOK_MIC_MSG) {
+ tok_id = 0x0404;
+ message2 = &empty_message;
+ goto wrap_with_checksum;
+ } else if (toktype == KG_TOK_DEL_CTX) {
+ tok_id = 0x0405;
+ message = message2 = &empty_message;
+ goto wrap_with_checksum;
+ } else
+ abort();
+
+ token->value = outbuf;
+ token->length = bufsize;
+ return 0;
+
+error:
+ free(outbuf);
+ token->value = NULL;
+ token->length = 0;
+ return err;
+}
+
+/* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
+ conf_state is only valid if SEAL. */
+
+OM_uint32
+gss_krb5int_unseal_token_v3(krb5_context *contextptr,
+ OM_uint32 *minor_status,
+ krb5_gss_ctx_id_rec *ctx,
+ unsigned char *ptr, int bodysize,
+ gss_buffer_t message_buffer,
+ int *conf_state, int *qop_state, int toktype)
+{
+ krb5_context context = *contextptr;
+ krb5_data plain;
+ gssint_uint64 seqnum;
+ size_t ec, rrc;
+ int key_usage;
+ unsigned char acceptor_flag;
+ krb5_checksum sum;
+ krb5_error_code err;
+ krb5_boolean valid;
+ krb5_keyblock *key;
+
+ assert(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
+ assert(ctx->big_endian == 0);
+ assert(ctx->proto == 1);
+
+ if (qop_state)
+ *qop_state = GSS_C_QOP_DEFAULT;
+
+ acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
+ key_usage = (toktype == KG_TOK_WRAP_MSG
+ ? (!ctx->initiate
+ ? KG_USAGE_INITIATOR_SEAL
+ : KG_USAGE_ACCEPTOR_SEAL)
+ : (!ctx->initiate
+ ? KG_USAGE_INITIATOR_SIGN
+ : KG_USAGE_ACCEPTOR_SIGN));
+
+ /* Oops. I wrote this code assuming ptr would be at the start of
+ the token header. */
+ ptr -= 2;
+ bodysize += 2;
+
+ if (bodysize < 16) {
+ defective:
+ *minor_status = 0;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
+ *minor_status = G_BAD_DIRECTION;
+ return GSS_S_BAD_SIG;
+ }
+
+ /* Two things to note here.
+
+ First, we can't really enforce the use of the acceptor's subkey,
+ if we're the acceptor; the initiator may have sent messages
+ before getting the subkey. We could probably enforce it if
+ we're the initiator.
+
+ Second, if someone tweaks the code to not set the flag telling
+ the krb5 library to generate a new subkey in the AP-REP
+ message, the MIT library may include a subkey anyways --
+ namely, a copy of the AP-REQ subkey, if it was provided. So
+ the initiator may think we wanted a subkey, and set the flag,
+ even though we weren't trying to set the subkey. The "other"
+ key, the one not asserted by the acceptor, will have the same
+ value in that case, though, so we can just ignore the flag. */
+ if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
+ key = ctx->acceptor_subkey;
+ } else {
+ key = ctx->enc;
+ }
+
+ if (toktype == KG_TOK_WRAP_MSG) {
+ if (load_16_be(ptr) != 0x0504)
+ goto defective;
+ if (ptr[3] != 0xff)
+ goto defective;
+ ec = load_16_be(ptr+4);
+ rrc = load_16_be(ptr+6);
+ seqnum = load_64_be(ptr+8);
+ if (!rotate_left(ptr+16, bodysize-16, rrc)) {
+ no_mem:
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+ if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
+ /* confidentiality */
+ krb5_enc_data cipher;
+ unsigned char *althdr;
+
+ if (conf_state)
+ *conf_state = 1;
+ /* Do we have no decrypt_size function?
+
+ For all current cryptosystems, the ciphertext size will
+ be larger than the plaintext size. */
+ cipher.enctype = key->enctype;
+ cipher.ciphertext.length = bodysize - 16;
+ cipher.ciphertext.data = ptr + 16;
+ plain.length = bodysize - 16;
+ plain.data = malloc(plain.length);
+ if (plain.data == NULL)
+ goto no_mem;
+ err = krb5_c_decrypt(context, key, key_usage, 0,
+ &cipher, &plain);
+ if (err) {
+ free(plain.data);
+ goto error;
+ }
+ /* Don't use bodysize here! Use the fact that
+ cipher.ciphertext.length has been adjusted to the
+ correct length. */
+ althdr = plain.data + plain.length - 16;
+ if (load_16_be(althdr) != 0x0504
+ || althdr[2] != ptr[2]
+ || althdr[3] != ptr[3]
+ || memcmp(althdr+8, ptr+8, 8))
+ goto defective;
+ message_buffer->value = plain.data;
+ message_buffer->length = plain.length - ec - 16;
+ } else {
+ /* no confidentiality */
+ if (conf_state)
+ *conf_state = 0;
+ if (ec + 16 < ec)
+ /* overflow check */
+ goto defective;
+ if (ec + 16 > bodysize)
+ goto defective;
+ /* We have: header | msg | cksum.
+ We need cksum(msg | header).
+ Rotate the first two. */
+ store_16_be(0, ptr+4);
+ store_16_be(0, ptr+6);
+ plain.length = bodysize-ec;
+ plain.data = ptr;
+ if (!rotate_left(ptr, bodysize-ec, 16))
+ goto no_mem;
+ sum.length = ec;
+ if (sum.length != ctx->cksum_size) {
+ *minor_status = 0;
+ return GSS_S_BAD_SIG;
+ }
+ sum.contents = ptr+bodysize-ec;
+ sum.checksum_type = ctx->cksumtype;
+ err = krb5_c_verify_checksum(context, key, key_usage,
+ &plain, &sum, &valid);
+ if (err)
+ goto error;
+ if (!valid) {
+ *minor_status = 0;
+ return GSS_S_BAD_SIG;
+ }
+ message_buffer->length = plain.length - 16;
+ message_buffer->value = malloc(message_buffer->length);
+ if (message_buffer->value == NULL)
+ goto no_mem;
+ memcpy(message_buffer->value, plain.data, message_buffer->length);
+ }
+ err = g_order_check(&ctx->seqstate, seqnum);
+ *minor_status = 0;
+ return err;
+ } else if (toktype == KG_TOK_MIC_MSG) {
+ /* wrap token, no confidentiality */
+ if (load_16_be(ptr) != 0x0404)
+ goto defective;
+ verify_mic_1:
+ if (ptr[3] != 0xff)
+ goto defective;
+ if (load_32_be(ptr+4) != 0xffffffffL)
+ goto defective;
+ seqnum = load_64_be(ptr+8);
+ plain.length = message_buffer->length + 16;
+ plain.data = malloc(plain.length);
+ if (plain.data == NULL)
+ goto no_mem;
+ if (message_buffer->length)
+ memcpy(plain.data, message_buffer->value, message_buffer->length);
+ memcpy(plain.data + message_buffer->length, ptr, 16);
+ sum.length = bodysize - 16;
+ sum.contents = ptr + 16;
+ sum.checksum_type = ctx->cksumtype;
+ err = krb5_c_verify_checksum(context, key, key_usage,
+ &plain, &sum, &valid);
+ if (err) {
+ error:
+ free(plain.data);
+ *minor_status = err;
+ return GSS_S_BAD_SIG; /* XXX */
+ }
+ if (!valid) {
+ free(plain.data);
+ *minor_status = 0;
+ return GSS_S_BAD_SIG;
+ }
+ err = g_order_check(&ctx->seqstate, seqnum);
+ *minor_status = 0;
+ return err;
+ } else if (toktype == KG_TOK_DEL_CTX) {
+ if (load_16_be(ptr) != 0x0405)
+ goto defective;
+ message_buffer = &empty_message;
+ goto verify_mic_1;
+ } else {
+ goto defective;
+ }
+}
unsigned char *ptr;
unsigned int bodysize;
int err;
+ int toktype2;
/* validate the context handle */
if (! kg_validate_ctx_id(context_handle)) {
ptr = (unsigned char *) input_token_buffer->value;
- if (!(err = g_verify_token_header((gss_OID) ctx->mech_used,
- &bodysize, &ptr, toktype,
- input_token_buffer->length))) {
- return(kg_unseal_v1(context, minor_status, ctx, ptr, bodysize,
- message_buffer, conf_state, qop_state,
- toktype));
+ if (ctx->proto)
+ switch (toktype) {
+ case KG_TOK_SIGN_MSG:
+ toktype2 = 0x0404;
+ break;
+ case KG_TOK_SEAL_MSG:
+ toktype2 = 0x0504;
+ break;
+ case KG_TOK_DEL_CTX:
+ toktype2 = 0x0405;
+ break;
+ default:
+ toktype2 = toktype;
+ break;
+ }
+ else
+ toktype2 = toktype;
+ err = g_verify_token_header((gss_OID) ctx->mech_used,
+ &bodysize, &ptr, toktype2,
+ input_token_buffer->length,
+ !ctx->proto);
+ if (err) {
+ *minor_status = err;
+ return GSS_S_DEFECTIVE_TOKEN;
}
- *minor_status = err;
- return(GSS_S_DEFECTIVE_TOKEN);
+ if (ctx->proto == 0)
+ return kg_unseal_v1(context, minor_status, ctx, ptr, bodysize,
+ message_buffer, conf_state, qop_state,
+ toktype);
+ else
+ return gss_krb5int_unseal_token_v3(context, minor_status, ctx,
+ ptr, bodysize, message_buffer,
+ conf_state, qop_state, toktype);
}
if ((ctx = (krb5_gss_ctx_id_rec *) arg)) {
required = 16*sizeof(krb5_int32);
required += sizeof(ctx->seed);
- required += ctx->nctypes*sizeof(krb5_int32);
kret = 0;
if (!kret && ctx->here)
&bp, &remain);
(void) krb5_ser_pack_int32((krb5_int32) ctx->krb_flags,
&bp, &remain);
- (void) krb5_ser_pack_int32((krb5_int32) ctx->seq_send,
+ (void) krb5_ser_pack_int64((krb5_int64) ctx->seq_send,
&bp, &remain);
- (void) krb5_ser_pack_int32((krb5_int32) ctx->seq_recv,
+ (void) krb5_ser_pack_int64((krb5_int64) ctx->seq_recv,
&bp, &remain);
(void) krb5_ser_pack_int32((krb5_int32) ctx->established,
&bp, &remain);
(void) krb5_ser_pack_int32((krb5_int32) ctx->big_endian,
&bp, &remain);
- (void) krb5_ser_pack_int32((krb5_int32) ctx->nctypes,
- &bp, &remain);
/* Now dynamic data */
kret = 0;
(krb5_pointer) ctx->auth_context,
&bp, &remain);
- for (i=0; i<ctx->nctypes; i++) {
- if (!kret) {
- kret = krb5_ser_pack_int32((krb5_int32) ctx->ctypes[i],
- &bp, &remain);
- }
- }
-
if (!kret) {
(void) krb5_ser_pack_int32(KG_CONTEXT, &bp, &remain);
*buffer = bp;
ctx->endtime = (krb5_timestamp) ibuf;
(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
ctx->krb_flags = (krb5_flags) ibuf;
- (void) krb5_ser_unpack_int32(&ctx->seq_send, &bp, &remain);
- (void) krb5_ser_unpack_int32(&ctx->seq_recv, &bp, &remain);
+ (void) krb5_ser_unpack_int64(&ctx->seq_send, &bp, &remain);
+ (void) krb5_ser_unpack_int64(&ctx->seq_recv, &bp, &remain);
(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
ctx->established = (int) ibuf;
(void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
ctx->big_endian = (int) ibuf;
- (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
- ctx->nctypes = (int) ibuf;
if ((kret = kg_oid_internalize(kcontext, &ctx->mech_used, &bp,
&remain))) {
KV5M_AUTH_CONTEXT,
(krb5_pointer *) &ctx->auth_context,
&bp, &remain);
-
- if (!kret) {
- if (ctx->nctypes) {
- if ((ctx->ctypes = (krb5_cksumtype *)
- malloc(ctx->nctypes*sizeof(krb5_cksumtype))) == NULL){
- kret = ENOMEM;
- }
-
- for (i=0; i<ctx->nctypes; i++) {
- if (!kret) {
- kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
- ctx->ctypes[i] = (krb5_cksumtype) ibuf;
- }
- }
- }
- }
/* Get trailer */
if (!kret &&
return(GSS_S_NO_CONTEXT);
}
+ if (ctx->proto == 1) {
+ /* No pseudo-ASN.1 wrapper overhead, so no sequence length and
+ OID. */
+ OM_uint32 sz = req_output_size;
+ /* Token header: 16 octets. */
+ if (conf_req_flag) {
+ while (sz > 0 && krb5_encrypt_size(sz, ctx->enc->enctype) + 16 > req_output_size)
+ sz--;
+ } else {
+ if (sz < 16 + ctx->cksum_size)
+ sz = 0;
+ else
+ sz -= (16 + ctx->cksum_size);
+ }
+
+ /* While testing only! */
+ if (sz < 65536)
+ sz = 0;
+ else
+ sz -= 65535;
+
+ *max_input_size = sz;
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ }
+
/* Calculate the token size and subtract that from the output size */
overhead = 7 + ctx->mech_used->length;
data_size = req_output_size;