From 5ffe972e2c0e6c3748b6b6a33a4f5f68736a6dc7 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Thu, 24 Oct 2002 06:49:59 +0000 Subject: [PATCH] Client code lacks support for draft-ietf-krb-wg-kerberos-sam-01.txt This widely-spread commit implements support for the so-called "new" hardware preauth protocol, defined in the IETF internet-draft draft-ietf-krb-wg-kerberos-sam-01.txt. Note that this code is client-side only. ticket: new git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@14939 dc483132-0cff-0310-8789-dd5450dbe970 --- src/include/ChangeLog | 5 + src/include/k5-int.h | 81 ++++++ src/include/krb5.hin | 8 + src/lib/crypto/ChangeLog | 5 + src/lib/crypto/Makefile.in | 9 + src/lib/crypto/combine_keys.c | 325 +++++++++++++++++++++++ src/lib/krb5/asn.1/ChangeLog | 7 + src/lib/krb5/asn.1/KRB5-asn.py | 45 +++- src/lib/krb5/asn.1/asn1_k_decode.c | 74 ++++++ src/lib/krb5/asn.1/asn1_k_decode.h | 11 + src/lib/krb5/asn.1/asn1_k_encode.c | 83 ++++++ src/lib/krb5/asn.1/asn1_k_encode.h | 18 ++ src/lib/krb5/asn.1/krb5_decode.c | 44 ++++ src/lib/krb5/asn.1/krb5_encode.c | 36 +++ src/lib/krb5/error_tables/ChangeLog | 8 + src/lib/krb5/error_tables/krb5_err.et | 3 + src/lib/krb5/error_tables/kv5m_err.et | 3 + src/lib/krb5/krb/ChangeLog | 11 + src/lib/krb5/krb/gic_pwd.c | 3 +- src/lib/krb5/krb/kfree.c | 100 ++++++++ src/lib/krb5/krb/preauth2.c | 356 +++++++++++++++++++++++++- 21 files changed, 1228 insertions(+), 7 deletions(-) create mode 100644 src/lib/crypto/combine_keys.c diff --git a/src/include/ChangeLog b/src/include/ChangeLog index 6aeda6d41..bf8dbf6b3 100644 --- a/src/include/ChangeLog +++ b/src/include/ChangeLog @@ -1,3 +1,8 @@ +2002-10-24 Ken Hornstein + + * k5-int.h, krb5.hin: Add new protocols, definitions, and + data structures for new hardware preauthentication protocol. + 2002-10-23 Ken Hornstein * krb5.hin: Add new LRQ type for password expiration diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 3f9c330c7..0ee5dd9a8 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -387,6 +387,39 @@ typedef struct _krb5_sam_response { krb5_timestamp sam_patimestamp; } krb5_sam_response; +typedef struct _krb5_sam_challenge_2 { + krb5_data sam_challenge_2_body; + krb5_checksum **sam_cksum; /* Array of checksums */ +} krb5_sam_challenge_2; + +typedef struct _krb5_sam_challenge_2_body { + krb5_magic magic; + krb5_int32 sam_type; /* information */ + krb5_flags sam_flags; /* KRB5_SAM_* values */ + krb5_data sam_type_name; + krb5_data sam_track_id; + krb5_data sam_challenge_label; + krb5_data sam_challenge; + krb5_data sam_response_prompt; + krb5_data sam_pk_for_sad; + krb5_int32 sam_nonce; + krb5_enctype sam_etype; +} krb5_sam_challenge_2_body; + +typedef struct _krb5_sam_response_2 { + krb5_magic magic; + krb5_int32 sam_type; /* informational */ + krb5_flags sam_flags; /* KRB5_SAM_* values */ + krb5_data sam_track_id; /* copied */ + krb5_enc_data sam_enc_nonce_or_sad; /* krb5_enc_sam_response_enc */ + krb5_int32 sam_nonce; +} krb5_sam_response_2; + +typedef struct _krb5_enc_sam_response_enc_2 { + krb5_magic magic; + krb5_int32 sam_nonce; + krb5_data sam_sad; +} krb5_enc_sam_response_enc_2; /* * Begin "ext-proto.h" @@ -648,6 +681,14 @@ krb5_error_code krb5int_default_free_state (krb5_data *state); +/* + * Combine two keys (normally used by the hardware preauth mechanism) + */ +krb5_error_code krb5int_c_combine_keys +(krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2, + krb5_keyblock *outkey); + + /* * These declarations are here, so both krb5 and k5crypto * can get to them. @@ -954,20 +995,36 @@ krb5_error_code krb5_do_preauth void KRB5_CALLCONV krb5_free_sam_challenge (krb5_context, krb5_sam_challenge * ); +void KRB5_CALLCONV krb5_free_sam_challenge_2 + (krb5_context, krb5_sam_challenge_2 * ); +void KRB5_CALLCONV krb5_free_sam_challenge_2_body + (krb5_context, krb5_sam_challenge_2_body *); void KRB5_CALLCONV krb5_free_sam_response (krb5_context, krb5_sam_response * ); +void KRB5_CALLCONV krb5_free_sam_response_2 + (krb5_context, krb5_sam_response_2 * ); void KRB5_CALLCONV krb5_free_predicted_sam_response (krb5_context, krb5_predicted_sam_response * ); void KRB5_CALLCONV krb5_free_enc_sam_response_enc (krb5_context, krb5_enc_sam_response_enc * ); +void KRB5_CALLCONV krb5_free_enc_sam_response_enc_2 + (krb5_context, krb5_enc_sam_response_enc_2 * ); void KRB5_CALLCONV krb5_free_sam_challenge_contents (krb5_context, krb5_sam_challenge * ); +void KRB5_CALLCONV krb5_free_sam_challenge_2_contents + (krb5_context, krb5_sam_challenge_2 * ); +void KRB5_CALLCONV krb5_free_sam_challenge_2_body_contents + (krb5_context, krb5_sam_challenge_2_body * ); void KRB5_CALLCONV krb5_free_sam_response_contents (krb5_context, krb5_sam_response * ); +void KRB5_CALLCONV krb5_free_sam_response_2_contents + (krb5_context, krb5_sam_response_2 *); void KRB5_CALLCONV krb5_free_predicted_sam_response_contents (krb5_context, krb5_predicted_sam_response * ); void KRB5_CALLCONV krb5_free_enc_sam_response_enc_contents (krb5_context, krb5_enc_sam_response_enc * ); +void KRB5_CALLCONV krb5_free_enc_sam_response_enc_2_contents + (krb5_context, krb5_enc_sam_response_enc_2 * ); void KRB5_CALLCONV krb5_free_pa_enc_ts (krb5_context, krb5_pa_enc_ts *); @@ -1243,6 +1300,18 @@ krb5_error_code encode_krb5_enc_sam_response_enc krb5_error_code encode_krb5_sam_response (const krb5_sam_response * , krb5_data **); +krb5_error_code encode_krb5_sam_challenge_2 + (const krb5_sam_challenge_2 * , krb5_data **); + +krb5_error_code encode_krb5_sam_challenge_2_body + (const krb5_sam_challenge_2_body * , krb5_data **); + +krb5_error_code encode_krb5_enc_sam_response_enc_2 + (const krb5_enc_sam_response_enc_2 * , krb5_data **); + +krb5_error_code encode_krb5_sam_response_2 + (const krb5_sam_response_2 * , krb5_data **); + krb5_error_code encode_krb5_predicted_sam_response (const krb5_predicted_sam_response * , krb5_data **); @@ -1280,6 +1349,18 @@ krb5_error_code decode_krb5_sam_response krb5_error_code decode_krb5_predicted_sam_response (const krb5_data *, krb5_predicted_sam_response **); +krb5_error_code decode_krb5_sam_challenge_2 + (const krb5_data *, krb5_sam_challenge_2 **); + +krb5_error_code decode_krb5_sam_challenge_2_body + (const krb5_data *, krb5_sam_challenge_2_body **); + +krb5_error_code decode_krb5_enc_sam_response_enc_2 + (const krb5_data *, krb5_enc_sam_response_enc_2 **); + +krb5_error_code decode_krb5_sam_response_2 + (const krb5_data *, krb5_sam_response_2 **); + /************************************************************************* * Prototypes for krb5_decode.c diff --git a/src/include/krb5.hin b/src/include/krb5.hin index 345660253..5b8cb2c60 100644 --- a/src/include/krb5.hin +++ b/src/include/krb5.hin @@ -543,6 +543,12 @@ krb5_error_code KRB5_CALLCONV #define KRB5_KEYUSAGE_GSS_TOK_WRAP_INTEG 23 #define KRB5_KEYUSAGE_GSS_TOK_WRAP_PRIV 24 +/* Defined in hardware preauth draft */ + +#define KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM 25 +#define KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID 26 +#define KRB5_KEYUSAGE_PA_SAM_RESPONSE 27 + krb5_boolean KRB5_CALLCONV krb5_c_valid_enctype (krb5_enctype ktype); krb5_boolean KRB5_CALLCONV krb5_c_valid_cksumtype @@ -860,6 +866,8 @@ krb5_error_code krb5_decrypt_data #define KRB5_PADATA_ETYPE_INFO 11 /* Etype info for preauth */ #define KRB5_PADATA_SAM_CHALLENGE 12 /* draft challenge system */ #define KRB5_PADATA_SAM_RESPONSE 13 /* draft challenge system response */ +#define KRB5_PADATA_SAM_CHALLENGE_2 14 /* draft challenge system, updated */ +#define KRB5_PADATA_SAM_RESPONSE_2 15 /* draft challenge system, updated */ #define KRB5_SAM_USE_SAD_AS_KEY 0x80000000 #define KRB5_SAM_SEND_ENCRYPTED_SAD 0x40000000 diff --git a/src/lib/crypto/ChangeLog b/src/lib/crypto/ChangeLog index cf91d7b71..36b03a51c 100644 --- a/src/lib/crypto/ChangeLog +++ b/src/lib/crypto/ChangeLog @@ -1,3 +1,8 @@ +2002-10-24 Ken Hornstein + + * Makefile.in, combine_keys.c: New file to implement + key-combination algorithm. + 2002-10-09 Ken Raeburn * pbkdf2.c, t_hmac.c, t_pkcs5.c: New files. diff --git a/src/lib/crypto/Makefile.in b/src/lib/crypto/Makefile.in index 3273a8282..5dc557485 100644 --- a/src/lib/crypto/Makefile.in +++ b/src/lib/crypto/Makefile.in @@ -39,6 +39,7 @@ STLIBOBJS=\ cksumtype_to_string.o \ cksumtypes.o \ coll_proof_cksum.o \ + combine_keys.o \ crypto_libinit.o \ default_state.o \ decrypt.o \ @@ -69,6 +70,7 @@ OBJS=\ $(OUTPRE)cksumtype_to_string.$(OBJEXT) \ $(OUTPRE)cksumtypes.$(OBJEXT) \ $(OUTPRE)coll_proof_cksum.$(OBJEXT) \ + $(OUTPRE)combine_keys.$(OBJEXT) \ $(OUTPRE)crypto_libinit.$(OBJEXT) \ $(OUTPRE)default_state.$(OBJEXT) \ $(OUTPRE)decrypt.$(OBJEXT) \ @@ -99,6 +101,7 @@ SRCS=\ $(srcdir)/cksumtype_to_string.c \ $(srcdir)/cksumtypes.c \ $(srcdir)/coll_proof_cksum.c \ + $(srcdir)/combine_keys.c \ $(srcdir)/crypto_libinit.c \ $(srcdir)/default_state.c \ $(srcdir)/decrypt.c \ @@ -348,6 +351,12 @@ coll_proof_cksum.so coll_proof_cksum.po $(OUTPRE)coll_proof_cksum.$(OBJEXT): col $(COM_ERR_DEPS) $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ $(SRCTOP)/include/krb5/kdb.h $(BUILDTOP)/include/profile.h \ cksumtypes.h +combine_keys.so combine_keys.po $(OUTPRE)combine_keys.$(OBJEXT): combine_keys.c \ + $(SRCTOP)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(BUILDTOP)/include/krb5.h \ + $(COM_ERR_DEPS) $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \ + $(SRCTOP)/include/krb5/kdb.h $(BUILDTOP)/include/profile.h \ + etypes.h $(srcdir)/dk/dk.h crypto_libinit.so crypto_libinit.po $(OUTPRE)crypto_libinit.$(OBJEXT): crypto_libinit.c \ crypto_libinit.h default_state.so default_state.po $(OUTPRE)default_state.$(OBJEXT): default_state.c $(SRCTOP)/include/k5-int.h \ diff --git a/src/lib/crypto/combine_keys.c b/src/lib/crypto/combine_keys.c new file mode 100644 index 000000000..472c07e6a --- /dev/null +++ b/src/lib/crypto/combine_keys.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2002 Naval Research Laboratory (NRL/CCS) + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the software, + * derivative works or modified versions, and any portions thereof. + * + * NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND + * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER + * RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Key combination function. + * + * If Key1 and Key2 are two keys to be combined, the algorithm to combine + * them is as follows. + * + * Definitions: + * + * k-truncate is defined as truncating to the key size the input. + * + * DR is defined as the generate "random" data from a key + * (defined in crypto draft) + * + * DK is defined as the key derivation function (krb5_derive_key()) + * + * (note: | means "concatenate") + * + * Combine key algorithm: + * + * R1 = DR(Key1, n-fold(Key2)) [ Output is length of Key1 ] + * R2 = DR(Key2, n-fold(Key1)) [ Output is length of Key2 ] + * + * rnd = n-fold(R1 | R2) [ Note: output size of nfold must be appropriately + * sized for random-to-key function ] + * tkey = random-to-key(rnd) + * Combine-Key(Key1, Key2) = DK(tkey, CombineConstant) + * + * CombineConstant is defined as the byte string: + * + * { 0x63 0x6f 0x6d 0x62 0x69 0x6e 0x65 }, which corresponds to the + * ASCII encoding of the string "combine" + */ + +#include "k5-int.h" +#include "etypes.h" +#include "dk.h" + +static krb5_error_code dr +(const struct krb5_enc_provider *enc, const krb5_keyblock *inkey, + unsigned char *outdata, const krb5_data *in_constant); + +krb5_error_code KRB5_CALLCONV krb5int_c_combine_keys +(krb5_context context, krb5_keyblock *key1, krb5_keyblock *key2, krb5_keyblock *outkey) +{ + unsigned char *r1, *r2, *combined, *rnd, *output; + size_t keybytes, keylength; + const struct krb5_enc_provider *enc; + krb5_data input, randbits; + krb5_keyblock tkey; + krb5_error_code ret; + int i, myalloc = 0; + + if (key1->length != key2->length || key1->enctype != key2->enctype) + return (KRB5_CRYPTO_INTERNAL); + + /* + * Find our encryption algorithm + */ + + for (i = 0; i < krb5_enctypes_length; i++) { + if (krb5_enctypes_list[i].etype == key1->enctype) + break; + } + + if (i == krb5_enctypes_length) + return (KRB5_BAD_ENCTYPE); + + enc = krb5_enctypes_list[i].enc; + + (*(enc->keysize))(&keybytes, &keylength); + + /* + * Allocate and set up buffers + */ + + if ((r1 = (unsigned char *) malloc(keybytes)) == NULL) + return (ENOMEM); + + if ((r2 = (unsigned char *) malloc(keybytes)) == NULL) { + free(r1); + return (ENOMEM); + } + + if ((rnd = (unsigned char *) malloc(keybytes)) == NULL) { + free(r1); + free(r2); + return (ENOMEM); + } + + if ((combined = (unsigned char *) malloc(keybytes * 2)) == NULL) { + free(r1); + free(r2); + free(rnd); + return (ENOMEM); + } + + if ((output = (unsigned char *) malloc(keylength)) == NULL) { + free(r1); + free(r2); + free(rnd); + free(combined); + return (ENOMEM); + } + + /* + * Get R1 and R2 (by running the input keys through the DR algorithm. + * Note this is most of derive-key, but not all. + */ + + input.length = key2->length; + input.data = (char *) key2->contents; + if ((ret = dr(enc, key1, r1, &input))) + goto cleanup; + +#if 0 + { + int i; + printf("R1 ="); + for (i = 0; i < keybytes; i++) + printf(" %02x", (unsigned char) r1[i]); + printf("\n"); + } +#endif + + input.length = key1->length; + input.data = (char *) key1->contents; + if ((ret = dr(enc, key2, r2, &input))) + goto cleanup; + +#if 0 + { + int i; + printf("R2 ="); + for (i = 0; i < keybytes; i++) + printf(" %02x", (unsigned char) r2[i]); + printf("\n"); + } +#endif + + /* + * Concatenate the two keys together, and then run them through + * n-fold to reduce them to a length appropriate for the random-to-key + * operation. Note here that krb5_nfold() takes sizes in bits, hence + * the multiply by 8. + */ + + memcpy(combined, r1, keybytes); + memcpy(combined + keybytes, r2, keybytes); + + krb5_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd); + +#if 0 + { + int i; + printf("rnd ="); + for (i = 0; i < keybytes; i++) + printf(" %02x", (unsigned char) rnd[i]); + printf("\n"); + } +#endif + + /* + * Run the "random" bits through random-to-key to produce a encryption + * key. + */ + + randbits.length = keybytes; + randbits.data = (char *) rnd; + tkey.length = keylength; + tkey.contents = output; + + if ((ret = (*(enc->make_key))(&randbits, &tkey))) + goto cleanup; + +#if 0 + { + int i; + printf("tkey ="); + for (i = 0; i < tkey.length; i++) + printf(" %02x", (unsigned char) tkey.contents[i]); + printf("\n"); + } +#endif + + /* + * Run through derive-key one more time to produce the final key. + * Note that the input to derive-key is the ASCII string "combine". + */ + + input.length = 7; /* Note; change this if string length changes */ + input.data = "combine"; + + /* + * Just FYI: _if_ we have space here in the key, then simply use it + * without modification. But if the key is blank (no allocated storage) + * then allocate some memory for it. This allows programs to use one of + * the existing keys as the output key, _or_ pass in a blank keyblock + * for us to allocate. It's easier for us to allocate it since we already + * know the crypto library internals + */ + + if (outkey->length == 0 || outkey->contents == NULL) { + outkey->contents = (krb5_octet *) malloc(keylength); + if (!outkey->contents) { + ret = ENOMEM; + goto cleanup; + } + outkey->length = keylength; + outkey->enctype = key1->enctype; + myalloc = 1; + } + + if ((ret = krb5_derive_key(enc, &tkey, outkey, &input))) { + if (myalloc) { + free(outkey->contents); + outkey->contents = NULL; + } + goto cleanup; + } + +#if 0 + { + int i; + printf("output ="); + for (i = 0; i < outkey->length; i++) + printf(" %02x", (unsigned char) outkey->contents[i]); + printf("\n"); + } +#endif + + ret = 0; + +cleanup: + memset(r1, 0, keylength); + memset(r2, 0, keylength); + memset(rnd, 0, keybytes); + memset(combined, 0, keylength * 2); + memset(output, 0, keylength); + + free(r1); + free(r2); + free(rnd); + free(combined); + free(output); + + return (ret); +} + +/* + * Our DR function; mostly taken from derive.c + */ + +static krb5_error_code dr +(const struct krb5_enc_provider *enc, const krb5_keyblock *inkey, unsigned char *out, const krb5_data *in_constant) +{ + size_t blocksize, keybytes, keylength, n; + unsigned char *inblockdata, *outblockdata; + krb5_data inblock, outblock; + + (*(enc->block_size))(&blocksize); + (*(enc->keysize))(&keybytes, &keylength); + + /* allocate and set up buffers */ + + if ((inblockdata = (unsigned char *) malloc(blocksize)) == NULL) + return(ENOMEM); + + if ((outblockdata = (unsigned char *) malloc(blocksize)) == NULL) { + free(inblockdata); + return(ENOMEM); + } + + inblock.data = (char *) inblockdata; + inblock.length = blocksize; + + outblock.data = (char *) outblockdata; + outblock.length = blocksize; + + /* initialize the input block */ + + if (in_constant->length == inblock.length) { + memcpy(inblock.data, in_constant->data, inblock.length); + } else { + krb5_nfold(in_constant->length*8, (unsigned char *) in_constant->data, + inblock.length*8, (unsigned char *) inblock.data); + } + + /* loop encrypting the blocks until enough key bytes are generated */ + + n = 0; + while (n < keybytes) { + (*(enc->encrypt))(inkey, 0, &inblock, &outblock); + + if ((keybytes - n) <= outblock.length) { + memcpy(out+n, outblock.data, (keybytes - n)); + break; + } + + memcpy(out+n, outblock.data, outblock.length); + memcpy(inblock.data, outblock.data, outblock.length); + n += outblock.length; + } + + /* clean memory, free resources and exit */ + + memset(inblockdata, 0, blocksize); + memset(outblockdata, 0, blocksize); + + free(outblockdata); + free(inblockdata); + + return(0); +} + diff --git a/src/lib/krb5/asn.1/ChangeLog b/src/lib/krb5/asn.1/ChangeLog index ed71c85b6..577429d7e 100644 --- a/src/lib/krb5/asn.1/ChangeLog +++ b/src/lib/krb5/asn.1/ChangeLog @@ -1,3 +1,10 @@ +2002-10-24 Ken Hornstein + + * KRB5-asn.py, asn1_k_decode.c, asn1_k_decode.h, asn1_k_encode.c, + asn1_k_encode.h, krb5_decode.c, krb5_encode.c: New functions, + prototypes, and ASN.1 definitions for the new hardware + preauthentication protocol. + 2002-07-02 Sam Hartman * asn1_encode.h: Document asn1_encode_enumerated diff --git a/src/lib/krb5/asn.1/KRB5-asn.py b/src/lib/krb5/asn.1/KRB5-asn.py index 365debcf8..867ac6771 100644 --- a/src/lib/krb5/asn.1/KRB5-asn.py +++ b/src/lib/krb5/asn.1/KRB5-asn.py @@ -368,13 +368,32 @@ PA-SAM-CHALLENGE ::= SEQUENCE { sam-cksum[9] Checksum OPTIONAL } --- these are [0].. [2] in the draft -SAMFlags ::= BIT STRING { - use-sad-as-key(0), - send-encrypted-sad(1), - must-pk-encrypt-sad(2) +PA-SAM-CHALLENGE-2 ::= SEQUENCE { + sam-body[0] PA-SAM-CHALLENGE-2-BODY, + sam-cksum[1] SEQUENCE (1..MAX) OF Checksum, + ... +} + +PA-SAM-CHALLENGE-2-BODY ::= SEQUENCE { + sam-type[0] INTEGER, + sam-flags[1] SAMFlags, + sam-type-name[2] GeneralString OPTIONAL, + sam-track-id[3] GeneralString OPTIONAL, + sam-challenge-label[4] GeneralString OPTIONAL, + sam-challenge[5] GeneralString OPTIONAL, + sam-response-prompt[6] GeneralString OPTIONAL, + sam-pk-for-sad[7] EncryptionKey OPTIONAL, + sam-nonce[8] INTEGER, + sam-etype[9] INTEGER, + ... } +-- these are [0].. [2] in the draft +SAMFlags ::= BIT STRING (SIZE (32..MAX)) + -- use-sad-as-key(0) + -- send-encrypted-sad(1) + -- must-pk-encrypt-sad(2) + PA-SAM-RESPONSE ::= SEQUENCE { sam-type[0] INTEGER, sam-flags[1] SAMFlags, @@ -388,6 +407,16 @@ PA-SAM-RESPONSE ::= SEQUENCE { sam-patimestamp[6] KerberosTime OPTIONAL } +PA-SAM-RESPONSE-2 ::= SEQUENCE { + sam-type[0] INTEGER, + sam-flags[1] SAMFlags, + sam-track-id[2] GeneralString OPTIONAL, + sam-enc-nonce-or-sad[3] EncryptedData, + -- PA-ENC-SAM-RESPONSE-ENC + sam-nonce[4] INTEGER, + ... +} + PA-ENC-SAM-KEY ::= SEQUENCE { sam-key[0] EncryptionKey } @@ -398,4 +427,10 @@ PA-ENC-SAM-RESPONSE-ENC ::= SEQUENCE { sam-usec[2] INTEGER OPTIONAL, sam-passcode[3] GeneralString OPTIONAL } + +PA-ENC-SAM-RESPONSE-ENC-2 ::= SEQUENCE { + sam-nonce[0] INTEGER, + sam-sad[1] GeneralString OPTIONAL, + ... +} END diff --git a/src/lib/krb5/asn.1/asn1_k_decode.c b/src/lib/krb5/asn.1/asn1_k_decode.c index 78d7e4725..f075db094 100644 --- a/src/lib/krb5/asn.1/asn1_k_decode.c +++ b/src/lib/krb5/asn.1/asn1_k_decode.c @@ -734,6 +734,11 @@ asn1_error_code asn1_decode_sequence_of_enctype(asn1buf *buf, int *num, krb5_enc cleanup(); } +asn1_error_code asn1_decode_sequence_of_checksum(asn1buf *buf, krb5_checksum ***val) +{ + decode_array_body(krb5_checksum, asn1_decode_checksum); +} + asn1_error_code asn1_decode_etype_info_entry(asn1buf *buf, krb5_etype_info_entry *val) { setup(); @@ -807,6 +812,48 @@ asn1_error_code asn1_decode_sam_challenge(asn1buf *buf, krb5_sam_challenge *val) } cleanup(); } +asn1_error_code asn1_decode_sam_challenge_2(asn1buf *buf, krb5_sam_challenge_2 *val) +{ + setup(); + { char *save, *end; + begin_structure(); + if (tagnum != 0) return ASN1_MISSING_FIELD; + if (asn1class != CONTEXT_SPECIFIC || construction != CONSTRUCTED) + return ASN1_BAD_ID; + save = subbuf.next; + { sequence_of(&subbuf); + end_sequence_of(&subbuf); + } + end = subbuf.next; + if ((val->sam_challenge_2_body.data = (char *) malloc(end - save)) == NULL) + return ENOMEM; + val->sam_challenge_2_body.length = end - save; + memcpy(val->sam_challenge_2_body.data, save, end - save); + next_tag(); + get_field(val->sam_cksum, 1, asn1_decode_sequence_of_checksum); + end_structure(); + } + cleanup(); +} +asn1_error_code asn1_decode_sam_challenge_2_body(asn1buf *buf, krb5_sam_challenge_2_body *val) +{ + setup(); + { begin_structure(); + get_field(val->sam_type,0,asn1_decode_int32); + get_field(val->sam_flags,1,asn1_decode_sam_flags); + opt_string(val->sam_type_name,2,asn1_decode_charstring); + opt_string(val->sam_track_id,3,asn1_decode_charstring); + opt_string(val->sam_challenge_label,4,asn1_decode_charstring); + opt_string(val->sam_challenge,5,asn1_decode_charstring); + opt_string(val->sam_response_prompt,6,asn1_decode_charstring); + opt_string(val->sam_pk_for_sad,7,asn1_decode_charstring); + get_field(val->sam_nonce,8,asn1_decode_int32); + get_field(val->sam_etype, 9, asn1_decode_int32); + end_structure(); + val->magic = KV5M_SAM_CHALLENGE; + } + cleanup(); +} asn1_error_code asn1_decode_enc_sam_key(asn1buf *buf, krb5_sam_key *val) { setup(); @@ -833,6 +880,18 @@ asn1_error_code asn1_decode_enc_sam_response_enc(asn1buf *buf, krb5_enc_sam_resp cleanup(); } +asn1_error_code asn1_decode_enc_sam_response_enc_2(asn1buf *buf, krb5_enc_sam_response_enc_2 *val) +{ + setup(); + { begin_structure(); + get_field(val->sam_nonce,0,asn1_decode_int32); + opt_string(val->sam_sad,1,asn1_decode_charstring); + end_structure(); + val->magic = KV5M_ENC_SAM_RESPONSE_ENC_2; + } + cleanup(); +} + #define opt_encfield(fld,tag,fn) \ if(tagnum == tag){ \ get_field(fld,tag,fn); } \ @@ -861,6 +920,21 @@ asn1_error_code asn1_decode_sam_response(asn1buf *buf, krb5_sam_response *val) cleanup(); } +asn1_error_code asn1_decode_sam_response_2(asn1buf *buf, krb5_sam_response_2 *val) +{ + setup(); + { begin_structure(); + get_field(val->sam_type,0,asn1_decode_int32); + get_field(val->sam_flags,1,asn1_decode_sam_flags); + opt_string(val->sam_track_id,2,asn1_decode_charstring); + get_field(val->sam_enc_nonce_or_sad,3,asn1_decode_encrypted_data); + get_field(val->sam_nonce,4,asn1_decode_int32); + end_structure(); + val->magic = KV5M_SAM_RESPONSE; + } + cleanup(); +} + asn1_error_code asn1_decode_predicted_sam_response(asn1buf *buf, krb5_predicted_sam_response *val) { diff --git a/src/lib/krb5/asn.1/asn1_k_decode.h b/src/lib/krb5/asn.1/asn1_k_decode.h index 22b2d2b5e..8f8b0bcff 100644 --- a/src/lib/krb5/asn.1/asn1_k_decode.h +++ b/src/lib/krb5/asn.1/asn1_k_decode.h @@ -143,12 +143,20 @@ asn1_error_code asn1_decode_etype_info_entry (asn1buf *buf, krb5_etype_info_entry *val); asn1_error_code asn1_decode_sam_challenge (asn1buf *buf, krb5_sam_challenge *val); +asn1_error_code asn1_decode_sam_challenge_2 + (asn1buf *buf, krb5_sam_challenge_2 *val); +asn1_error_code asn1_decode_sam_challenge_2_body + (asn1buf *buf, krb5_sam_challenge_2_body *val); asn1_error_code asn1_decode_enc_sam_key (asn1buf *buf, krb5_sam_key *val); asn1_error_code asn1_decode_enc_sam_response_enc (asn1buf *buf, krb5_enc_sam_response_enc *val); +asn1_error_code asn1_decode_enc_sam_response_enc_2 + (asn1buf *buf, krb5_enc_sam_response_enc_2 *val); asn1_error_code asn1_decode_sam_response (asn1buf *buf, krb5_sam_response *val); +asn1_error_code asn1_decode_sam_response_2 + (asn1buf *buf, krb5_sam_response_2 *val); asn1_error_code asn1_decode_predicted_sam_response (asn1buf *buf, krb5_predicted_sam_response *val); @@ -169,6 +177,9 @@ asn1_error_code asn1_decode_last_req asn1_error_code asn1_decode_sequence_of_enctype (asn1buf *buf, int *num, krb5_enctype **val); +asn1_error_code asn1_decode_sequence_of_checksum + (asn1buf *buf, krb5_checksum ***val); + asn1_error_code asn1_decode_sequence_of_passwdsequence (asn1buf *buf, passwd_phrase_element ***val); diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c index 2177b527e..111695b3a 100644 --- a/src/lib/krb5/asn.1/asn1_k_encode.c +++ b/src/lib/krb5/asn.1/asn1_k_encode.c @@ -371,6 +371,24 @@ asn1_error_code asn1_encode_enc_kdc_rep_part(asn1buf *buf, const krb5_enc_kdc_re asn1_cleanup(); } +asn1_error_code asn1_encode_sequence_of_checksum(asn1buf *buf, const krb5_checksum ** val, unsigned int *retlen) +{ + asn1_setup(); + int i; + + if(val == NULL) return ASN1_MISSING_FIELD; + + for (i=0; val[i] != NULL; i++); + for (i--; i>=0; i--){ + retval = asn1_encode_checksum(buf,val[i],&length); + if(retval) return retval; + sum += length; + } + asn1_makeseq(); + + asn1_cleanup(); +} + asn1_error_code asn1_encode_kdc_req_body(asn1buf *buf, const krb5_kdc_req *rep, unsigned int *retlen) { asn1_setup(); @@ -782,6 +800,45 @@ asn1_error_code asn1_encode_sam_challenge(asn1buf *buf, const krb5_sam_challenge asn1_cleanup(); } +asn1_error_code asn1_encode_sam_challenge_2(asn1buf *buf, const krb5_sam_challenge_2 *val, unsigned int *retlen) +{ + asn1_setup(); + if ( (!val) || (!val->sam_cksum) || (!val->sam_cksum[0])) + return ASN1_MISSING_FIELD; + + asn1_addfield((const krb5_checksum **) val->sam_cksum, 1, asn1_encode_sequence_of_checksum); + asn1buf_insert_octetstring(buf, val->sam_challenge_2_body.length, + (unsigned char *)val->sam_challenge_2_body.data); + sum += val->sam_challenge_2_body.length; + retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0, + val->sam_challenge_2_body.length, &length); + if(retval) return retval; + sum += length; + + asn1_makeseq(); + asn1_cleanup(); +} + +asn1_error_code asn1_encode_sam_challenge_2_body(asn1buf *buf, const krb5_sam_challenge_2_body *val, unsigned int *retlen) +{ + asn1_setup(); + + asn1_addfield(val->sam_etype, 9, asn1_encode_integer); + asn1_addfield(val->sam_nonce,8,asn1_encode_integer); + add_optstring(val->sam_pk_for_sad,7,asn1_encode_charstring); + add_optstring(val->sam_response_prompt,6,asn1_encode_charstring); + add_optstring(val->sam_challenge,5,asn1_encode_charstring); + add_optstring(val->sam_challenge_label,4,asn1_encode_charstring); + add_optstring(val->sam_track_id,3,asn1_encode_charstring); + add_optstring(val->sam_type_name,2,asn1_encode_charstring); + + asn1_addfield(val->sam_flags,1,asn1_encode_sam_flags); + asn1_addfield(val->sam_type,0,asn1_encode_integer); + + asn1_makeseq(); + asn1_cleanup(); +} + asn1_error_code asn1_encode_sam_key(asn1buf *buf, const krb5_sam_key *val, unsigned int *retlen) { asn1_setup(); @@ -806,6 +863,17 @@ asn1_error_code asn1_encode_enc_sam_response_enc(asn1buf *buf, const krb5_enc_sa asn1_cleanup(); } +asn1_error_code asn1_encode_enc_sam_response_enc_2(asn1buf *buf, const krb5_enc_sam_response_enc_2 *val, unsigned int *retlen) +{ + asn1_setup(); + add_optstring(val->sam_sad,1,asn1_encode_charstring); + asn1_addfield(val->sam_nonce,0,asn1_encode_integer); + + asn1_makeseq(); + + asn1_cleanup(); +} + asn1_error_code asn1_encode_sam_response(asn1buf *buf, const krb5_sam_response *val, unsigned int *retlen) { asn1_setup(); @@ -826,6 +894,21 @@ asn1_error_code asn1_encode_sam_response(asn1buf *buf, const krb5_sam_response * asn1_cleanup(); } +asn1_error_code asn1_encode_sam_response_2(asn1buf *buf, const krb5_sam_response_2 *val, unsigned int *retlen) +{ + asn1_setup(); + + asn1_addfield(val->sam_nonce,4,asn1_encode_integer); + asn1_addfield(&(val->sam_enc_nonce_or_sad),3,asn1_encode_encrypted_data); + add_optstring(val->sam_track_id,2,asn1_encode_charstring); + asn1_addfield(val->sam_flags,1,asn1_encode_sam_flags); + asn1_addfield(val->sam_type,0,asn1_encode_integer); + + asn1_makeseq(); + + asn1_cleanup(); +} + asn1_error_code asn1_encode_predicted_sam_response(asn1buf *buf, const krb5_predicted_sam_response *val, unsigned int *retlen) { asn1_setup(); diff --git a/src/lib/krb5/asn.1/asn1_k_encode.h b/src/lib/krb5/asn.1/asn1_k_encode.h index 6d3633513..5914e0981 100644 --- a/src/lib/krb5/asn.1/asn1_k_encode.h +++ b/src/lib/krb5/asn.1/asn1_k_encode.h @@ -69,6 +69,7 @@ asn1_encode_sequence_of_pa_data asn1_encode_sequence_of_ticket asn1_encode_sequence_of_enctype + asn1_encode_sequence_of_checksum asn1_encode_sequence_of_krb_cred_info */ @@ -184,6 +185,9 @@ asn1_error_code asn1_encode_sequence_of_enctype const int len, const krb5_enctype *val, unsigned int *retlen); +asn1_error_code asn1_encode_sequence_of_checksum + (asn1buf *buf, const krb5_checksum **val, unsigned int *retlen); + asn1_error_code asn1_encode_kdc_req (int msg_type, asn1buf *buf, @@ -234,6 +238,13 @@ asn1_error_code asn1_encode_sam_flags asn1_error_code asn1_encode_sam_challenge (asn1buf *buf, const krb5_sam_challenge * val, unsigned int *retlen); +asn1_error_code asn1_encode_sam_challenge_2 + (asn1buf *buf, const krb5_sam_challenge_2 * val, unsigned int *retlen); + +asn1_error_code asn1_encode_sam_challenge_2_body + (asn1buf *buf, const krb5_sam_challenge_2_body * val, + unsigned int *retlen); + asn1_error_code asn1_encode_sam_key (asn1buf *buf, const krb5_sam_key *val, unsigned int *retlen); @@ -241,9 +252,16 @@ asn1_error_code asn1_encode_enc_sam_response_enc (asn1buf *buf, const krb5_enc_sam_response_enc *val, unsigned int *retlen); +asn1_error_code asn1_encode_enc_sam_response_enc_2 + (asn1buf *buf, const krb5_enc_sam_response_enc_2 *val, + unsigned int *retlen); + asn1_error_code asn1_encode_sam_response (asn1buf *buf, const krb5_sam_response *val, unsigned int *retlen); +asn1_error_code asn1_encode_sam_response_2 + (asn1buf *buf, const krb5_sam_response_2 *val, unsigned int *retlen); + asn1_error_code asn1_encode_predicted_sam_response (asn1buf *buf, const krb5_predicted_sam_response *val, unsigned int *retlen); diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c index cb6e8f252..f2d916527 100644 --- a/src/lib/krb5/asn.1/krb5_decode.c +++ b/src/lib/krb5/asn.1/krb5_decode.c @@ -761,6 +761,28 @@ krb5_error_code decode_krb5_sam_challenge(const krb5_data *code, krb5_sam_challe cleanup(free); } +krb5_error_code decode_krb5_sam_challenge_2(const krb5_data *code, krb5_sam_challenge_2 **rep) +{ + setup_buf_only(); + alloc_field(*rep,krb5_sam_challenge_2); + + retval = asn1_decode_sam_challenge_2(&buf,*rep); + if(retval) clean_return(retval); + + cleanup(free); +} + +krb5_error_code decode_krb5_sam_challenge_2_body(const krb5_data *code, krb5_sam_challenge_2_body **rep) +{ + setup_buf_only(); + alloc_field(*rep, krb5_sam_challenge_2_body); + + retval = asn1_decode_sam_challenge_2_body(&buf, *rep); + if(retval) clean_return(retval); + + cleanup(free); +} + krb5_error_code decode_krb5_enc_sam_key(const krb5_data *code, krb5_sam_key **rep) { setup_buf_only(); @@ -783,6 +805,17 @@ krb5_error_code decode_krb5_enc_sam_response_enc(const krb5_data *code, krb5_enc cleanup(free); } +krb5_error_code decode_krb5_enc_sam_response_enc_2(const krb5_data *code, krb5_enc_sam_response_enc_2 **rep) +{ + setup_buf_only(); + alloc_field(*rep,krb5_enc_sam_response_enc_2); + + retval = asn1_decode_enc_sam_response_enc_2(&buf,*rep); + if(retval) clean_return(retval); + + cleanup(free); +} + krb5_error_code decode_krb5_sam_response(const krb5_data *code, krb5_sam_response **rep) { setup_buf_only(); @@ -794,6 +827,17 @@ krb5_error_code decode_krb5_sam_response(const krb5_data *code, krb5_sam_respons cleanup(free); } +krb5_error_code decode_krb5_sam_response_2(const krb5_data *code, krb5_sam_response_2 **rep) +{ + setup_buf_only(); + alloc_field(*rep,krb5_sam_response_2); + + retval = asn1_decode_sam_response_2(&buf,*rep); + if(retval) clean_return(retval); + + cleanup(free); +} + krb5_error_code decode_krb5_predicted_sam_response(const krb5_data *code, krb5_predicted_sam_response **rep) { setup_buf_only(); /* preallocated */ diff --git a/src/lib/krb5/asn.1/krb5_encode.c b/src/lib/krb5/asn.1/krb5_encode.c index 133f98f3a..2a4f7bb14 100644 --- a/src/lib/krb5/asn.1/krb5_encode.c +++ b/src/lib/krb5/asn.1/krb5_encode.c @@ -751,6 +751,24 @@ krb5_error_code encode_krb5_sam_challenge(const krb5_sam_challenge *rep, krb5_da krb5_cleanup(); } +krb5_error_code encode_krb5_sam_challenge_2(const krb5_sam_challenge_2 *rep, krb5_data **code) +{ + krb5_setup(); + retval = asn1_encode_sam_challenge_2(buf,rep,&length); + if(retval) return retval; + sum += length; + krb5_cleanup(); +} + +krb5_error_code encode_krb5_sam_challenge_2_body(const krb5_sam_challenge_2_body *rep, krb5_data **code) +{ + krb5_setup(); + retval = asn1_encode_sam_challenge_2_body(buf,rep,&length); + if(retval) return retval; + sum += length; + krb5_cleanup(); +} + krb5_error_code encode_krb5_sam_key(const krb5_sam_key *rep, krb5_data **code) { krb5_setup(); @@ -769,6 +787,15 @@ krb5_error_code encode_krb5_enc_sam_response_enc(const krb5_enc_sam_response_enc krb5_cleanup(); } +krb5_error_code encode_krb5_enc_sam_response_enc_2(const krb5_enc_sam_response_enc_2 *rep, krb5_data **code) +{ + krb5_setup(); + retval = asn1_encode_enc_sam_response_enc_2(buf,rep,&length); + if(retval) return retval; + sum += length; + krb5_cleanup(); +} + krb5_error_code encode_krb5_sam_response(const krb5_sam_response *rep, krb5_data **code) { krb5_setup(); @@ -778,6 +805,15 @@ krb5_error_code encode_krb5_sam_response(const krb5_sam_response *rep, krb5_data krb5_cleanup(); } +krb5_error_code encode_krb5_sam_response_2(const krb5_sam_response_2 *rep, krb5_data **code) +{ + krb5_setup(); + retval = asn1_encode_sam_response_2(buf,rep,&length); + if(retval) return retval; + sum += length; + krb5_cleanup(); +} + krb5_error_code encode_krb5_predicted_sam_response(const krb5_predicted_sam_response *rep, krb5_data **code) { krb5_setup(); diff --git a/src/lib/krb5/error_tables/ChangeLog b/src/lib/krb5/error_tables/ChangeLog index 90dfda0a7..0f90a66e7 100644 --- a/src/lib/krb5/error_tables/ChangeLog +++ b/src/lib/krb5/error_tables/ChangeLog @@ -1,3 +1,11 @@ +2002-10-24 Ken Hornstein * init_ets.c (krb5_init_ets, krb5_free_ets): Use prototype style diff --git a/src/lib/krb5/error_tables/krb5_err.et b/src/lib/krb5/error_tables/krb5_err.et index 7fb927944..08e5cdd2b 100644 --- a/src/lib/krb5/error_tables/krb5_err.et +++ b/src/lib/krb5/error_tables/krb5_err.et @@ -310,6 +310,9 @@ error_code KRB5_GET_IN_TKT_LOOP, "Looping detected inside krb5_get_in_tkt" error_code KRB5_CONFIG_NODEFREALM, "Configuration file does not specify default realm" error_code KRB5_SAM_UNSUPPORTED, "Bad SAM flags in obtain_sam_padata" +error_code KRB5_SAM_INVALID_ETYPE, "Invalid encryption type in SAM challenge" +error_code KRB5_SAM_NO_CHECKSUM, "Missing checksum in SAM challenge" +error_code KRB5_SAM_BAD_CHECKSUM, "Bad checksum in SAM challenge" error_code KRB5_KT_NAME_TOOLONG, "Keytab name too long" error_code KRB5_KT_KVNONOTFOUND, "Key version number for principal in key table is incorrect" error_code KRB5_APPL_EXPIRED, "This application has expired" diff --git a/src/lib/krb5/error_tables/kv5m_err.et b/src/lib/krb5/error_tables/kv5m_err.et index eb6bdd870..1b79de252 100644 --- a/src/lib/krb5/error_tables/kv5m_err.et +++ b/src/lib/krb5/error_tables/kv5m_err.et @@ -76,9 +76,12 @@ error_code KV5M_RCACHE, "Bad magic number for krb5_rcache structure" error_code KV5M_CCACHE, "Bad magic number for krb5_ccache structure" error_code KV5M_PREAUTH_OPS, "Bad magic number for krb5_preauth_ops" error_code KV5M_SAM_CHALLENGE, "Bad magic number for krb5_sam_challenge" +error_code KV5M_SAM_CHALLENGE_2, "Bad magic number for krb5_sam_challenge_2" error_code KV5M_SAM_KEY, "Bad magic number for krb5_sam_key" error_code KV5M_ENC_SAM_RESPONSE_ENC, "Bad magic number for krb5_enc_sam_response_enc" +error_code KV5M_ENC_SAM_RESPONSE_ENC_2, "Bad magic number for krb5_enc_sam_response_enc" error_code KV5M_SAM_RESPONSE, "Bad magic number for krb5_sam_response" +error_code KV5M_SAM_RESPONSE_2, "Bad magic number for krb5_sam_response 2" error_code KV5M_PREDICTED_SAM_RESPONSE, "Bad magic number for krb5_predicted_sam_response" error_code KV5M_PASSWD_PHRASE_ELEMENT, "Bad magic number for passwd_phrase_element" error_code KV5M_GSS_OID, "Bad magic number for GSSAPI OID" diff --git a/src/lib/krb5/krb/ChangeLog b/src/lib/krb5/krb/ChangeLog index a651f2497..4665b6525 100644 --- a/src/lib/krb5/krb/ChangeLog +++ b/src/lib/krb5/krb/ChangeLog @@ -1,3 +1,14 @@ +2002-10-24 Ken Hornstein + + * gic_pwd.c (krb5_get_init_creds_password): Exit out of the loop + when preauth fails. + + * kfree.c: Add various free functions for new preauth + data structures. + + * preauth2.c (pa_sam): Fix up support for "old" hardware preauth. + Also implement new hardware preauth in pa_sam2(). + 2002-10-23 Ken Hornstein * gic_pwd.c (krb5_get_init_creds_password): Fix bug in previous diff --git a/src/lib/krb5/krb/gic_pwd.c b/src/lib/krb5/krb/gic_pwd.c index 287ee7b7b..7bc4d5a46 100644 --- a/src/lib/krb5/krb/gic_pwd.c +++ b/src/lib/krb5/krb/gic_pwd.c @@ -130,9 +130,10 @@ krb5_get_init_creds_password(krb5_context context, krb5_creds *creds, krb5_princ goto cleanup; /* If all the kdc's are unavailable, or if the error was due to a - user interrupt, fail */ + user interrupt, or preauth errored out, fail */ if ((ret == KRB5_KDC_UNREACH) || + (ret == KRB5_PREAUTH_FAILED) || (ret == KRB5_LIBOS_PWDINTR) || (ret == KRB5_REALM_CANT_RESOLVE)) goto cleanup; diff --git a/src/lib/krb5/krb/kfree.c b/src/lib/krb5/krb/kfree.c index 60a3c6182..46d485d32 100644 --- a/src/lib/krb5/krb/kfree.c +++ b/src/lib/krb5/krb/kfree.c @@ -513,6 +513,15 @@ krb5_free_sam_challenge(krb5_context ctx, krb5_sam_challenge *sc) krb5_xfree(sc); } +void KRB5_CALLCONV +krb5_free_sam_challenge_2(krb5_context ctx, krb5_sam_challenge_2 *sc2) +{ + if (!sc2) + return; + krb5_free_sam_challenge_2_contents(ctx, sc2); + krb5_xfree(sc2); +} + void KRB5_CALLCONV krb5_free_sam_challenge_contents(krb5_context ctx, krb5_sam_challenge *sc) { @@ -536,6 +545,57 @@ krb5_free_sam_challenge_contents(krb5_context ctx, krb5_sam_challenge *sc) } } +void KRB5_CALLCONV +krb5_free_sam_challenge_2_contents(krb5_context ctx, + krb5_sam_challenge_2 *sc2) +{ + krb5_checksum **cksump; + + if (!sc2) + return; + if (sc2->sam_challenge_2_body.data) + krb5_free_data_contents(ctx, &sc2->sam_challenge_2_body); + if (sc2->sam_cksum) { + cksump = sc2->sam_cksum; + while (*cksump) { + krb5_free_checksum(ctx, *cksump); + cksump++; + } + krb5_xfree(sc2->sam_cksum); + sc2->sam_cksum = 0; + } +} + +void KRB5_CALLCONV +krb5_free_sam_challenge_2_body(krb5_context ctx, + krb5_sam_challenge_2_body *sc2) +{ + if (!sc2) + return; + krb5_free_sam_challenge_2_body_contents(ctx, sc2); + krb5_xfree(sc2); +} + +void KRB5_CALLCONV +krb5_free_sam_challenge_2_body_contents(krb5_context ctx, + krb5_sam_challenge_2_body *sc2) +{ + if (!sc2) + return; + if (sc2->sam_type_name.data) + krb5_free_data_contents(ctx, &sc2->sam_type_name); + if (sc2->sam_track_id.data) + krb5_free_data_contents(ctx, &sc2->sam_track_id); + if (sc2->sam_challenge_label.data) + krb5_free_data_contents(ctx, &sc2->sam_challenge_label); + if (sc2->sam_challenge.data) + krb5_free_data_contents(ctx, &sc2->sam_challenge); + if (sc2->sam_response_prompt.data) + krb5_free_data_contents(ctx, &sc2->sam_response_prompt); + if (sc2->sam_pk_for_sad.data) + krb5_free_data_contents(ctx, &sc2->sam_pk_for_sad); +} + void KRB5_CALLCONV krb5_free_sam_response(krb5_context ctx, krb5_sam_response *sr) { @@ -545,6 +605,15 @@ krb5_free_sam_response(krb5_context ctx, krb5_sam_response *sr) krb5_xfree(sr); } +void KRB5_CALLCONV +krb5_free_sam_response_2(krb5_context ctx, krb5_sam_response_2 *sr2) +{ + if (!sr2) + return; + krb5_free_sam_response_2_contents(ctx, sr2); + krb5_xfree(sr2); +} + void KRB5_CALLCONV krb5_free_sam_response_contents(krb5_context ctx, krb5_sam_response *sr) { @@ -558,6 +627,17 @@ krb5_free_sam_response_contents(krb5_context ctx, krb5_sam_response *sr) krb5_free_data_contents(ctx, &sr->sam_enc_nonce_or_ts.ciphertext); } +void KRB5_CALLCONV +krb5_free_sam_response_2_contents(krb5_context ctx, krb5_sam_response_2 *sr2) +{ + if (!sr2) + return; + if (sr2->sam_track_id.data) + krb5_free_data_contents(ctx, &sr2->sam_track_id); + if (sr2->sam_enc_nonce_or_sad.ciphertext.data) + krb5_free_data_contents(ctx, &sr2->sam_enc_nonce_or_sad.ciphertext); +} + void KRB5_CALLCONV krb5_free_predicted_sam_response(krb5_context ctx, krb5_predicted_sam_response *psr) @@ -594,6 +674,16 @@ krb5_free_enc_sam_response_enc(krb5_context ctx, krb5_xfree(esre); } +void KRB5_CALLCONV +krb5_free_enc_sam_response_enc_2(krb5_context ctx, + krb5_enc_sam_response_enc_2 *esre2) +{ + if (!esre2) + return; + krb5_free_enc_sam_response_enc_2_contents(ctx, esre2); + krb5_xfree(esre2); +} + void KRB5_CALLCONV krb5_free_enc_sam_response_enc_contents(krb5_context ctx, krb5_enc_sam_response_enc *esre) @@ -604,6 +694,16 @@ krb5_free_enc_sam_response_enc_contents(krb5_context ctx, krb5_free_data_contents(ctx, &esre->sam_sad); } +void KRB5_CALLCONV +krb5_free_enc_sam_response_enc_2_contents(krb5_context ctx, + krb5_enc_sam_response_enc_2 *esre2) +{ + if (!esre2) + return; + if (esre2->sam_sad.data) + krb5_free_data_contents(ctx, &esre2->sam_sad); +} + void KRB5_CALLCONV krb5_free_pa_enc_ts(krb5_context ctx, krb5_pa_enc_ts *pa_enc_ts) { diff --git a/src/lib/krb5/krb/preauth2.c b/src/lib/krb5/krb/preauth2.c index 9ee5cdfc5..1e07c18c2 100644 --- a/src/lib/krb5/krb/preauth2.c +++ b/src/lib/krb5/krb/preauth2.c @@ -268,6 +268,24 @@ krb5_error_code pa_sam(krb5_context context, return(KRB5_SAM_UNSUPPORTED); } + /* If we need the password from the user (USE_SAD_AS_KEY not set), */ + /* then get it here. Exception for "old" KDCs with CryptoCard */ + /* support which uses the USE_SAD_AS_KEY flag, but still needs pwd */ + + if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) || + (sam_challenge->sam_type == PA_SAM_TYPE_CRYPTOCARD)) { + + /* etype has either been set by caller or by KRB5_PADATA_ETYPE_INFO */ + /* message from the KDC. If it is not set, pick an enctype that we */ + /* think the KDC will have for us. */ + + if (etype && *etype == 0) + *etype = ENCTYPE_DES_CBC_CRC; + + if (ret = (gak_fct)(context, request->client, *etype, prompter, + prompter_data, salt, as_key, gak_data)) + return(ret); + } sprintf(name, "%.*s", SAMDATA(sam_challenge->sam_type_name, "SAM Authentication", sizeof(name) - 1)); @@ -288,7 +306,7 @@ krb5_error_code pa_sam(krb5_context context, response_data.length = sizeof(response); kprompt.prompt = prompt; - kprompt.hidden = sam_challenge->sam_challenge.length?0:1; + kprompt.hidden = 1; kprompt.reply = &response_data; prompt_type = KRB5_PROMPT_TYPE_PREAUTH; @@ -317,6 +335,11 @@ krb5_error_code pa_sam(krb5_context context, /* XXX What if more than one flag is set? */ if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { + /* Most of this should be taken care of before we get here. We */ + /* will need the user's password and as_key to encrypt the SAD */ + /* and we want to preserve ordering of user prompts (first */ + /* password, then SAM data) so that user's won't be confused. */ + if (as_key->length) { krb5_free_keyblock_contents(context, as_key); as_key->length = 0; @@ -443,6 +466,332 @@ krb5_error_code pa_sam(krb5_context context, return(0); } +static +krb5_error_code pa_sam_2(krb5_context context, + krb5_kdc_req *request, + krb5_pa_data *in_padata, + krb5_pa_data **out_padata, + krb5_data *salt, + krb5_enctype *etype, + krb5_keyblock *as_key, + krb5_prompter_fct prompter, + void *prompter_data, + krb5_gic_get_as_key_fct gak_fct, + void *gak_data) { + + krb5_error_code retval; + krb5_sam_challenge_2 *sc2 = NULL; + krb5_sam_challenge_2_body *sc2b = NULL; + krb5_data tmp_data; + krb5_data response_data; + char name[100], banner[100], prompt[100], response[100]; + krb5_prompt kprompt; + krb5_prompt_type prompt_type; + krb5_data defsalt; + krb5_checksum **cksum; + krb5_data *scratch = NULL; + krb5_boolean valid_cksum = 0; + krb5_enc_sam_response_enc_2 enc_sam_response_enc_2; + krb5_sam_response_2 sr2; + krb5_pa_data *sam_padata; + + if (prompter == NULL) + return KRB5_LIBOS_CANTREADPWD; + + tmp_data.length = in_padata->length; + tmp_data.data = (char *)in_padata->contents; + + if (retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)) + return(retval); + + retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b); + + if (retval) + return(retval); + + if (!sc2->sam_cksum || ! *sc2->sam_cksum) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(KRB5_SAM_NO_CHECKSUM); + } + + if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(KRB5_SAM_UNSUPPORTED); + } + + if (!valid_enctype(sc2b->sam_etype)) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(KRB5_SAM_INVALID_ETYPE); + } + + /* All of the above error checks are KDC-specific, that is, they */ + /* assume a failure in the KDC reply. By returning anything other */ + /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED, */ + /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will */ + /* most likely go on to try the AS_REQ against master KDC */ + + if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { + /* We will need the password to obtain the key used for */ + /* the checksum, and encryption of the sam_response. */ + /* Go ahead and get it now, preserving the ordering of */ + /* prompts for the user. */ + + retval = (gak_fct)(context, request->client, + sc2b->sam_etype, prompter, + prompter_data, salt, as_key, gak_data); + if (retval) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(retval); + } + } + + sprintf(name, "%.*s", + SAMDATA(sc2b->sam_type_name, "SAM Authentication", + sizeof(name) - 1)); + + sprintf(banner, "%.*s", + SAMDATA(sc2b->sam_challenge_label, + sam_challenge_banner(sc2b->sam_type), + sizeof(banner)-1)); + + sprintf(prompt, "%s%.*s%s%.*s", + sc2b->sam_challenge.length?"Challenge is [":"", + SAMDATA(sc2b->sam_challenge, "", 20), + sc2b->sam_challenge.length?"], ":"", + SAMDATA(sc2b->sam_response_prompt, "passcode", 55)); + + response_data.data = response; + response_data.length = sizeof(response); + kprompt.prompt = prompt; + kprompt.hidden = 1; + kprompt.reply = &response_data; + + prompt_type = KRB5_PROMPT_TYPE_PREAUTH; + krb5int_set_prompt_types(context, &prompt_type); + + if (retval = ((*prompter)(context, prompter_data, name, + banner, 1, &kprompt))) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + krb5int_set_prompt_types(context, 0); + return(retval); + } + + krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL); + + /* Generate salt used by string_to_key() */ + if ((salt->length == -1) && (salt->data == NULL)) { + if (retval = krb5_principal2salt(context, request->client, &defsalt)) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(retval); + } + salt = &defsalt; + } else { + defsalt.length = 0; + } + + /* Get encryption key to be used for checksum and sam_response */ + if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) { + /* as_key = string_to_key(password) */ + int i; + + if (as_key->length) { + krb5_free_keyblock_contents(context, as_key); + as_key->length = 0; + } + + /* generate a key using the supplied password */ + retval = krb5_c_string_to_key(context, sc2b->sam_etype, + (krb5_data *)gak_data, salt, as_key); + + if (retval) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + if (defsalt.length) krb5_xfree(defsalt.data); + return(retval); + } + + if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) { + /* as_key = combine_key (as_key, string_to_key(SAD)) */ + krb5_keyblock tmp_kb; + + retval = krb5_c_string_to_key(context, sc2b->sam_etype, + &response_data, salt, &tmp_kb); + + if (retval) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + if (defsalt.length) krb5_xfree(defsalt.data); + return(retval); + } + + /* This should be a call to the crypto library some day */ + /* key types should already match the sam_etype */ + retval = krb5int_c_combine_keys(context, as_key, &tmp_kb, as_key); + + if (retval) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + if (defsalt.length) krb5_xfree(defsalt.data); + return(retval); + } + krb5_free_keyblock_contents(context, &tmp_kb); + } + + if (defsalt.length) + krb5_xfree(defsalt.data); + + } else { + /* as_key = string_to_key(SAD) */ + + if (as_key->length) { + krb5_free_keyblock_contents(context, as_key); + as_key->length = 0; + } + + /* generate a key using the supplied password */ + retval = krb5_c_string_to_key(context, sc2b->sam_etype, + &response_data, salt, as_key); + + if (defsalt.length) + krb5_xfree(defsalt.data); + + if (retval) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(retval); + } + } + + /* Now we have a key, verify the checksum on the sam_challenge */ + + cksum = sc2->sam_cksum; + + while (*cksum) { + /* Check this cksum */ + retval = krb5_c_verify_checksum(context, as_key, + KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM, + &sc2->sam_challenge_2_body, + *cksum, &valid_cksum); + if (retval) { + krb5_free_data(context, scratch); + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(retval); + } + if (valid_cksum) + break; + cksum++; + } + + if (!valid_cksum) { + + /* If KRB5_SAM_SEND_ENCRYPTED_SAD is set, then password is only */ + /* source for checksum key. Therefore, a bad checksum means a */ + /* bad password. Don't give that direct feedback to someone */ + /* trying to brute-force passwords. */ + + if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + /* + * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications + * can interpret that as "password incorrect", which is probably + * the best error we can return in this situation. + */ + return(KRB5KRB_AP_ERR_BAD_INTEGRITY); + } + + /* fill in enc_sam_response_enc_2 */ + enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2; + enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce; + if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) { + enc_sam_response_enc_2.sam_sad = response_data; + } else { + enc_sam_response_enc_2.sam_sad.data = NULL; + enc_sam_response_enc_2.sam_sad.length = 0; + } + + /* encode and encrypt enc_sam_response_enc_2 with as_key */ + retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2, + &scratch); + if (retval) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(retval); + } + + /* Fill in sam_response_2 */ + memset(&sr2, 0, sizeof(sr2)); + sr2.sam_type = sc2b->sam_type; + sr2.sam_flags = sc2b->sam_flags; + sr2.sam_track_id = sc2b->sam_track_id; + sr2.sam_nonce = sc2b->sam_nonce; + + /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded */ + /* enc_sam_response_enc_2 from above */ + + retval = krb5_c_encrypt_length(context, as_key->enctype, scratch->length, + (unsigned int *) &sr2.sam_enc_nonce_or_sad.ciphertext.length); + if (retval) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(retval); + } + + sr2.sam_enc_nonce_or_sad.ciphertext.data = + (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length); + + if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + return(ENOMEM); + } + + retval = krb5_c_encrypt(context, as_key, KRB5_KEYUSAGE_PA_SAM_RESPONSE, + NULL, scratch, &sr2.sam_enc_nonce_or_sad); + if (retval) { + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + krb5_free_data(context, scratch); + krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); + return(retval); + } + krb5_free_data(context, scratch); + scratch = NULL; + + /* Encode the sam_response_2 */ + retval = encode_krb5_sam_response_2(&sr2, &scratch); + krb5_free_sam_challenge_2(context, sc2); + krb5_free_sam_challenge_2_body(context, sc2b); + krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext); + + if (retval) { + return (retval); + } + + /* Almost there, just need to make padata ! */ + sam_padata = malloc(sizeof(krb5_pa_data)); + if (sam_padata == NULL) { + krb5_free_data(context, scratch); + return(ENOMEM); + } + + sam_padata->magic = KV5M_PA_DATA; + sam_padata->pa_type = KRB5_PADATA_SAM_RESPONSE_2; + sam_padata->length = scratch->length; + sam_padata->contents = (krb5_octet *) scratch->data; + + *out_padata = sam_padata; + + return(0); +} + static pa_types_t pa_types[] = { { KRB5_PADATA_PW_SALT, @@ -459,6 +808,11 @@ static pa_types_t pa_types[] = { pa_enc_timestamp, PA_REAL, }, + { + KRB5_PADATA_SAM_CHALLENGE_2, + pa_sam_2, + PA_REAL, + }, { KRB5_PADATA_SAM_CHALLENGE, pa_sam, -- 2.26.2