[3004] in Kerberos-V5-bugs
krb5-kdc/662: Multiple bugs in KDC and client preauth
daemon@ATHENA.MIT.EDU (fcusack@iconnet.net)
Wed Nov 25 01:48:19 1998
Resent-From: gnats@rt-11.MIT.EDU (GNATS Management)
Resent-To: krb5-unassigned@RT-11.MIT.EDU
Resent-Reply-To: krb5-bugs@MIT.EDU, fcusack@iconnet.net
Date: Wed, 25 Nov 1998 01:47:51 -0500 (EST)
From: fcusack@iconnet.net
Reply-To: fcusack@iconnet.net
To: krb5-bugs@MIT.EDU
Cc: fcusack@iconnet.net
>Number: 662
>Category: krb5-kdc
>Synopsis: Multiple bugs in KDC and client preauth
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: krb5-unassigned
>State: open
>Class: sw-bug
>Submitter-Id: unknown
>Arrival-Date: Wed Nov 25 01:48:01 EST 1998
>Last-Modified:
>Originator: Frank Cusack
>Organization:
Icon CMT Corp.
>Release: krb5-current-19981119
>Environment:
Unix
System: SunOS ratbert 5.6 Generic_105181-09 sun4u sparc SUNW,Ultra-5_10
Architecture: sun4
>Description:
Multiple bugs are in the KDC preauth code, most of them in SAM.
Lots of memory leaks, also current code doesn't follow the
password-03 draft.
The patches below correct all the leaks I could find, and
change KDC behavior to match the password-03 draft. Specifically,
when the client receives a sam-challenge with the use-sad-as-key
flag set, and it sends a correct sam-response back, it expects
the ensuing AS_REP (encrypted part) to be encrypted with the
SAD, *not* with the client's password-derived key.
Likewise, when no sam-flags are set, the client expects an
AS_REP to be encrypted with K1^K2, where K1 == password-derived
key and K2 == SAD. (With parity adjusted and weak keys corrected.)
These changes were made by adding a paramter to check_padata(),
"krb5_keyblock *as_key", which is filled in if the KDC should
use a specific key other than the password-derived key for
the AS_REP.
I used the macro AS_REP_105_SAM_COMPAT (on by default) to only
use the correct AS_REP behavior if the macro is *not* defined.
This is b/c 1.0.5 clients always expect the AS_REP to be
encrypted with the password-derived key, regardless of the
sam-flags. Since an instant switchover will probably be
impossible for existing sites, with the compat behavior
the following happens:
The KDC always sets use-sad-as-key. This causes
the client to prompt for the SAD first, without prompting
for the password. The client encrypts a nonce or timestamp
with the SAD (which is correct for use-sad-as-key). When
the KDC gets the response, it sends back the AS_REP encrypted
with the password-derived key (even though use-sad-as-key
was set).
Clients try to decrypt the AS_REP first with the SAD, and
if that fails, then with the password (prompting the user
at this time.) This will allow a site to update all of
it's clients and KDC in whatever order it wants; since 1.0.5
clients will still work against a compat KDC.
Then, after all clients have been updated, the site can
update the KDC to the correct behavior. The compat clients
will now handle use-sad-as-key correctly, without ever
prompting for a password, since the AS_REP will now be
encrypted with the SAD. But changing the KDC means you
will lose the use-sad-as-key behavior in favor of "no flags".
So after the KDC changeover, clients will be prompted
first for their password, then for the SAD.
The site is then urged to update all the clients again,
without the compat mode enabled. This will close a
security hole.
Note that until the clients are updated, if an incorrect
password or an incorrect SAD are entered, the client will
be prompted again for the password. This is to try again
with just the password. Of course, this won't work since
the AS_REP won't be encrypted with just the password.
Strangely, with this macro on, clients prompt for the SAD first,
then the password, when the KDC is also in compat mode.
This jibes with their operation -- first
they have to encrypt a timestamp with the SAD, then decrypt the
AS_REP with the password. It's probably not too difficult
to fix, but the macro was an afterthought, so I consider the
backward behavior a "feature" -- it's readily apparent you
are in compat mode (and vulnerable to certain attacks).
I haven't actually tested compat mode, but the changes are
minimal -- it *should* work. The macro is defined in k5-int.h.
A problem here is that the draft doesn't specify how to salt
the keys. The existing code always sets use-sad-as-key and
uses "no salt" when deriving a key, I do the same. I always
select the normally salted key for the password-derived key.
Thus, I never return padata of type PW-SALT if a use-sad-as-key
or "no" flags were set in a sam-response.
The draft is changing wrt checksums, I calculate them trivially
but it is not checked in the client code. Note that I could
not get the code to work with checksums at all --
krb5_calculate_checksum() [which calls krb5_c_make_checksum()]
always fails b/c it doesn't know the checksum type (I think).
I didn't bother to fix this b/c I understand the sam-checksum
will be removed and the client will use the krb-error checksum.
However, the patches here will not produce working code b/c
of the checksum failure; you'll have to #ifdef 0 that code.
You'll also want to note that the KDC does not compile with
kdb updates enabled; looks like it's due to data structure
changes related to the new crypto API. Some types of preauth
require kdb updates (synchronous tokens, and s/key will
probably require it.)
I made a change to the krb5_prompter_fct, it now takes a
"char *name" parameter. Windows clients can use this as
the window title (patch included), CLI clients can just
print it with the challenge.
I removed all client-side interpretation of the sam-type;
part of the idea for the SAM draft is client independence.
I did a lot of restructuring with the kdc code. kdc_preauth.c
is now very small and is just a dispatcher. There is a new
subdirectory kdc/preauth which contains one file for each
preauth type. This makes it extremely easy to add a new
SAM type (or other preauth type) without touching "base code".
To add a new SAM type (I will submit S/Key shortly, and
securid patches), just edit pa_sam.c to include the new type,
add prototypes in pa_sam.h, and add a pa_sam_xxx.c to
actually implement the mechanism.
I didn't know where to change configure.in to include the
subdirectory, so I made a trivial change to kdc/Makefile.in.
You'll probably (you will) want to do this the right way.
You'll also need to add a --with-securid= flag to configure.
A future S/Key patch will require a --with-opie= flag.
After the patches, I have included each new file
(kdc/kdc_preauth.h and kdc/preauth/*) separated by ====
markers, with the filename. You can look for the text
"end of patches" to find where the new files start.
Some of this source code came from Ken Hornstein, I
just plugged some leaks and restructured it. The securid
stuff comes from Kenneth Renard.
I will submit another pr with patches for kadmin to support
the Cryptocard. This should be in the main distribution
as an example of how to add kadmin support for new SAM types.
I don't think the "old" creds API will work at all with
this preauth. You'll have to update kinit to use the new
API.
>How-To-Repeat:
>Fix:
Index: include/k5-int.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/include/k5-int.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 k5-int.h
--- k5-int.h 1998/11/24 17:05:14 1.1.1.1
+++ k5-int.h 1998/11/25 06:45:27
@@ -75,6 +75,9 @@
#include "osconf.h"
+/* Compatibility switch for SAM preauth */
+#define AS_REP_105_SAM_COMPAT
+
/*
* Begin "k5-config.h"
*/
@@ -330,13 +333,23 @@
#define PA_SAM_TYPE_SKEY_K0 3 /* S/key where KDC has key 0 */
#define PA_SAM_TYPE_SKEY 4 /* Traditional S/Key */
#define PA_SAM_TYPE_SECURID 5 /* Security Dynamics */
+#define PA_SAM_TYPE_CRYPTOCARD 6 /* CRYPTOCard */
+/* password-03 draft says CRYPTOCard is 6 */
+#if 0
#define PA_SAM_TYPE_ACTIVCARD_DEC 6 /* ActivCard decimal mode */
#define PA_SAM_TYPE_ACTIVCARD_HEX 7 /* ActivCard hex mode */
#define PA_SAM_TYPE_DIGI_PATH_HEX 8 /* Digital Pathways hex mode */
+#endif
#define PA_SAM_TYPE_EXP_BASE 128 /* experimental */
#define PA_SAM_TYPE_GRAIL (PA_SAM_TYPE_EXP_BASE+0) /* testing */
#define PA_SAM_TYPE_SECURID_PREDICT (PA_SAM_TYPE_EXP_BASE+1) /* special */
+typedef struct _krb5_rb1_track_data {
+ krb5_magic magic;
+ krb5_keyblock sam_key;
+ krb5_data next_challenge;
+} krb5_rb1_track_data;
+
typedef struct _krb5_predicted_sam_response {
krb5_magic magic;
krb5_keyblock sam_key;
@@ -938,7 +951,31 @@
krb5_prompter_fct, void *,
krb5_gic_get_as_key_fct, void *));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_sam_challenge
+ KRB5_PROTOTYPE((krb5_context, krb5_sam_challenge FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_sam_response
+ KRB5_PROTOTYPE((krb5_context, krb5_sam_response FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_predicted_sam_response
+ KRB5_PROTOTYPE((krb5_context, krb5_predicted_sam_response FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_enc_sam_response_enc
+ KRB5_PROTOTYPE((krb5_context, krb5_enc_sam_response_enc FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_rb1_track_data
+ KRB5_PROTOTYPE((krb5_context, krb5_rb1_track_data FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_sam_challenge_contents
+ KRB5_PROTOTYPE((krb5_context, krb5_sam_challenge FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_sam_response_contents
+ KRB5_PROTOTYPE((krb5_context, krb5_sam_response FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_predicted_sam_response_contents
+ KRB5_PROTOTYPE((krb5_context, krb5_predicted_sam_response FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_enc_sam_response_enc_contents
+ KRB5_PROTOTYPE((krb5_context, krb5_enc_sam_response_enc FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_rb1_track_data_contents
+ KRB5_PROTOTYPE((krb5_context, krb5_rb1_track_data FAR * ));
+KRB5_DLLIMP void KRB5_CALLCONV krb5_free_pa_enc_ts
+ KRB5_PROTOTYPE((krb5_context, krb5_pa_enc_ts FAR *));
+
+
/* #include "krb5/wordsize.h" -- comes in through base-defs.h. */
#include "profile.h"
@@ -1177,6 +1214,9 @@
krb5_error_code encode_krb5_predicted_sam_response
KRB5_PROTOTYPE((const krb5_predicted_sam_response * , krb5_data **));
+krb5_error_code encode_krb5_rb1_track_data
+ KRB5_PROTOTYPE((const krb5_rb1_track_data *, krb5_data **));
+
/*************************************************************************
* End of prototypes for krb5_encode.c
*************************************************************************/
@@ -1195,6 +1235,9 @@
krb5_error_code decode_krb5_predicted_sam_response
KRB5_PROTOTYPE((const krb5_data *, krb5_predicted_sam_response **));
+
+krb5_error_code decode_krb5_rb1_track_data
+ KRB5_PROTOTYPE((const krb5_data *, krb5_rb1_track_data **));
/*************************************************************************
Index: include/krb5.hin
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/include/krb5.hin,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 krb5.hin
--- krb5.hin 1998/11/24 17:05:14 1.1.1.1
+++ krb5.hin 1998/11/25 06:45:27
@@ -2232,6 +2232,7 @@
typedef krb5_error_code (KRB5_CALLCONV *krb5_prompter_fct)(krb5_context context,
void *data,
+ const char *name,
const char *banner,
int num_prompts,
krb5_prompt prompts[]);
@@ -2241,6 +2242,7 @@
krb5_prompter_posix
KRB5_PROTOTYPE((krb5_context context,
void *data,
+ const char *name,
const char *banner,
int num_prompts,
krb5_prompt prompts[]));
Index: include/krb5/kdb.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/include/krb5/kdb.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 kdb.h
--- kdb.h 1998/11/24 17:05:15 1.1.1.1
+++ kdb.h 1998/11/25 06:45:28
@@ -144,6 +144,10 @@
#define KRB5_TL_MOD_PRINC 0x0002
#define KRB5_TL_KADM_DATA 0x0003
#define KRB5_TL_KADM5_E_DATA 0x0004
+#define KRB5_TL_RB1_CHALLENGE 0x0005
+#ifdef SECURID
+#define KRB5_TL_SECURID_STATE 0x0006
+#endif /* SECURID */
/*
* Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set
Index: kdc/Makefile.in
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/Makefile.in,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Makefile.in
--- Makefile.in 1998/11/24 17:05:08 1.1.1.1
+++ Makefile.in 1998/11/25 06:45:28
@@ -23,7 +23,17 @@
$(srcdir)/policy.c \
$(srcdir)/extern.c \
$(srcdir)/replay.c \
- $(srcdir)/kerberos_v4.c
+ $(srcdir)/kerberos_v4.c \
+ $(srcdir)/preauth/pa_enc_ts.c \
+ $(srcdir)/preauth/pa_etype_info.c \
+ $(srcdir)/preauth/pa_pw_salt.c \
+ $(srcdir)/preauth/pa_sam.c \
+ $(srcdir)/preauth/pa_sam_grail.c \
+ $(srcdir)/preauth/pa_sam_enigma.c \
+ $(srcdir)/preauth/pa_sam_digi_path.c \
+ $(srcdir)/preauth/pa_sam_skey.c \
+ $(srcdir)/preauth/pa_sam_securid.c \
+ $(srcdir)/preauth/pa_sam_cryptocard.c
OBJS= \
kdc5_err.o \
@@ -38,7 +48,17 @@
policy.o \
extern.o \
replay.o \
- kerberos_v4.o
+ kerberos_v4.o \
+ pa_enc_ts.o \
+ pa_etype_info.o \
+ pa_pw_salt.o \
+ pa_sam.o \
+ pa_sam_grail.o \
+ pa_sam_enigma.o \
+ pa_sam_digi_path.o \
+ pa_sam_skey.o \
+ pa_sam_securid.o \
+ pa_sam_cryptocard.o
RT_OBJS= rtest.o \
kdc_util.o \
@@ -46,6 +66,70 @@
extern.o
depend:: kdc5_err.c
+
+pa_enc_ts.c: $(srcdir)/preauth/pa_enc_ts.c
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_enc_ts.c $@
+
+pa_enc_ts.o: pa_enc_ts.c
+
+pa_etype_info.c: $(srcdir)/preauth/pa_etype_info.c
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_etype_info.c $@
+
+pa_etype_info.o: pa_etype_info.c
+
+pa_pw_salt.c: $(srcdir)/preauth/pa_pw_salt.c
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_pw_salt.c $@
+
+pa_pw_salt.o: pa_pw_salt.c
+
+pa_sam.c: $(srcdir)/preauth/pa_sam.c pa_sam.h
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_sam.c $@
+
+pa_sam.o: pa_sam.c
+
+pa_sam_grail.c: $(srcdir)/preauth/pa_sam_grail.c pa_sam.h
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_sam_grail.c $@
+
+pa_sam_grail.o: pa_sam_grail.c
+
+pa_sam_enigma.c: $(srcdir)/preauth/pa_sam_enigma.c pa_sam.h
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_sam_enigma.c $@
+
+pa_sam_enigma.o: pa_sam_enigma.c
+
+pa_sam_digi_path.c: $(srcdir)/preauth/pa_sam_digi_path.c pa_sam.h
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_sam_digi_path.c $@
+
+pa_sam_digi_path.o: pa_sam_digi_path.c
+
+pa_sam_skey.c: $(srcdir)/preauth/pa_sam_skey.c pa_sam.h
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_sam_skey.c $@
+
+pa_sam_skey.o: pa_sam_skey.c
+
+pa_sam_securid.c: $(srcdir)/preauth/pa_sam_securid.c pa_sam.h
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_sam_securid.c $@
+
+pa_sam_securid.o: pa_sam_securid.c
+
+pa_sam_cryptocard.c: $(srcdir)/preauth/pa_sam_cryptocard.c pa_sam.h
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_sam_cryptocard.c $@
+
+pa_sam_cryptocard.o: pa_sam_cryptocard.c
+
+pa_sam.h: $(srcdir)/preauth/pa_sam.h
+ $(RM) $@
+ $(CP) $(srcdir)/preauth/pa_sam.h $@
logger.c: $(SRCTOP)/lib/kadm5/logger.c
$(RM) $@
Index: kdc/do_as_req.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/do_as_req.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 do_as_req.c
--- do_as_req.c 1998/11/24 17:05:08 1.1.1.1
+++ do_as_req.c 1998/11/25 06:45:28
@@ -77,7 +77,10 @@
register int i;
krb5_timestamp until, rtime;
char *cname = 0, *sname = 0, *fromstring = 0;
+ krb5_keyblock as_key;
+ memset(&as_key, 0, sizeof(as_key));
+
ticket_reply.enc_part.ciphertext.data = 0;
e_data.data = 0;
encrypting_key.contents = 0;
@@ -256,7 +259,8 @@
* Check the preauthentication if it is there.
*/
if (request->padata) {
- errcode = check_padata(kdc_context, &client, request, &enc_tkt_reply);
+ errcode = check_padata(kdc_context, &client, request, &enc_tkt_reply,
+ &as_key);
if (errcode) {
#ifdef KRBCONF_KDC_MODIFIES_KDB
/*
@@ -328,35 +332,46 @@
}
ticket_reply.enc_part.kvno = server_key->key_data_kvno;
- /*
- * Find the appropriate client key. We search in the order specified
- * by request keytype list.
- */
- client_key = (krb5_key_data *) NULL;
- for (i = 0; i < request->nktypes; i++) {
- useenctype = request->ktype[i];
- if (!valid_enctype(useenctype))
- continue;
+ /* XXX This doesn't work yet if padata has to be returned (eg PW_SALT) */
+#ifndef AS_REP_105_SAM_COMPAT
+ if (!as_key.contents) {
+#endif /* !AS_REP_105_SAM_COMPAT */
+ /*
+ * Find the appropriate client key. We search in the order specified
+ * by request keytype list.
+ */
+ client_key = (krb5_key_data *) NULL;
+ for (i = 0; i < request->nktypes; i++) {
+ useenctype = request->ktype[i];
+ if (!valid_enctype(useenctype))
+ continue;
- if (!krb5_dbe_find_enctype(kdc_context, &client, useenctype, -1,
- 0, &client_key))
+ if (!krb5_dbe_find_enctype(kdc_context, &client, useenctype, -1,
+ 0, &client_key))
break;
- }
- if (!(client_key)) {
- /* Cannot find an appropriate key */
- status = "CANT_FIND_CLIENT_KEY";
- errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
- goto errout;
- }
+ }
+ if (!(client_key)) {
+ /* Cannot find an appropriate key */
+ status = "CANT_FIND_CLIENT_KEY";
+ errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
+ goto errout;
+ }
- /* convert client.key_data into a real key */
- if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
- client_key, &encrypting_key,
- NULL))) {
- status = "DECRYPT_CLIENT_KEY";
- goto errout;
+ /* convert client.key_data into a real key */
+ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context,
+ &master_keyblock,
+ client_key, &encrypting_key,
+ NULL))) {
+ status = "DECRYPT_CLIENT_KEY";
+ goto errout;
+ }
+ encrypting_key.enctype = useenctype;
+#ifndef AS_REP_105_SAM_COMPAT
+ } else {
+ /* check_padata() returned an encrypting key we should use */
+ krb5_copy_keyblock_contents(kdc_context, &as_key, &encrypting_key);
}
- encrypting_key.enctype = useenctype;
+#endif /* !AS_REP_105_SAM_COMPAT */
/* Start assembling the response */
reply.msg_type = KRB5_AS_REP;
@@ -379,11 +394,20 @@
reply_encpart.times.authtime = authtime = kdc_time;
reply_encpart.caddrs = enc_tkt_reply.caddrs;
- reply.enc_part.kvno = client_key->key_data_kvno;
+ if (as_key.contents) {
+ reply.enc_part.kvno = 1; /* Not used */
+ } else {
+ reply.enc_part.kvno = client_key->key_data_kvno;
+ }
+
/* Fetch the padata info to be returned */
- errcode = return_padata(kdc_context, &client, request, &reply, client_key,
- &encrypting_key);
+ if (as_key.contents) {
+ errcode = 0;
+ } else {
+ errcode = return_padata(kdc_context, &client, request, &reply,
+ client_key, &encrypting_key);
+ }
if (errcode) {
status = "KDC_RETURN_PADATA";
goto errout;
@@ -421,6 +445,9 @@
#endif /* KRBCONF_KDC_MODIFIES_KDB */
errout:
+ if (as_key.contents)
+ krb5_free_keyblock_contents(kdc_context, &as_key);
+
if (status)
krb5_klog_syslog(LOG_INFO, "AS_REQ %s(%d): %s: %s for %s%s%s",
fromstring, portnum, status,
Index: kdc/kdc_preauth.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/kdc_preauth.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 kdc_preauth.c
--- kdc_preauth.c 1998/11/24 17:05:08 1.1.1.1
+++ kdc_preauth.c 1998/11/25 06:45:28
@@ -23,99 +23,11 @@
* Preauthentication routines for the KDC.
*/
-/*
- * Copyright (C) 1998 by the FundsXpress, INC.
- *
- * All rights reserved.
- *
- * Export of this software from the United States of America may require
- * a specific license from the United States Government. It is the
- * responsibility of any person or organization contemplating export to
- * obtain such a license before exporting.
- *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of FundsXpress. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission. FundsXpress makes no representations about the suitability of
- * this software for any purpose. It is provided "as is" without express
- * or implied warranty.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
-#include <stdio.h>
+#include "kdc_preauth.h"
-typedef krb5_error_code (*verify_proc)
- KRB5_PROTOTYPE((krb5_context, krb5_db_entry *client,
- krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data));
-
-typedef krb5_error_code (*edata_proc)
- KRB5_PROTOTYPE((krb5_context, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- krb5_pa_data *data));
-
-typedef krb5_error_code (*return_proc)
- KRB5_PROTOTYPE((krb5_context, krb5_pa_data * padata,
- krb5_db_entry *client,
- krb5_kdc_req *request, krb5_kdc_rep *reply,
- krb5_key_data *client_key,
- krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa));
-
-typedef struct _krb5_preauth_systems {
- int type;
- int flags;
- edata_proc get_edata;
- verify_proc verify_padata;
- return_proc return_padata;
-} krb5_preauth_systems;
-
-static krb5_error_code verify_enc_timestamp
- KRB5_PROTOTYPE((krb5_context, krb5_db_entry *client,
- krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data));
-
-static krb5_error_code get_etype_info
- KRB5_PROTOTYPE((krb5_context, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- krb5_pa_data *data));
-static krb5_error_code return_pw_salt
- KRB5_PROTOTYPE((krb5_context, krb5_pa_data * padata,
- krb5_db_entry *client,
- krb5_kdc_req *request, krb5_kdc_rep *reply,
- krb5_key_data *client_key,
- krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa));
-
-/* SAM preauth support */
-static krb5_error_code verify_sam_response
- KRB5_PROTOTYPE((krb5_context, krb5_db_entry *client,
- krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data));
-
-static krb5_error_code get_sam_edata
- KRB5_PROTOTYPE((krb5_context, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- krb5_pa_data *data));
-/*
- * Preauth property flags
- */
-#define PA_HARDWARE 0x00000001
-#define PA_REQUIRED 0x00000002
-#define PA_SUFFICIENT 0x00000004
- /* Not really a padata, so don't include it in the etype list*/
-#define PA_PSEUDO 0x00000008
-
static krb5_preauth_systems preauth_systems[] = {
{
KRB5_PADATA_ENC_TIMESTAMP,
@@ -142,13 +54,13 @@
KRB5_PADATA_SAM_RESPONSE,
0,
0,
- verify_sam_response,
+ sam_verify_response,
0
},
{
KRB5_PADATA_SAM_CHALLENGE,
PA_HARDWARE, /* causes get_preauth_hint_list to use this */
- get_sam_edata,
+ sam_get_edata,
0,
0
},
@@ -163,7 +75,7 @@
krb5_preauth_systems **preauth;
{
krb5_preauth_systems *ap = preauth_systems;
-
+
while ((ap->type != -1) && (ap->type != type))
ap++;
if (ap->type == -1)
@@ -190,9 +102,9 @@
#endif
if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
- !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
+ !isflagset(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH))
return "NEEDED_PREAUTH";
-
+
if (isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
!isflagset(enc_tkt_reply->flags, TKT_FLG_HW_AUTH))
return "NEEDED_HW_PREAUTH";
@@ -205,16 +117,16 @@
krb5_db_entry *client, *server;
krb5_data *e_data;
{
- int hw_only;
- krb5_preauth_systems *ap;
- krb5_pa_data **pa_data, **pa;
- krb5_data *edat;
- krb5_error_code retval;
-
+ krb5_error_code retval;
+ int hw_only;
+ krb5_preauth_systems * ap;
+ krb5_pa_data ** pa_data, **pa;
+ krb5_data * edat;
+
/* Zero these out in case we need to abort */
e_data->length = 0;
e_data->data = 0;
-
+
hw_only = isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH);
pa_data = malloc(sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1));
if (pa_data == 0)
@@ -227,29 +139,28 @@
continue;
if (ap->flags & PA_PSEUDO)
continue;
- *pa = malloc(sizeof(krb5_pa_data));
- if (*pa == 0)
+ if ((*pa = malloc(sizeof(krb5_pa_data))) == NULL)
goto errout;
memset(*pa, 0, sizeof(krb5_pa_data));
(*pa)->magic = KV5M_PA_DATA;
(*pa)->pa_type = ap->type;
if (ap->get_edata) {
- retval = (ap->get_edata)(kdc_context, request, client, server, *pa);
- if (retval) {
- /* just failed on this type, continue */
- free(*pa);
- *pa = 0;
- continue;
- }
+ retval = (ap->get_edata)(kdc_context, request, client, server, *pa);
+ if (retval) {
+ /* just failed on this type, continue */
+ krb5_xfree(*pa);
+ *pa = 0;
+ continue;
+ }
}
pa++;
}
- retval = encode_krb5_padata_sequence((const krb5_pa_data **) pa_data,
- &edat);
- if (retval)
+ if (retval = encode_krb5_padata_sequence((const krb5_pa_data **) pa_data,
+ &edat)) {
goto errout;
+ }
*e_data = *edat;
- free(edat);
+ krb5_xfree(edat);
errout:
krb5_free_pa_data(kdc_context, pa_data);
@@ -262,19 +173,23 @@
*
* Returns 0 if the pre-authentication is valid, non-zero to indicate
* an error code of some sort.
+ *
+ * *as_key gets filled in if the AS_REP should use
+ * a specific key (other than the pass phrase).
*/
krb5_error_code
-check_padata (context, client, request, enc_tkt_reply)
+check_padata (context, client, request, enc_tkt_reply, as_key)
krb5_context context;
krb5_db_entry * client;
krb5_kdc_req * request;
krb5_enc_tkt_part * enc_tkt_reply;
+ krb5_keyblock * as_key;
{
- krb5_error_code retval = 0;
- krb5_pa_data **padata;
- krb5_preauth_systems *pa_sys;
- int pa_ok = 0, pa_found = 0;
+ krb5_error_code retval = 0;
+ krb5_pa_data ** padata;
+ krb5_preauth_systems * pa_sys;
+ int pa_ok = 0, pa_found = 0;
if (request->padata == 0)
return 0;
@@ -286,7 +201,7 @@
continue;
pa_found++;
retval = pa_sys->verify_padata(context, client, request,
- enc_tkt_reply, *padata);
+ enc_tkt_reply, *padata, as_key);
if (retval) {
com_err("krb5kdc", retval, "pa verify failure");
if (pa_sys->flags & PA_REQUIRED) {
@@ -301,8 +216,16 @@
}
if (pa_ok)
return 0;
+
+ /* pa system was not found, but principal doesn't require preauth */
+ if (!pa_found &&
+ !isflagset(client->attributes, KRB5_KDB_REQUIRES_PRE_AUTH) &&
+ !isflagset(client->attributes, KRB5_KDB_REQUIRES_HW_AUTH))
+ return 0;
+
if (!pa_found)
com_err("krb5kdc", retval, "no valid preauth type found");
+
return KRB5KDC_ERR_PREAUTH_FAILED;
}
@@ -371,719 +294,4 @@
if (send_pa_list)
krb5_free_pa_data(context, send_pa_list);
return (retval);
-}
-
-static krb5_error_code
-verify_enc_timestamp(context, client, request, enc_tkt_reply, pa)
- krb5_context context;
- krb5_db_entry * client;
- krb5_kdc_req * request;
- krb5_enc_tkt_part * enc_tkt_reply;
- krb5_pa_data * pa;
-{
- krb5_pa_enc_ts * pa_enc = 0;
- krb5_error_code retval;
- krb5_data scratch;
- krb5_data enc_ts_data;
- krb5_enc_data *enc_data = 0;
- krb5_keyblock key;
- krb5_key_data * client_key;
- krb5_int32 start;
- krb5_timestamp timenow;
-
- scratch.data = pa->contents;
- scratch.length = pa->length;
-
- enc_ts_data.data = 0;
-
- if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
- goto cleanup;
-
- enc_ts_data.length = enc_data->ciphertext.length;
- if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
- goto cleanup;
-
- start = 0;
- while (1) {
- if ((retval = krb5_dbe_search_enctype(context, client,
- &start, enc_data->enctype,
- -1, 0, &client_key)))
- goto cleanup;
-
- if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
- client_key, &key, NULL)))
- goto cleanup;
-
- key.enctype = enc_data->enctype;
-
- retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
- 0, enc_data, &enc_ts_data);
- krb5_free_keyblock_contents(context, &key);
- if (retval == 0)
- break;
- }
-
- if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
- goto cleanup;
-
- if ((retval = krb5_timeofday(context, &timenow)) != 0)
- goto cleanup;
-
- if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
- retval = KRB5KRB_AP_ERR_SKEW;
- goto cleanup;
- }
-
- setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
-
- retval = 0;
-
-cleanup:
- if (enc_data) {
- krb5_free_data_contents(context, &enc_data->ciphertext);
- free(enc_data);
- }
- krb5_free_data_contents(context, &enc_ts_data);
- if (pa_enc)
- free(pa_enc);
- return retval;
-}
-
-/*
- * This function returns the etype information for a particular
- * client, to be passed back in the preauth list in the KRB_ERROR
- * message.
- */
-static krb5_error_code
-get_etype_info(context, request, client, server, pa_data)
- krb5_context context;
- krb5_kdc_req * request;
- krb5_db_entry * client;
- krb5_db_entry * server;
- krb5_pa_data * pa_data;
-{
- krb5_etype_info_entry ** entry = 0;
- krb5_key_data *client_key;
- krb5_error_code retval;
- krb5_data salt;
- krb5_data * scratch;
- krb5_enctype db_etype;
- int i = 0;
- int start = 0;
-
- salt.data = 0;
-
- entry = malloc((client->n_key_data * 2 + 1) * sizeof(krb5_etype_info_entry *));
- if (entry == NULL)
- return ENOMEM;
- entry[0] = NULL;
-
- while (1) {
- retval = krb5_dbe_search_enctype(context, client, &start, -1,
- -1, 0, &client_key);
- if (retval == ENOENT)
- break;
- if (retval)
- goto cleanup;
- db_etype = client_key->key_data_type[0];
- if (db_etype == ENCTYPE_DES_CBC_MD4 || db_etype == ENCTYPE_DES_CBC_MD5)
- db_etype = ENCTYPE_DES_CBC_CRC;
-
- while (1) {
- if ((entry[i] = malloc(sizeof(krb5_etype_info_entry))) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- entry[i+1] = 0;
- entry[i]->magic = KV5M_ETYPE_INFO_ENTRY;
- entry[i]->etype = db_etype;
- entry[i]->length = -1;
- entry[i]->salt = 0;
- retval = get_salt_from_key(context, request->client,
- client_key, &salt);
- if (retval)
- goto cleanup;
- if (salt.length >= 0) {
- entry[i]->length = salt.length;
- entry[i]->salt = salt.data;
- salt.data = 0;
- }
- i++;
- /*
- * If we have a DES_CRC key, it can also be used as a
- * DES_MD5 key.
- */
- if (db_etype == ENCTYPE_DES_CBC_CRC)
- db_etype = ENCTYPE_DES_CBC_MD5;
- else
- break;
- }
- }
- retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry,
- &scratch);
- if (retval)
- goto cleanup;
- pa_data->contents = scratch->data;
- pa_data->length = scratch->length;
- free(scratch);
-
- retval = 0;
-
-cleanup:
- if (entry)
- krb5_free_etype_info(context, entry);
- if (salt.data)
- free(salt.data);
- return retval;
-}
-
-static krb5_error_code
-return_pw_salt(context, in_padata, client, request, reply, client_key,
- encrypting_key, send_pa)
- krb5_context context;
- krb5_pa_data * in_padata;
- krb5_db_entry * client;
- krb5_kdc_req * request;
- krb5_kdc_rep * reply;
- krb5_key_data * client_key;
- krb5_keyblock * encrypting_key;
- krb5_pa_data ** send_pa;
-{
- krb5_error_code retval;
- krb5_pa_data * padata;
- krb5_data * scratch;
- krb5_data salt_data;
-
- if (client_key->key_data_ver == 1 ||
- client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
- return 0;
-
- if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
- return ENOMEM;
- padata->magic = KV5M_PA_DATA;
- padata->pa_type = KRB5_PADATA_PW_SALT;
-
- switch (client_key->key_data_type[1]) {
- case KRB5_KDB_SALTTYPE_V4:
- /* send an empty (V4) salt */
- padata->contents = 0;
- padata->length = 0;
- break;
- case KRB5_KDB_SALTTYPE_NOREALM:
- if ((retval = krb5_principal2salt_norealm(kdc_context,
- request->client,
- &salt_data)))
- goto cleanup;
- padata->contents = (krb5_octet *)salt_data.data;
- padata->length = salt_data.length;
- break;
- case KRB5_KDB_SALTTYPE_AFS3:
- /* send an AFS style realm-based salt */
- /* for now, just pass the realm back and let the client
- do the work. In the future, add a kdc configuration
- variable that specifies the old cell name. */
- padata->pa_type = KRB5_PADATA_AFS3_SALT;
- /* it would be just like ONLYREALM, but we need to pass the 0 */
- scratch = krb5_princ_realm(kdc_context, request->client);
- if ((padata->contents = malloc(scratch->length+1)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- memcpy(padata->contents, scratch->data, scratch->length);
- padata->length = scratch->length+1;
- padata->contents[scratch->length] = 0;
- break;
- case KRB5_KDB_SALTTYPE_ONLYREALM:
- scratch = krb5_princ_realm(kdc_context, request->client);
- if ((padata->contents = malloc(scratch->length)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- memcpy(padata->contents, scratch->data, scratch->length);
- padata->length = scratch->length;
- break;
- case KRB5_KDB_SALTTYPE_SPECIAL:
- if ((padata->contents = malloc(client_key->key_data_length[1]))
- == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- memcpy(padata->contents, client_key->key_data_contents[1],
- client_key->key_data_length[1]);
- padata->length = client_key->key_data_length[1];
- break;
- default:
- free(padata);
- return 0;
- }
-
- *send_pa = padata;
- return 0;
-
-cleanup:
- free(padata);
- return retval;
-}
-
-
-static struct {
- char* name;
- int sam_type;
-} *sam_ptr, sam_inst_map[] = {
- "SNK4", PA_SAM_TYPE_DIGI_PATH,
- "SECURID", PA_SAM_TYPE_SECURID,
- "GRAIL", PA_SAM_TYPE_GRAIL,
- 0, 0
-};
-
-static krb5_error_code
-get_sam_edata(context, request, client, server, pa_data)
- krb5_context context;
- krb5_kdc_req * request;
- krb5_db_entry * client;
- krb5_db_entry * server;
- krb5_pa_data * pa_data;
-{
- krb5_error_code retval;
- krb5_sam_challenge sc;
- krb5_predicted_sam_response psr;
- krb5_data * scratch;
- int i = 0;
- krb5_keyblock encrypting_key;
- char response[9];
- char inputblock[8];
- krb5_data predict_response;
-
- /* Given the client name we can figure out what type of preauth
- they need. The spec is currently for querying the database for
- names that match the types of preauth used. Later we should
- make this mapping show up in kdc.conf. In the meantime, we
- hardcode the following:
- /SNK4 -- Digital Pathways SNK/4 preauth.
- /GRAIL -- experimental preauth
- The first one found is used. See sam_inst_map above.
-
- For SNK4 in particular, the key in the database is the key for
- the device; kadmin needs a special interface for it.
- */
-
- {
- int npr = 1;
- krb5_boolean more;
- krb5_db_entry assoc;
- krb5_key_data *assoc_key;
- krb5_principal newp;
- int probeslot;
-
- sc.sam_type = 0;
-
- retval = krb5_copy_principal(kdc_context, request->client, &newp);
- if (retval) {
- com_err("krb5kdc", retval, "copying client name for preauth probe");
- return retval;
- }
-
- probeslot = krb5_princ_size(context, newp)++;
- krb5_princ_name(kdc_context, newp) =
- realloc(krb5_princ_name(kdc_context, newp),
- krb5_princ_size(context, newp) * sizeof(krb5_data));
-
- for(sam_ptr = sam_inst_map; sam_ptr->name; sam_ptr++) {
- krb5_princ_component(kdc_context,newp,probeslot)->data = sam_ptr->name;
- krb5_princ_component(kdc_context,newp,probeslot)->length =
- strlen(sam_ptr->name);
- npr = 1;
- retval = krb5_db_get_principal(kdc_context, newp, &assoc, &npr, &more);
- if(!retval && npr) {
- sc.sam_type = sam_ptr->sam_type;
- break;
- }
- }
-
- krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
- krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
- krb5_princ_size(context, newp)--;
-
- krb5_free_principal(kdc_context, newp);
-
- /* if sc.sam_type is set, it worked */
- if (sc.sam_type) {
- /* so use assoc to get the key out! */
- {
- /* here's what do_tgs_req does */
- retval = krb5_dbe_find_enctype(kdc_context, &assoc,
- ENCTYPE_DES_CBC_RAW,
- KRB5_KDB_SALTTYPE_NORMAL,
- 0, /* Get highest kvno */
- &assoc_key);
- if (retval) {
- char *sname;
- krb5_unparse_name(kdc_context, request->client, &sname);
- com_err("krb5kdc", retval,
- "snk4 finding the enctype and key <%s>", sname);
- free(sname);
- return retval;
- }
- /* convert server.key into a real key */
- retval = krb5_dbekd_decrypt_key_data(kdc_context,
- &master_keyblock,
- assoc_key, &encrypting_key,
- NULL);
- if (retval) {
- com_err("krb5kdc", retval,
- "snk4 pulling out key entry");
- return retval;
- }
- /* now we can use encrypting_key... */
- }
- } else {
- /* SAM is not an option - so don't return as hint */
- return KRB5_PREAUTH_BAD_TYPE;
- }
- }
- sc.magic = KV5M_SAM_CHALLENGE;
- sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
-
- switch (sc.sam_type) {
- case PA_SAM_TYPE_GRAIL:
- sc.sam_type_name.data = "Experimental System";
- sc.sam_type_name.length = strlen(sc.sam_type_name.data);
- sc.sam_challenge_label.data = "experimental challenge label";
- sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
- sc.sam_challenge.data = "12345";
- sc.sam_challenge.length = strlen(sc.sam_challenge.data);
-
- psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
- /* string2key on sc.sam_challenge goes in here */
- /* eblock is just to set the enctype */
- {
- const krb5_enctype type = ENCTYPE_DES_CBC_MD5;
-
- if ((retval = krb5_c_string_to_key(context, type, &sc.sam_challenge,
- 0 /* salt */, &psr.sam_key)))
- goto cleanup;
-
- if ((retval = encode_krb5_predicted_sam_response(&psr, &scratch)))
- goto cleanup;
-
- {
- size_t enclen;
- krb5_enc_data tmpdata;
-
- if ((retval = krb5_c_encrypt_length(context,
- master_keyblock.enctype,
- scratch->length, &enclen)))
- goto cleanup;
-
- if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- tmpdata.ciphertext.length = enclen;
-
- if ((retval = krb5_c_encrypt(context, &master_keyblock,
- /* XXX */ 0, 0, scratch, &tmpdata)))
- goto cleanup;
-
- sc.sam_track_id = tmpdata.ciphertext;
- }
- }
-
- sc.sam_response_prompt.data = "response prompt";
- sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
- sc.sam_pk_for_sad.length = 0;
- sc.sam_nonce = 0;
- /* Generate checksum */
- /*krb5_checksum_size(context, ctype)*/
- /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
- seed_length,outcksum) */
- /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
- seed_length) */
- sc.sam_cksum.contents = (krb5_octet *)
- malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
- if (sc.sam_cksum.contents == NULL) return(ENOMEM);
-
- retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
- sc.sam_challenge.data,
- sc.sam_challenge.length,
- psr.sam_key.contents, /* key */
- psr.sam_key.length, /* key length */
- &sc.sam_cksum);
- if (retval) { free(sc.sam_cksum.contents); return(retval); }
-
- retval = encode_krb5_sam_challenge(&sc, &scratch);
- if (retval) goto cleanup;
- pa_data->magic = KV5M_PA_DATA;
- pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
- pa_data->contents = scratch->data;
- pa_data->length = scratch->length;
-
- retval = 0;
- break;
- case PA_SAM_TYPE_DIGI_PATH:
- sc.sam_type_name.data = "Digital Pathways";
- sc.sam_type_name.length = strlen(sc.sam_type_name.data);
-#if 1
- sc.sam_challenge_label.data = "Enter the following on your keypad";
- sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
-#endif
- /* generate digit string, take it mod 1000000 (six digits.) */
- {
- int j;
- krb5_keyblock session_key;
- char outputblock[8];
- int i;
-
- session_key.contents = 0;
-
- memset(inputblock, 0, 8);
-
- retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
- &session_key);
-
- if (retval) {
- /* random key failed */
- com_err("krb5kdc", retval,"generating random challenge for preauth");
- return retval;
- }
- /* now session_key has a key which we can pick bits out of */
- /* we need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
- if (session_key.length != 8) {
- com_err("krb5kdc", retval = KRB5KDC_ERR_ETYPE_NOSUPP,
- "keytype didn't match code expectations");
- return retval;
- }
- for(i = 0; i<6; i++) {
- inputblock[i] = '0' + ((session_key.contents[i]/2) % 10);
- }
- if (session_key.contents)
- krb5_free_keyblock_contents(kdc_context, &session_key);
-
- /* retval = krb5_finish_key(kdc_context, &eblock); */
- /* now we have inputblock containing the 8 byte input to DES... */
- sc.sam_challenge.data = inputblock;
- sc.sam_challenge.length = 6;
-
- encrypting_key.enctype = ENCTYPE_DES_CBC_RAW;
-
- if (retval) {
- com_err("krb5kdc", retval, "snk4 processing key");
- }
-
- {
- krb5_data plain;
- krb5_enc_data cipher;
-
- plain.length = 8;
- plain.data = inputblock;
-
- /* XXX I know this is enough because of the fixed raw enctype.
- if it's not, the underlying code will return a reasonable
- error, which should never happen */
- cipher.ciphertext.length = 8;
- cipher.ciphertext.data = outputblock;
-
- if ((retval = krb5_c_encrypt(kdc_context, &encrypting_key,
- /* XXX */ 0, 0, &plain, &cipher))) {
- com_err("krb5kdc", retval, "snk4 response generation failed");
- return retval;
- }
- }
-
- /* now output block is the raw bits of the response; convert it
- to display form */
- for (j=0; j<4; j++) {
- char n[2];
- int k;
- n[0] = outputblock[j] & 0xf;
- n[1] = (outputblock[j]>>4) & 0xf;
- for (k=0; k<2; k++) {
- if(n[k] > 9) n[k] = ((n[k]-1)>>2);
- /* This is equivalent to:
- if(n[k]>=0xa && n[k]<=0xc) n[k] = 2;
- if(n[k]>=0xd && n[k]<=0xf) n[k] = 3;
- */
- }
- /* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
- /* for v5, we just generate a string */
- response[2*j+0] = '0' + n[1];
- response[2*j+1] = '0' + n[0];
- /* and now, response has what we work with. */
- }
- response[8] = 0;
- predict_response.data = response;
- predict_response.length = 8;
-#if 0 /* for debugging, hack the output too! */
-sc.sam_challenge_label.data = response;
-sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
-#endif
- }
-
- psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
- /* string2key on sc.sam_challenge goes in here */
- /* eblock is just to set the enctype */
- {
- retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
- &predict_response, 0 /* salt */,
- &psr.sam_key);
- if (retval) goto cleanup;
-
- retval = encode_krb5_predicted_sam_response(&psr, &scratch);
- if (retval) goto cleanup;
-
- {
- size_t enclen;
- krb5_enc_data tmpdata;
-
- if ((retval = krb5_c_encrypt_length(context,
- master_keyblock.enctype,
- scratch->length, &enclen)))
- goto cleanup;
-
- if ((tmpdata.ciphertext.data = (char *) malloc(enclen)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
- tmpdata.ciphertext.length = enclen;
-
- if ((retval = krb5_c_encrypt(context, &master_keyblock,
- /* XXX */ 0, 0, scratch, &tmpdata)))
- goto cleanup;
-
- sc.sam_track_id = tmpdata.ciphertext;
- }
- if (retval) goto cleanup;
- }
-
- sc.sam_response_prompt.data = "Enter the displayed response";
- sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
- sc.sam_pk_for_sad.length = 0;
- sc.sam_nonce = 0;
- /* Generate checksum */
- /*krb5_checksum_size(context, ctype)*/
- /*krb5_calculate_checksum(context,ctype,in,in_length,seed,
- seed_length,outcksum) */
- /*krb5_verify_checksum(context,ctype,cksum,in,in_length,seed,
- seed_length) */
- sc.sam_cksum.contents = (krb5_octet *)
- malloc(krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES));
- if (sc.sam_cksum.contents == NULL) return(ENOMEM);
-
- retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
- sc.sam_challenge.data,
- sc.sam_challenge.length,
- psr.sam_key.contents, /* key */
- psr.sam_key.length, /* key length */
- &sc.sam_cksum);
- if (retval) { free(sc.sam_cksum.contents); return(retval); }
-
- retval = encode_krb5_sam_challenge(&sc, &scratch);
- if (retval) goto cleanup;
- pa_data->magic = KV5M_PA_DATA;
- pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
- pa_data->contents = scratch->data;
- pa_data->length = scratch->length;
-
- retval = 0;
- break;
- }
-
-cleanup:
- krb5_free_keyblock_contents(context, &encrypting_key);
- return retval;
-}
-
-static krb5_error_code
-verify_sam_response(context, client, request, enc_tkt_reply, pa)
- krb5_context context;
- krb5_db_entry * client;
- krb5_kdc_req * request;
- krb5_enc_tkt_part * enc_tkt_reply;
- krb5_pa_data * pa;
-{
- krb5_error_code retval;
- krb5_data scratch;
- krb5_sam_response *sr = 0;
- krb5_predicted_sam_response *psr = 0;
- krb5_enc_sam_response_enc *esre = 0;
- krb5_timestamp timenow;
-
- scratch.data = pa->contents;
- scratch.length = pa->length;
-
- if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
- scratch.data = 0;
- com_err("krb5kdc", retval, "decode_krb5_sam_response failed");
- goto cleanup;
- }
-
- {
- krb5_enc_data tmpdata;
-
- tmpdata.enctype = ENCTYPE_UNKNOWN;
- tmpdata.ciphertext = sr->sam_track_id;
-
- scratch.length = tmpdata.ciphertext.length;
- if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
-
- if ((retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
- &tmpdata, &scratch))) {
- com_err("krb5kdc", retval, "decrypt track_id failed");
- goto cleanup;
- }
- }
-
- if ((retval = decode_krb5_predicted_sam_response(&scratch, &psr))) {
- com_err("krb5kdc", retval,
- "decode_krb5_predicted_sam_response failed");
- goto cleanup;
- }
-
- {
- free(scratch.data);
- scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
- if ((scratch.data = (char *) malloc(scratch.length)) == NULL) {
- retval = ENOMEM;
- goto cleanup;
- }
-
- if ((retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0,
- 0, &sr->sam_enc_nonce_or_ts, &scratch))) {
- com_err("krb5kdc", retval, "decrypt nonce_or_ts failed");
- goto cleanup;
- }
- }
-
- if ((retval = decode_krb5_enc_sam_response_enc(&scratch, &esre))) {
- com_err("krb5kdc", retval, "decode_krb5_enc_sam_response_enc failed");
- goto cleanup;
- }
-
- if (esre->sam_timestamp != sr->sam_patimestamp) {
- retval = KRB5KDC_ERR_PREAUTH_FAILED;
- goto cleanup;
- }
-
- if ((retval = krb5_timeofday(context, &timenow)))
- goto cleanup;
-
- if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
- retval = KRB5KRB_AP_ERR_SKEW;
- goto cleanup;
- }
-
- setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
-
- cleanup:
- if (retval)
- com_err("krb5kdc", retval, "sam verify failure");
- if (scratch.data) free(scratch.data);
- if (sr) free(sr);
- if (psr) free(psr);
- if (esre) free(esre);
-
- return retval;
}
Index: kdc/kdc_util.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/kdc/kdc_util.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 kdc_util.h
--- kdc_util.h 1998/11/24 17:05:08 1.1.1.1
+++ kdc_util.h 1998/11/25 06:45:29
@@ -141,7 +141,8 @@
krb5_data *e_data));
krb5_error_code check_padata
PROTOTYPE((krb5_context context, krb5_db_entry *client,
- krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply));
+ krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_keyblock *as_key));
krb5_error_code return_padata
PROTOTYPE((krb5_context context, krb5_db_entry *client,
Index: lib/krb5/asn.1/asn1_k_decode.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/asn.1/asn1_k_decode.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 asn1_k_decode.c
--- asn1_k_decode.c 1998/11/24 17:04:41 1.1.1.1
+++ asn1_k_decode.c 1998/11/25 06:45:31
@@ -818,3 +818,18 @@
}
cleanup();
}
+
+asn1_error_code asn1_decode_rb1_track_data(buf, val)
+ asn1buf * buf;
+ krb5_rb1_track_data * val;
+{
+ setup();
+ { begin_structure();
+ opt_string(val->next_challenge, 0, asn1_decode_charstring);
+ get_field(val->sam_key, 1, asn1_decode_encryption_key);
+ end_structure();
+ val->magic = KV5M_RB1_TRACK_DATA;
+ }
+ cleanup();
+}
+
Index: lib/krb5/asn.1/asn1_k_decode.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/asn.1/asn1_k_decode.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 asn1_k_decode.h
--- asn1_k_decode.h 1998/11/24 17:04:41 1.1.1.1
+++ asn1_k_decode.h 1998/11/25 06:45:31
@@ -148,6 +148,8 @@
PROTOTYPE((asn1buf *buf, krb5_sam_response *val));
asn1_error_code asn1_decode_predicted_sam_response
PROTOTYPE((asn1buf *buf, krb5_predicted_sam_response *val));
+asn1_error_code asn1_decode_rb1_track_data
+ PROTOTYPE((asn1buf *buf, krb5_rb1_track_data *val));
/* arrays */
asn1_error_code asn1_decode_authorization_data
Index: lib/krb5/asn.1/asn1_k_encode.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/asn.1/asn1_k_encode.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 asn1_k_encode.c
--- asn1_k_encode.c 1998/11/24 17:04:41 1.1.1.1
+++ asn1_k_encode.c 1998/11/25 06:45:32
@@ -955,3 +955,16 @@
asn1_cleanup();
}
+
+asn1_error_code asn1_encode_rb1_track_data(buf, val, retlen)
+ asn1buf * buf;
+ const krb5_rb1_track_data *val;
+ int *retlen;
+{
+ asn1_setup();
+ asn1_addfield(&(val->sam_key), 1, asn1_encode_encryption_key);
+ add_optstring(val->next_challenge, 0, asn1_encode_charstring);
+ asn1_makeseq();
+ asn1_cleanup();
+}
+
Index: lib/krb5/asn.1/asn1_k_encode.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/asn.1/asn1_k_encode.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 asn1_k_encode.h
--- asn1_k_encode.h 1998/11/24 17:04:41 1.1.1.1
+++ asn1_k_encode.h 1998/11/25 06:45:32
@@ -245,4 +245,8 @@
PROTOTYPE((asn1buf *buf, const krb5_predicted_sam_response *val,
int *retlen));
+asn1_error_code asn1_encode_rb1_track_data
+ PROTOTYPE((asn1buf *buf, const krb5_rb1_track_data *val,
+ int *retlen));
+
#endif
Index: lib/krb5/asn.1/krb5_decode.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/asn.1/krb5_decode.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 krb5_decode.c
--- krb5_decode.c 1998/11/24 17:04:42 1.1.1.1
+++ krb5_decode.c 1998/11/25 06:45:33
@@ -837,3 +837,16 @@
cleanup(free);
}
+krb5_error_code decode_krb5_rb1_track_data(code, rep)
+ const krb5_data * code;
+ krb5_rb1_track_data **rep;
+{
+ setup_buf_only(); /* preallocated */
+ alloc_field(*rep,krb5_rb1_track_data);
+
+ retval = asn1_decode_rb1_track_data(&buf,*rep);
+ if(retval) clean_return(retval);
+
+ cleanup(free);
+}
+
Index: lib/krb5/asn.1/krb5_encode.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/asn.1/krb5_encode.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 krb5_encode.c
--- krb5_encode.c 1998/11/24 17:04:42 1.1.1.1
+++ krb5_encode.c 1998/11/25 06:45:33
@@ -847,3 +847,14 @@
sum += length;
krb5_cleanup();
}
+
+krb5_error_code encode_krb5_rb1_track_data(rep, code)
+ const krb5_rb1_track_data * rep;
+ krb5_data ** code;
+{
+ krb5_setup();
+ retval = asn1_encode_rb1_track_data(buf,rep,&length);
+ if(retval) return retval;
+ sum += length;
+ krb5_cleanup();
+}
Index: lib/krb5/error_tables/kv5m_err.et
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/error_tables/kv5m_err.et,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 kv5m_err.et
--- kv5m_err.et 1998/11/24 17:04:49 1.1.1.1
+++ kv5m_err.et 1998/11/25 06:45:33
@@ -80,5 +80,6 @@
error_code KV5M_PASSWD_PHRASE_ELEMENT, "Bad magic number for passwd_phrase_element"
error_code KV5M_GSS_OID, "Bad magic number for GSSAPI OID"
error_code KV5M_GSS_QUEUE, "Bad magic number for GSSAPI QUEUE"
+error_code KV5M_RB1_TRACK_DATA, "Bad magic number for RB1_TRACK_DATA"
end
Index: lib/krb5/krb/get_in_tkt.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/krb/get_in_tkt.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 get_in_tkt.c
--- get_in_tkt.c 1998/11/24 17:04:43 1.1.1.1
+++ get_in_tkt.c 1998/11/25 06:45:34
@@ -901,6 +901,10 @@
request.padata = NULL;
}
+ /*
+ * krb5_do_preauth() will fill in as_key if a specific
+ * key is to be used for decrypting the AS_REP.
+ */
if (ret = krb5_do_preauth(context, &request,
padata, &request.padata,
&salt, &as_key, prompter,
@@ -955,33 +959,50 @@
/* XXX if there's padata on output, something is wrong, but it's
not obviously an error */
- /* XXX because etypes are handled poorly (particularly wrt SAM,
- where the etype is fixed by the kdc), we may want to try
- decrypt_as_reply twice. If there's an as_key available, try
- it. If decrypting the as_rep fails, or if there isn't an
- as_key at all yet, then use the gak_fct to get one, and try
- again. */
+ /* Get an as_key to use, if we didn't get one from krb5_do_preauth() */
+#ifdef AS_REP_105_SAM_COMPAT
+ {
+ int got_as_key = 0;
+#endif /* AS_REP_105_SAM_COMPAT */
+ if (as_key.length == 0) {
+ if (ret = ((*gak_fct)(context, request.client,
+ local_as_reply->enc_part.enctype,
+ prompter, prompter_data, &salt,
+ &as_key, gak_data)))
+ goto cleanup;
+ }
+#ifdef AS_REP_105_SAM_COMPAT
+ else
+ got_as_key = 1;
+#endif /* AS_REP_105_SAM_COMPAT */
- if (as_key.length)
ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
NULL, &as_key, krb5_kdc_rep_decrypt_proc,
NULL);
- else
- ret = -1;
-
- if (ret) {
- /* if we haven't get gotten a key, get it now */
- if (ret = ((*gak_fct)(context, request.client,
- local_as_reply->enc_part.enctype,
- prompter, prompter_data, &salt,
- &as_key, gak_data)))
- goto cleanup;
+#ifdef AS_REP_105_SAM_COMPAT
+ if (ret && got_as_key) {
+ /* as_key was either SAD, or passwd^SAD; try just passwd. */
+ if (as_key.length)
+ krb5_free_keyblock_contents(context, &as_key);
+ as_key.length = 0;
+ as_key.contents = 0;
+
+ if (ret = ((*gak_fct)(context, request.client,
+ local_as_reply->enc_part.enctype,
+ prompter, prompter_data, &salt,
+ &as_key, gak_data)))
+ goto cleanup;
- if (ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
+ ret = decrypt_as_reply(context, NULL, local_as_reply, NULL,
NULL, &as_key, krb5_kdc_rep_decrypt_proc,
- NULL))
- goto cleanup;
+ NULL);
+ }
+ }
+#endif /* AS_REP_105_SAM_COMPAT */
+
+ if (ret) {
+ goto cleanup;
}
if (ret = verify_as_reply(context, time_now, &request, local_as_reply))
Index: lib/krb5/krb/gic_pwd.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/krb/gic_pwd.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 gic_pwd.c
--- gic_pwd.c 1998/11/24 17:04:43 1.1.1.1
+++ gic_pwd.c 1998/11/25 06:45:34
@@ -51,7 +51,7 @@
prompt.hidden = 1;
prompt.reply = password;
- if (ret = ((*prompter)(context, prompter_data, NULL, 1, &prompt)))
+ if (ret = ((*prompter)(context, prompter_data, NULL, NULL, 1, &prompt)))
return(ret);
}
@@ -200,7 +200,7 @@
pw0.length = sizeof(pw0array);
pw1.length = sizeof(pw1array);
- if (ret = ((*prompter)(context, data, banner,
+ if (ret = ((*prompter)(context, data, NULL, banner,
sizeof(prompt)/sizeof(prompt[0]), prompt)))
goto cleanup;
@@ -296,7 +296,7 @@
hours/24);
/* ignore an error here */
- (*prompter)(context, data, banner, 0, 0);
+ (*prompter)(context, data, NULL, banner, 0, 0);
}
}
Index: lib/krb5/krb/kfree.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/krb/kfree.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 kfree.c
--- kfree.c 1998/11/24 17:04:44 1.1.1.1
+++ kfree.c 1998/11/25 06:45:34
@@ -583,3 +583,126 @@
return;
}
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_sam_challenge(krb5_context ctx, krb5_sam_challenge FAR *sc)
+{
+ if (!sc)
+ return;
+ krb5_free_sam_challenge_contents(ctx, sc);
+ krb5_xfree(sc);
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_sam_challenge_contents(krb5_context ctx, krb5_sam_challenge FAR *sc)
+{
+ if (!sc)
+ return;
+ if (sc->sam_type_name.data)
+ krb5_free_data_contents(ctx, &sc->sam_type_name);
+ if (sc->sam_track_id.data)
+ krb5_free_data_contents(ctx, &sc->sam_track_id);
+ if (sc->sam_challenge_label.data)
+ krb5_free_data_contents(ctx, &sc->sam_challenge_label);
+ if (sc->sam_challenge.data)
+ krb5_free_data_contents(ctx, &sc->sam_challenge);
+ if (sc->sam_response_prompt.data)
+ krb5_free_data_contents(ctx, &sc->sam_response_prompt);
+ if (sc->sam_pk_for_sad.data)
+ krb5_free_data_contents(ctx, &sc->sam_pk_for_sad);
+ if (sc->sam_cksum.contents)
+ krb5_xfree(sc->sam_cksum.contents);
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_sam_response(krb5_context ctx, krb5_sam_response FAR *sr)
+{
+ if (!sr)
+ return;
+ krb5_free_sam_response_contents(ctx, sr);
+ krb5_xfree(sr);
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_sam_response_contents(krb5_context ctx, krb5_sam_response FAR *sr)
+{
+ if (!sr)
+ return;
+ if (sr->sam_track_id.data)
+ krb5_free_data_contents(ctx, &sr->sam_track_id);
+ if (sr->sam_enc_key.ciphertext.data)
+ krb5_free_data_contents(ctx, &sr->sam_enc_key.ciphertext);
+ if (sr->sam_enc_nonce_or_ts.ciphertext.data)
+ krb5_free_data_contents(ctx, &sr->sam_enc_nonce_or_ts.ciphertext);
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_predicted_sam_response(krb5_context ctx,
+ krb5_predicted_sam_response FAR *psr)
+{
+ if (!psr)
+ return;
+ krb5_free_predicted_sam_response_contents(ctx, psr);
+ krb5_xfree(psr);
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_predicted_sam_response_contents(krb5_context ctx,
+ krb5_predicted_sam_response FAR *psr)
+{
+ if (!psr)
+ return;
+ if (psr->sam_key.contents);
+ krb5_free_keyblock_contents(ctx, &psr->sam_key);
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_enc_sam_response_enc(krb5_context ctx,
+ krb5_enc_sam_response_enc FAR *esre)
+{
+ if (!esre)
+ return;
+ krb5_free_enc_sam_response_enc_contents(ctx, esre);
+ krb5_xfree(esre);
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_enc_sam_response_enc_contents(krb5_context ctx,
+ krb5_enc_sam_response_enc FAR *esre)
+{
+ if (!esre)
+ return;
+ if (esre->sam_passcode.data)
+ krb5_free_data_contents(ctx, &esre->sam_passcode);
+}
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_rb1_track_data(krb5_context ctx, krb5_rb1_track_data FAR *rb1td)
+{
+ if (!rb1td)
+ return
+ krb5_free_rb1_track_data_contents(ctx, rb1td);
+ krb5_xfree(rb1td);
+}
+
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_rb1_track_data_contents(krb5_context ctx,
+ krb5_rb1_track_data FAR *rb1td)
+{
+ if (!rb1td)
+ return;
+ if (rb1td->sam_key.contents)
+ krb5_free_keyblock_contents(ctx, &rb1td->sam_key);
+ if (rb1td->next_challenge.data)
+ krb5_free_data_contents(ctx, &rb1td->next_challenge);
+}
+
+
+KRB5_DLLIMP void KRB5_CALLCONV
+krb5_free_pa_enc_ts(krb5_context ctx, krb5_pa_enc_ts FAR *pa_enc_ts)
+{
+ if (!pa_enc_ts)
+ return;
+ krb5_xfree(pa_enc_ts);
+}
+
Index: lib/krb5/krb/preauth.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/krb/preauth.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 preauth.c
--- preauth.c 1998/11/24 17:04:44 1.1.1.1
+++ preauth.c 1998/11/25 06:45:34
@@ -447,6 +447,9 @@
if (sc->sam_cksum.length == 0) {
/* or invalid -- but lets just handle presence now XXX */
+#if 0
+ /* XXX bad/missing checksum means type is invalid also */
+ /* client should ignore this anyway */
switch (sc->sam_type) {
case PA_SAM_TYPE_ENIGMA: /* Enigma Logic */
label = "Challenge for Enigma Logic mechanism";
@@ -472,6 +475,9 @@
label = "Challenge for Security Dynamics mechanism";
break;
}
+#else /* 0 */
+ label = "Challenge";
+#endif /* 0 */
prompt = "Passcode";
label_len = strlen(label);
prompt_len = strlen(prompt);
Index: lib/krb5/krb/preauth2.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/krb/preauth2.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 preauth2.c
--- preauth2.c 1998/11/24 17:04:44 1.1.1.1
+++ preauth2.c 1998/11/25 06:45:34
@@ -152,6 +152,9 @@
return(0);
}
+#if 0
+/* XXX Client should ignore sam-type info. */
+/* This ifdef is tied to one in pa_sam() */
static
char *sam_challenge_banner(sam_type)
krb5_int32 sam_type;
@@ -189,18 +192,17 @@
return(label);
}
+#endif /* 0 */
/* this macro expands to the int,ptr necessary for "%.*s" in an sprintf */
#define SAMDATA(kdata, str, maxsize) \
(kdata.length)? \
- ((((kdata.length)<=(maxsize))?(kdata.length):(maxsize))): \
+ ((((kdata.length)<=(maxsize))?(kdata.length):(strlen(str)))): \
strlen(str), \
- (kdata.length)?(kdata.data):(str)
+ (kdata.length)? \
+ ((((kdata.length)<=(maxsize))?(kdata.data):(str))):(str)
-/* XXX Danger! This code is not in sync with the kerberos-password-02
- draft. This draft cannot be implemented as written. This code is
- compatible with earlier versions of mit krb5 and cygnus kerbnet. */
static
krb5_error_code pa_sam(krb5_context context,
@@ -216,7 +218,8 @@
{
krb5_error_code ret;
krb5_data tmpsam;
- char banner[100], prompt[100], response[100];
+ char name[100], banner[100];
+ char prompt[100], response[100];
krb5_data response_data;
krb5_prompt kprompt;
krb5_data defsalt;
@@ -224,20 +227,67 @@
krb5_sam_response sam_response;
/* these two get encrypted and stuffed in to sam_response */
krb5_enc_sam_response_enc enc_sam_response_enc;
- krb5_keyblock * sam_use_key = 0;
+ krb5_keyblock sam_use_key;
krb5_data * scratch;
krb5_pa_data * pa;
+ int i = 0;
tmpsam.length = in_padata->length;
tmpsam.data = (char *) in_padata->contents;
if (ret = decode_krb5_sam_challenge(&tmpsam, &sam_challenge))
return(ret);
+ if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD)
+ i++;
+ if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)
+ i++;
+ if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)
+ i++;
+
+ /* Flags are mutually exclusive. There might be a better error. */
+ if (i > 1) {
+ krb5_free_sam_challenge(context, sam_challenge);
+ return KRB5_SENDAUTH_BADRESPONSE;
+ }
+
if (sam_challenge->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
- krb5_xfree(sam_challenge);
+ krb5_free_sam_challenge(context, sam_challenge);
return(KRB5_SAM_UNSUPPORTED);
}
+ /* XXX shouldn't use fixed enctype */
+#ifndef AS_REP_105_COMPAT
+ if (!(sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) ||
+ (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
+ /*
+ * We need to use the password as part or all of the key.
+ * If as_key contains info, it should be the users pass phrase.
+ * If not, get the password before issuing the challenge.
+ */
+#endif /* AS_REP_105_COMPAT */
+ if (as_key->length == 0) {
+ if (ret = ((*gak_fct)(context, request->client,
+ ENCTYPE_DES_CBC_MD5, prompter, prompter_data,
+ salt, as_key, gak_data))) {
+ krb5_free_sam_challenge(context, sam_challenge);
+ return(ret);
+ }
+ } else {
+ /* XXX We should convert DES* to DES_CBC_MD5 */
+ if (as_key->enctype != ENCTYPE_DES_CBC_MD5) {
+ krb5_free_sam_challenge(context, sam_challenge);
+ return(KRB5_PROG_ETYPE_NOSUPP);
+ }
+ }
+ } else { /* KRB5_SAM_USE_SAD_AS_KEY */
+ /* Get rid of any as_key info, we will replace it */
+ if (as_key->length) {
+ krb5_free_keyblock_contents(context, as_key);
+ as_key->length = 0;
+ }
+ }
+
+#if 0
sprintf(banner, "%.*s",
SAMDATA(sam_challenge->sam_challenge_label,
sam_challenge_banner(sam_challenge->sam_type),
@@ -250,45 +300,65 @@
sam_challenge->sam_challenge.length?"], ":"",
SAMDATA(sam_challenge->sam_response_prompt, "passcode", 55));
+#else /* 0 */
+
+ sprintf(name, "%.*s",
+ SAMDATA(sam_challenge->sam_type_name, "SAM Authentication",
+ sizeof(name) - 1));
+
+ if (sam_challenge->sam_challenge.length) {
+ sprintf(banner, "%.*s: [%.*s]",
+ SAMDATA(sam_challenge->sam_challenge_label, "Challenge",
+ sizeof(banner) - 25),
+ SAMDATA(sam_challenge->sam_challenge, "", 20));
+ } else {
+ sprintf(banner, "%.*s",
+ SAMDATA(sam_challenge->sam_challenge_label, "",
+ sizeof(banner)));
+ }
+
+ sprintf(prompt, "%.*s",
+ SAMDATA(sam_challenge->sam_response_prompt, "Response",
+ sizeof(response) - 1));
+#endif /* 0 */
+
response_data.data = response;
response_data.length = sizeof(response);
kprompt.prompt = prompt;
- kprompt.hidden = sam_challenge->sam_challenge.length?0:1;
+ kprompt.hidden = sam_challenge->sam_challenge.length ? 0 : 1;
kprompt.reply = &response_data;
- if (ret = ((*prompter)(context, prompter_data, banner, 1, &kprompt))) {
- krb5_xfree(sam_challenge);
+ if (ret = ((*prompter)(context, prompter_data, name,
+ banner, 1, &kprompt))) {
+ krb5_free_sam_challenge(context, sam_challenge);
return(ret);
}
+ /* Setup the response data (nonce or timestamp). */
+ if (sam_challenge->sam_nonce == 0) {
+ if (ret = krb5_us_timeofday(context,
+ &enc_sam_response_enc.sam_timestamp,
+ &enc_sam_response_enc.sam_usec)) {
+ krb5_free_sam_challenge(context, sam_challenge);
+ return(ret);
+ }
+ sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
+ }
+
enc_sam_response_enc.sam_nonce = sam_challenge->sam_nonce;
if (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
+ /* Add the passcode if required. */
enc_sam_response_enc.sam_passcode = response_data;
- } else if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
- if (sam_challenge->sam_nonce == 0) {
- if (ret = krb5_us_timeofday(context,
- &enc_sam_response_enc.sam_timestamp,
- &enc_sam_response_enc.sam_usec)) {
- krb5_xfree(sam_challenge);
- return(ret);
- }
-
- sam_response.sam_patimestamp = enc_sam_response_enc.sam_timestamp;
- }
-
- /* process the key as password */
-
- if (as_key->length) {
- krb5_free_keyblock_contents(context, as_key);
- as_key->length = 0;
- }
+ } else {
+ /* We need to use the response as part or all of the key. */
+ enc_sam_response_enc.sam_passcode.length = 0;
#if 0
if ((salt->length == -1) && (salt->data == NULL)) {
if (ret = krb5_principal2salt(context, request->client,
&defsalt)) {
- krb5_xfree(sam_challenge);
+ krb5_free_sam_challenge(context, sam_challenge);
return(ret);
}
@@ -297,26 +367,57 @@
defsalt.length = 0;
}
#else
+ /* XXX The spec isn't very clear about this right now. */
defsalt.length = 0;
salt = NULL;
#endif
/* XXX the server uses this fixed enctype, so we will, too. */
-
ret = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
- &response_data, salt, as_key);
+ &response_data, salt, &sam_use_key);
if (defsalt.length)
krb5_xfree(defsalt.data);
if (ret) {
- krb5_xfree(sam_challenge);
+ krb5_free_sam_challenge(context, sam_challenge);
return(ret);
}
- enc_sam_response_enc.sam_passcode.length = 0;
- }
+ if (sam_challenge->sam_flags & KRB5_SAM_USE_SAD_AS_KEY) {
+ /* We freed as_key earlier */
+ krb5_copy_keyblock_contents(context, &sam_use_key, as_key);
+ } else {
+ /* Default mode; K1 ^ K2 */
+ /*
+ * XXX Should verify keys are compatible. For now, we know
+ * both are DES_CBC_MD5.
+ */
+ krb5_octet *p = as_key->contents;
+ krb5_octet *q = sam_use_key.contents;
+ int i;
+ for (i = 0; i < as_key->length; i++) {
+ p[i] ^= q[i];
+ }
+
+ /* XXX Probably shouldn't call these directly! */
+ mit_des_fixup_key_parity(as_key->contents);
+ if (mit_des_is_weak_key(as_key->contents))
+ ((krb5_octet *) as_key->contents)[7] ^= 0xf0;
+ }
+ krb5_free_keyblock_contents(context, &sam_use_key);
+ sam_use_key.length = 0;
+ } /* else (sam_challenge->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) */
+
+ /*
+ * as_key now contains the encryption key to use for sam_response
+ * and the AS_REP.
+ */
+
+ /* XXX we now have the necessary info to verify the sam-cksum. */
+ /* Unfortunately, we kind of need to verify it before we prompt! */
+
/* copy things from the challenge */
sam_response.sam_nonce = sam_challenge->sam_nonce;
sam_response.sam_flags = sam_challenge->sam_flags;
@@ -324,36 +425,58 @@
sam_response.sam_type = sam_challenge->sam_type;
sam_response.magic = KV5M_SAM_RESPONSE;
- krb5_xfree(sam_challenge);
-
/* encode the encoded part of the response */
if (ret = encode_krb5_enc_sam_response_enc(&enc_sam_response_enc,
- &scratch))
+ &scratch)) {
+ krb5_free_sam_challenge(context, sam_challenge);
return(ret);
+ }
ret = krb5_encrypt_data(context, as_key, 0, scratch,
&sam_response.sam_enc_nonce_or_ts);
krb5_free_data(context, scratch);
- if (ret)
+ if (ret) {
+ krb5_free_sam_challenge(context, sam_challenge);
return(ret);
+ }
/* sam_enc_key is reserved for future use */
sam_response.sam_enc_key.ciphertext.length = 0;
- if ((pa = malloc(sizeof(krb5_pa_data))) == NULL)
+ if ((pa = malloc(sizeof(krb5_pa_data))) == NULL) {
+ krb5_free_sam_challenge(context, sam_challenge);
+ krb5_free_data_contents(context,
+ &sam_response.sam_enc_nonce_or_ts.ciphertext);
+ /* krb5_free_data_contents(context, &sam_response.sam_enc_key.ciphertext); */
return(ENOMEM);
+ }
if (ret = encode_krb5_sam_response(&sam_response, &scratch)) {
+ krb5_free_sam_challenge(context, sam_challenge);
+ krb5_free_data_contents(context,
+ &sam_response.sam_enc_nonce_or_ts.ciphertext);
+ /* krb5_free_data_contents(context, &sam_response.sam_enc_key.ciphertext); */
free(pa);
return(ret);
}
+ krb5_free_sam_challenge(context, sam_challenge);
+ krb5_free_data_contents(context,
+ &sam_response.sam_enc_nonce_or_ts.ciphertext);
+ /* krb5_free_data_contents(context, &sam_response.sam_enc_key.ciphertext); */
pa->magic = KV5M_PA_DATA;
pa->pa_type = KRB5_PADATA_SAM_RESPONSE;
+ pa->contents = malloc(scratch->length);
+ if (!pa->contents) {
+ pa->length = 0;
+ krb5_free_data(context, scratch);
+ return(ENOMEM);
+ }
+ memcpy(pa->contents, scratch->data, scratch->length);
pa->length = scratch->length;
- pa->contents = (krb5_octet *) scratch->data;
+ krb5_free_data(context, scratch);
*out_padata = pa;
@@ -388,6 +511,11 @@
},
};
+/*
+ * as_key should be allocated storage (not NULL)
+ * If a pass phrase was collected from the user, as_key should
+ * contain the key.
+ */
krb5_error_code
krb5_do_preauth(krb5_context context,
krb5_kdc_req *request,
Index: lib/krb5/os/prompter.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/lib/krb5/os/prompter.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 prompter.c
--- prompter.c 1998/11/24 17:04:51 1.1.1.1
+++ prompter.c 1998/11/25 06:45:35
@@ -25,6 +25,7 @@
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
krb5_prompter_posix(krb5_context context,
void *data,
+ const char *name,
const char *banner,
int num_prompts,
krb5_prompt prompts[])
@@ -41,6 +42,11 @@
int fd;
#endif
+ if (name) {
+ fputs(name, stdout);
+ fputs("\n", stdout);
+ }
+
if (banner) {
fputs(banner, stdout);
fputs("\n", stdout);
@@ -118,6 +124,7 @@
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
krb5_prompter_posix(krb5_context context,
void *data,
+ const char *name,
const char *banner,
int num_prompts,
krb5_prompt prompts[])
Index: windows/lib/gic.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/windows/lib/gic.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 gic.c
--- gic.c 1998/11/24 17:06:09 1.1.1.1
+++ gic.c 1998/11/25 06:45:37
@@ -131,7 +131,7 @@
* broken environment
*/
krb5_error_code KRB5_CALLCONV
-gic_prompter(krb5_context ctx, void *data, const char *banner,
+gic_prompter(krb5_context ctx, void *data, const char *name, const char *banner,
int num_prompts, krb5_prompt prompts[])
{
int rc;
@@ -145,7 +145,8 @@
if (gd->width == 0)
gd->width = 450;
- dlg = vardlg_build((WORD)(gd->width), gd->banner, (WORD)num_prompts, prompts, (WORD)(gd->id));
+ dlg = vardlg_build((WORD)(gd->width), name, gd->banner,
+ (WORD)num_prompts, prompts, (WORD)(gd->id));
rc = DialogBoxIndirect(gd->hinstance, (LPDLGTEMPLATE)dlg, gd->hwnd, gic_dialog);
Index: windows/lib/gic.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/windows/lib/gic.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 gic.h
--- gic.h 1998/11/24 17:06:09 1.1.1.1
+++ gic.h 1998/11/25 06:45:37
@@ -23,6 +23,6 @@
} gic_data;
krb5_error_code KRB5_CALLCONV gic_prompter(krb5_context, void *, const char *,
- int, krb5_prompt []);
+ const char *, int, krb5_prompt []);
#endif /* _WINDOWS_LIB_GIC_H */
Index: windows/lib/vardlg.c
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/windows/lib/vardlg.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 vardlg.c
--- vardlg.c 1998/11/24 17:06:09 1.1.1.1
+++ vardlg.c 1998/11/25 06:45:38
@@ -171,8 +171,8 @@
* fields for each item.
*/
void *
-vardlg_build(WORD cx, const char *banner, WORD n, krb5_prompt prompts[],
- WORD id)
+vardlg_build(WORD cx, const char *name, const char *banner, WORD n,
+ krb5_prompt prompts[], WORD id)
{
unsigned char *p;
WORD i;
@@ -187,7 +187,10 @@
/*
* Store the dialog template
*/
- p += ADD_DLGTEMPLATE(p, 0, 0, cx, 0, "KerbNet", "MS Sans Serif", 8,
+ p += ADD_DLGTEMPLATE(p, 0, 0, cx, 0, name ?
+ strlen(name) < 30 ? name : "KerbNet" :
+ "KerbNet",
+ "MS Sans Serif", 8,
(WORD)(n * 2 + 3));
/*
Index: windows/lib/vardlg.h
===================================================================
RCS file: /icon/d04/cvsroot/3rd-party/krb5-19981119/windows/lib/vardlg.h,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 vardlg.h
--- vardlg.h 1998/11/24 17:06:09 1.1.1.1
+++ vardlg.h 1998/11/25 06:45:38
@@ -25,7 +25,7 @@
* If there are items, we also create a Cancel button and one (label, entry)
* fields for each item.
*/
-void *vardlg_build(WORD, const char *, WORD, krb5_prompt *, WORD);
+void *vardlg_build(WORD, const char *, const char *, WORD, krb5_prompt *, WORD);
void vardlg_config(HWND, WORD, const char *, WORD, krb5_prompt *, WORD);
=====================
****end of patches
=====================
Here are new files:
====================
kdc/kdc_preauth.h
====================
/*
* kdc/kdc_preauth.h
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* Preauthentication routines for the KDC.
*/
typedef krb5_error_code (*verify_proc)
KRB5_PROTOTYPE((krb5_context, krb5_db_entry *client,
krb5_kdc_req *request,
krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
krb5_keyblock *as_key));
typedef krb5_error_code (*edata_proc)
KRB5_PROTOTYPE((krb5_context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
krb5_pa_data *data));
typedef krb5_error_code (*return_proc)
KRB5_PROTOTYPE((krb5_context, krb5_pa_data * padata,
krb5_db_entry *client,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
krb5_pa_data **send_pa));
typedef struct _krb5_preauth_systems {
int type;
int flags;
edata_proc get_edata;
verify_proc verify_padata;
return_proc return_padata;
} krb5_preauth_systems;
krb5_error_code verify_enc_timestamp
KRB5_PROTOTYPE((krb5_context, krb5_db_entry *client,
krb5_kdc_req *request,
krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
krb5_keyblock *as_key));
krb5_error_code get_etype_info
KRB5_PROTOTYPE((krb5_context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
krb5_pa_data *data));
krb5_error_code return_pw_salt
KRB5_PROTOTYPE((krb5_context, krb5_pa_data * padata,
krb5_db_entry *client,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
krb5_pa_data **send_pa));
/* SAM preauth support */
krb5_error_code sam_verify_response
KRB5_PROTOTYPE((krb5_context, krb5_db_entry *client,
krb5_kdc_req *request,
krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
krb5_keyblock *as_key));
krb5_error_code sam_get_edata
KRB5_PROTOTYPE((krb5_context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
krb5_pa_data *data));
/*
* Preauth property flags
*/
#define PA_HARDWARE 0x00000001
#define PA_REQUIRED 0x00000002
#define PA_SUFFICIENT 0x00000004
/* Not really a padata, so don't include it in the etype list*/
#define PA_PSEUDO 0x00000008
====================================
end of kdc/kdc_preauth.h
====================================
====================================
kdc/preauth/pa_sam.h
====================================
/*
* kdc/preauth/pa_sam.h
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_SAM Preauthentication routine for the KDC.
*/
#include "k5-int.h"
/* XXX These probably need windows callconv defines? */
typedef krb5_error_code (*sam_generate_proc)
KRB5_PROTOTYPE((krb5_context context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
krb5_keyblock sam_key,
krb5_keyblock client_key,
int sam_mode,
krb5_pa_data *pa_data));
typedef krb5_error_code (*sam_verify_proc)
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
int sam_mode,
krb5_sam_response *sr,
krb5_enc_tkt_part *enc_tkt_reply, /* b/c of SecurID */
krb5_keyblock *as_key));
typedef struct _krb5_sam_inst {
char * name; /* instance key is stored in */
int sam_type; /* advisory sam-type */
int mode; /* token mode */
sam_generate_proc generate_proc; /* generate challenge */
sam_verify_proc verify_proc; /* verify response */
} krb5_sam_inst;
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_generate_grail
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
krb5_keyblock sam_key,
krb5_keyblock client_key,
int mode,
krb5_pa_data *pa_data));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_verify_grail
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
int mode,
krb5_sam_response *sr,
krb5_enc_tkt_part *enc_tkt_reply,
krb5_keyblock *as_key));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_generate_enigma
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
krb5_keyblock sam_key,
krb5_keyblock client_key,
int mode,
krb5_pa_data *pa_data));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_verify_enigma
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
int mode,
krb5_sam_response *sr,
krb5_enc_tkt_part *enc_tkt_reply,
krb5_keyblock *as_key));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_generate_digi_path
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
krb5_keyblock sam_key,
krb5_keyblock client_key,
int mode,
krb5_pa_data *pa_data));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_verify_digi_path
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
int mode,
krb5_sam_response *sr,
krb5_enc_tkt_part *enc_tkt_reply,
krb5_keyblock *as_key));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_generate_skey
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
krb5_keyblock sam_key,
krb5_keyblock client_key,
int mode,
krb5_pa_data *pa_data));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_verify_skey
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
int mode,
krb5_sam_response *sr,
krb5_enc_tkt_part *enc_tkt_reply,
krb5_keyblock *as_key));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_generate_securid
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
krb5_keyblock sam_key,
krb5_keyblock client_key,
int mode,
krb5_pa_data *pa_data));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_verify_securid
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
int mode,
krb5_sam_response *sr,
krb5_enc_tkt_part *enc_tkt_reply,
krb5_keyblock *as_key));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_generate_cryptocard
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
krb5_keyblock sam_key,
krb5_keyblock client_key,
int mode,
krb5_pa_data *pa_data));
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV sam_verify_cryptocard
KRB5_PROTOTYPE((krb5_context,
krb5_kdc_req *request,
krb5_db_entry assoc,
int npr,
int mode,
krb5_sam_response *sr,
krb5_enc_tkt_part *enc_tkt_reply,
krb5_keyblock *as_key));
#define SKEY_MODE_K0 0
#define SKEY_MODE_STD 1
#define RB1_MODE_DEFAULT 0
#define RB1_MODE_H8 1
#define RB1_MODE_H7 2
#define RB1_MODE_D8 3
#define RB1_MODE_D7 4
#define RB1_MODE_H8_RC 5
#define RB1_MODE_H7_RC 6
#define RB1_MODE_D8_RC 7
#define RB1_MODE_D7_RC 8
#ifdef SECURID
#define SECURID_STATE_NONE 0
#define SECURID_STATE_INITIAL 1
#define SECURID_NEXT_CODE 2
#define SECURID_NEW_PIN 3
#define SECURID_NEW_PIN_AGAIN 4
#endif /* SECURID */
================================
end of kdc/preauth/pa_sam.h
================================
================================
kdc/preauth/pa_enc_ts.c
================================
/*
* kdc/preauth/pa_enc_ts.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_ENC_TIMESTAMP Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include <stdio.h>
krb5_error_code
verify_enc_timestamp(context, client, request, enc_tkt_reply, pa, as_key)
krb5_context context;
krb5_db_entry * client;
krb5_kdc_req * request;
krb5_enc_tkt_part * enc_tkt_reply;
krb5_pa_data * pa;
krb5_keyblock * as_key;
{
krb5_pa_enc_ts * pa_enc = 0;
krb5_error_code retval;
krb5_data scratch;
krb5_data enc_ts_data;
krb5_enc_data *enc_data = 0;
krb5_keyblock key;
krb5_key_data * client_key;
krb5_int32 start;
krb5_timestamp timenow;
scratch.data = pa->contents;
scratch.length = pa->length;
enc_ts_data.data = 0;
if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
goto cleanup;
enc_ts_data.length = enc_data->ciphertext.length;
if ((enc_ts_data.data = malloc(enc_ts_data.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
start = 0;
while (1) {
if ((retval = krb5_dbe_search_enctype(context, client,
&start, enc_data->enctype,
-1, 0, &client_key)))
goto cleanup;
if ((retval = krb5_dbekd_decrypt_key_data(context, &master_keyblock,
client_key, &key, NULL)))
goto cleanup;
key.enctype = enc_data->enctype;
retval = krb5_decrypt_data(context, &key, 0, enc_data, &enc_ts_data);
krb5_free_keyblock_contents(context, &key);
if (retval == 0)
break;
}
if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
goto cleanup;
if ((retval = krb5_timeofday(context, &timenow)) != 0)
goto cleanup;
if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
retval = KRB5KRB_AP_ERR_SKEW;
goto cleanup;
}
setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
retval = 0;
cleanup:
if (enc_data) {
krb5_free_data_contents(context, &enc_data->ciphertext);
free(enc_data);
}
krb5_free_data_contents(context, &enc_ts_data);
if (pa_enc)
krb5_free_pa_enc_ts(context, pa_enc);
return retval;
}
================================
end of kdc/preauth/pa_enc_ts.c
================================
================================
kdc/preauth/pa_enc_unix_time.c
================================
[empty file]
================================
end of kdc/preauth/pa_enc_unix_time.c
================================
================================
kdc/preauth/pa_etype_info.c
================================
/*
* kdc/preauth/pa_etype_info.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* Preauthentication routines for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include <stdio.h>
/*
* This function returns the etype information for a particular
* client, to be passed back in the preauth list in the KRB_ERROR
* message.
*/
krb5_error_code
get_etype_info(context, request, client, server, pa_data)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry * client;
krb5_db_entry * server;
krb5_pa_data * pa_data;
{
krb5_etype_info entry = 0;
krb5_key_data * client_key;
krb5_error_code retval;
krb5_data salt;
krb5_data * scratch;
krb5_enctype db_etype;
int i = 0;
krb5_int32 start = 0;
salt.data = 0;
if ((entry = malloc((client->n_key_data * 2 + 1) *
sizeof(krb5_etype_info_entry *))) == NULL)
return ENOMEM;
entry[0] = NULL;
while (1) {
retval = krb5_dbe_search_enctype(context, client, &start, -1,
-1, 0, &client_key);
if (retval == ENOENT)
break;
if (retval)
goto cleanup;
db_etype = client_key->key_data_type[0];
if (db_etype == ENCTYPE_DES_CBC_MD4 || db_etype == ENCTYPE_DES_CBC_MD5)
db_etype = ENCTYPE_DES_CBC_CRC;
while (1) {
if ((entry[i] = malloc(sizeof(krb5_etype_info_entry))) == NULL) {
retval = ENOMEM;
goto cleanup;
}
entry[i+1] = 0;
entry[i]->magic = KV5M_ETYPE_INFO_ENTRY;
entry[i]->etype = db_etype;
entry[i]->length = -1;
entry[i]->salt = 0;
if ((retval = get_salt_from_key(context, request->client,
client_key, &salt)) == NULL)
goto cleanup;
if (salt.length >= 0) {
entry[i]->length = salt.length;
entry[i]->salt = salt.data;
salt.data = 0;
}
i++;
/*
* If we have a DES_CRC key, it can also be used as a
* DES_MD5 key.
*/
if (db_etype == ENCTYPE_DES_CBC_CRC)
db_etype = ENCTYPE_DES_CBC_MD5;
else
break;
}
}
retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry,
&scratch);
if (retval)
goto cleanup;
pa_data->contents = malloc(scratch->length);
if (pa_data->contents == NULL) {
pa_data->length = 0;
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
memcpy(pa_data->contents, scratch->data, scratch->length);
pa_data->length = scratch->length;
krb5_free_data(context, scratch);
retval = 0;
cleanup:
if (entry)
krb5_free_etype_info(context, entry);
krb5_free_data_contents(context, &salt);
return retval;
}
================================
end of kdc/preauth/pa_etype_info.c
================================
================================
kdc/preauth/pa_pw_salt.c
================================
/*
* kdc/preauth/pa_pw_salt.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_PW_SALT Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include <stdio.h>
krb5_error_code
return_pw_salt(context, in_padata, client, request, reply, client_key,
encrypting_key, send_pa)
krb5_context context;
krb5_pa_data * in_padata;
krb5_db_entry * client;
krb5_kdc_req * request;
krb5_kdc_rep * reply;
krb5_key_data * client_key;
krb5_keyblock * encrypting_key;
krb5_pa_data ** send_pa;
{
krb5_error_code retval;
krb5_pa_data * padata;
krb5_data * scratch;
krb5_data salt_data;
if (client_key->key_data_ver == 1 ||
client_key->key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)
return 0;
if ((padata = malloc(sizeof(krb5_pa_data))) == NULL)
return ENOMEM;
padata->magic = KV5M_PA_DATA;
padata->pa_type = KRB5_PADATA_PW_SALT;
switch (client_key->key_data_type[1]) {
case KRB5_KDB_SALTTYPE_V4:
/* send an empty (V4) salt */
padata->contents = 0;
padata->length = 0;
break;
case KRB5_KDB_SALTTYPE_NOREALM:
if ((retval = krb5_principal2salt_norealm(kdc_context,
request->client,
&salt_data)))
goto cleanup;
padata->contents = (krb5_octet *)salt_data.data;
padata->length = salt_data.length;
break;
case KRB5_KDB_SALTTYPE_AFS3:
/* send an AFS style realm-based salt */
/* for now, just pass the realm back and let the client
do the work. In the future, add a kdc configuration
variable that specifies the old cell name. */
padata->pa_type = KRB5_PADATA_AFS3_SALT;
/* it would be just like ONLYREALM, but we need to pass the \0 */
scratch = krb5_princ_realm(kdc_context, request->client);
if ((padata->contents = malloc(scratch->length + 1)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
memcpy(padata->contents, scratch->data, scratch->length);
padata->length = scratch->length + 1;
padata->contents[scratch->length] = '\0';
break;
case KRB5_KDB_SALTTYPE_ONLYREALM:
scratch = krb5_princ_realm(kdc_context, request->client);
if ((padata->contents = malloc(scratch->length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
memcpy(padata->contents, scratch->data, scratch->length);
padata->length = scratch->length;
break;
case KRB5_KDB_SALTTYPE_SPECIAL:
if ((padata->contents = malloc(client_key->key_data_length[1]))
== NULL) {
retval = ENOMEM;
goto cleanup;
}
memcpy(padata->contents, client_key->key_data_contents[1],
client_key->key_data_length[1]);
padata->length = client_key->key_data_length[1];
break;
default:
free(padata);
return 0;
}
*send_pa = padata;
return 0;
cleanup:
free(padata);
return retval;
}
================================
end of kdc/preauth/pa_pw_salt.c
================================
================================
kdc/preauth/pa_sam.c
================================
/*
* kdc/preauth/pa_sam.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_SAM Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include "pa_sam.h"
/*
* XXX Currently, SAM is defined by the hardware auth flag. This is not
* always true (eg, S/Key). This should probably be changed, however
* the hardware preauth flag is really only advisory, so at least for
* now, this should be OK.
*/
/*
* XXX Most (all?) of the sam types use the master database key to
* encrypt the track-data. This is probably bad.
*/
/*
* To define a new SAM type, place it in the sam_inst_map below,
* and take a look at the code for the other SAM types.
*
* The name field corresponds to the instance that contains the
* key for the SAM device. eg: Principal foo@MIT.EDU has the
* require_hwauth flag set, and the key associated with foo/RB1@MIT.EDU
* is the key programmed into the RB1 token card.
*
* The search for a SAM type starts by taking each sam name in turn,
* and appending it as an instance to the "full principal" name.
* If no principal of the newly formed name is found, the
* "full principal" name is stripped of the last component (instance)
* and the list of sam types is tried again. eg: Principal
* foo/admin@MIT.EDU has require_hwauth set. The search starts with
* foo/admin/<SAM-TYPES>@MIT.EDU, and if nothing is found, continues
* with foo/<SAM-TYPES>@MIT.EDU. This allows you to map instances to
* different sam types or keys.
*
* The mode flag is used to set various modes for the same device,
* eg hex or decimal mode on the CRYPTOCard.
*
* To program the token, you'll need to either add support to
* kadmin, (see the CRYPTOCard code) or do an `xst' and extract
* the key from the keytab with your own tool.
*
*/
static krb5_error_code sam_find_index(krb5_context,
krb5_principal client,
krb5_db_entry *assoc,
int *npr,
int *index);
/*
* Feel free to rearrange the order of these entries;
* earler entries are found sooner.
*/
static krb5_sam_inst sam_inst_map[] = {
{
/* Experimental system */
"GRAIL", PA_SAM_TYPE_GRAIL, 0,
sam_generate_grail, sam_verify_grail
},
{
/* CRYPTOCard RB-1 (or ST-1, if you can program it) */
"RB1", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_DEFAULT,
sam_generate_cryptocard, sam_verify_cryptocard
},
{
/* Digital Pathways */
"SNK4", PA_SAM_TYPE_DIGI_PATH, 0,
sam_generate_digi_path, sam_verify_digi_path
},
{
/* Security Dynamics SecurID */
"SECURID", PA_SAM_TYPE_SECURID, 0,
sam_generate_securid, sam_verify_securid
},
{
/* Enigma Logic (SafeWord) */
"EL", PA_SAM_TYPE_ENIGMA, 0,
sam_generate_enigma, sam_verify_enigma
},
{
/* S/Key where KDC has key 0 */
"SKEY0", PA_SAM_TYPE_SKEY_K0, SKEY_MODE_K0,
sam_generate_skey, sam_verify_skey
},
{
/* Standard S/Key */
"SKEY", PA_SAM_TYPE_SKEY, SKEY_MODE_STD,
sam_generate_skey, sam_verify_skey
},
#if 0
/* CRYPTOCARD mode-specific */
#ifdef KRBCONF_KDC_MODIFIES_KDB
{
"RB1-H8", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_H8,
sam_generate_cryptocard, sam_verify_cryptocard
},
{
"RB1-H7", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_H7,
sam_generate_cryptocard, sam_verify_cryptocard
},
{
"RB1-D8", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_D8,
sam_generate_cryptocard, sam_verify_cryptocard
},
{
"RB1-D7", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_D7,
sam_generate_cryptocard, sam_verify_cryptocard
},
#endif /* KRBCONF_KDC_MODIFIES_KDB */
{
"RB1-H8-RC", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_H8_RC,
sam_generate_cryptocard, sam_verify_cryptocard
},
{
"RB1-H7-RC", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_H7_RC,
sam_generate_cryptocard, sam_verify_cryptocard
},
{
"RB1-D8-RC", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_D8_RC,
sam_generate_cryptocard, sam_verify_cryptocard
},
{
"RB1-D7-RC", PA_SAM_TYPE_CRYPTOCARD, RB1_MODE_D7_RC,
sam_generate_cryptocard, sam_verify_cryptocard
},
#endif /* 0 */
{ 0, }
};
krb5_error_code
sam_get_edata(context, request, client, server, pa_data)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry * client;
krb5_db_entry * server;
krb5_pa_data * pa_data;
{
krb5_error_code retval;
krb5_keyblock sam_key;
krb5_keyblock client_key;
krb5_db_entry assoc;
krb5_boolean more;
krb5_key_data * assoc_key = 0;
int npr = 0;
int sam_index;
memset(&sam_key, 0, sizeof(sam_key));
memset(&client_key, 0, sizeof(client_key));
/* Get the client (principal) key */
retval = krb5_db_get_principal(kdc_context, request->client, &assoc,
&npr, &more);
if (!retval && npr == 1 && more == FALSE) {
/* here's what do_tgs_req does */
retval = krb5_dbe_find_enctype(kdc_context, &assoc,
ENCTYPE_DES_CBC_RAW,
KRB5_KDB_SALTTYPE_NORMAL,
0, /* Get highest kvno */
&assoc_key);
if (retval) {
char *sname;
krb5_unparse_name(kdc_context, request->client, &sname);
com_err("krb5kdc", retval,
"sam_get_edata finding the enctype and key <%s>", sname);
krb5_db_free_principal(kdc_context, &assoc, npr);
krb5_xfree(sname);
return retval;
}
retval = krb5_dbekd_decrypt_key_data(kdc_context,
&master_keyblock,
assoc_key, &client_key,
NULL);
if (retval) {
com_err("krb5kdc", retval,
"sam_get_edata pulling out key entry");
krb5_db_free_principal(kdc_context, &assoc, npr);
return retval;
}
} else {
/* Principal not found, or more than 1; we should not get here! */
if (npr)
krb5_db_free_principal(kdc_context, &assoc, npr);
com_err("krb5kdc", retval, "looking up principal for preauth");
if (!retval)
retval = KRB5KDC_ERR_PREAUTH_FAILED;
return retval;
}
krb5_db_free_principal(kdc_context, &assoc, npr);
npr = 0;
assoc_key = 0;
/* Find the SAM type ... */
if (retval = sam_find_index(context, request->client, &assoc, &npr,
&sam_index)) {
com_err("krb5kdc", retval, "locating sam type");
return retval;
}
/* ... and get the key */
retval = krb5_dbe_find_enctype(kdc_context, &assoc,
ENCTYPE_DES_CBC_RAW,
KRB5_KDB_SALTTYPE_NORMAL,
0, /* Get highest kvno */
&assoc_key);
if (retval) {
char *sname;
krb5_unparse_name(kdc_context, request->client, &sname);
com_err("krb5kdc", retval,
"sam_get_edata finding the enctype and key <%s>", sname);
krb5_db_free_principal(kdc_context, &assoc, npr);
krb5_xfree(sname);
return retval;
}
retval = krb5_dbekd_decrypt_key_data(kdc_context,
&master_keyblock,
assoc_key, &sam_key,
NULL);
if (retval) {
com_err("krb5kdc", retval,
"sam_get_edata pulling out key entry");
krb5_db_free_principal(kdc_context, &assoc, npr);
return retval;
}
retval = (sam_inst_map[sam_index].generate_proc)(context, request, assoc,
npr, sam_key, client_key,
sam_inst_map[sam_index].mode,
pa_data);
krb5_db_free_principal(kdc_context, &assoc, npr);
krb5_free_keyblock_contents(context, &client_key);
krb5_free_keyblock_contents(context, &sam_key);
return retval;
}
krb5_error_code
sam_verify_response(context, client, request, enc_tkt_reply, pa_data, as_key)
krb5_context context;
krb5_db_entry * client;
krb5_kdc_req * request;
krb5_enc_tkt_part * enc_tkt_reply;
krb5_pa_data * pa_data;
krb5_keyblock * as_key;
{
krb5_error_code retval;
krb5_data scratch;
krb5_sam_response * sr = 0;
krb5_db_entry assoc;
int npr;
int sam_index;
scratch.data = pa_data->contents;
scratch.length = pa_data->length;
retval = decode_krb5_sam_response(&scratch, &sr);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_sam_response failed");
return retval;
}
if (retval = sam_find_index(context, request->client, &assoc, &npr,
&sam_index)) {
com_err("krb5kdc", retval, "locating sam type for verify");
goto cleanup;
}
retval = (sam_inst_map[sam_index].verify_proc)(context, request, assoc, npr,
sam_inst_map[sam_index].mode,
sr, enc_tkt_reply, as_key);
krb5_db_free_principal(kdc_context, &assoc, npr);
cleanup:
if (sr)
krb5_free_sam_response(context, sr);
return retval;
}
/*
* Finds the db_entry for the SAM associated with the principal.
* If successful, caller is responsible for freeing assoc.
*/
static krb5_error_code
sam_find_index(context, client, assoc, npr, index)
krb5_context context;
krb5_principal client;
krb5_db_entry *assoc;
int *npr;
int *index;
{
krb5_error_code retval = 0;
krb5_principal newp;
krb5_boolean more;
int probeslot;
int sam_type = 0;
/* Find the SAM type and get the key */
if (retval = krb5_copy_principal(kdc_context, client, &newp))
return retval;
/* Search for foo/inst/SAM, then foo/SAM until failure. */
probeslot = krb5_princ_size(context, newp);
while (probeslot && !sam_type) {
krb5_princ_size(context, newp)++;
krb5_princ_name(kdc_context, newp) =
realloc(krb5_princ_name(kdc_context, newp),
krb5_princ_size(context, newp) * sizeof(krb5_data));
*npr = 0;
/* Look for a SAM instance */
for (*index = 0; sam_inst_map[*index].name; (*index)++) {
krb5_princ_component(kdc_context, newp, probeslot)->data =
sam_inst_map[*index].name;
krb5_princ_component(kdc_context, newp, probeslot)->length =
strlen(sam_inst_map[*index].name);
retval = krb5_db_get_principal(kdc_context, newp, assoc,
npr, &more);
if (!retval && *npr == 1 && more == FALSE) {
sam_type = sam_inst_map[*index].sam_type;
break;
} else if (*npr == 0 && more == FALSE) {
/* Principal not found */
continue;
} else {
/* Some kind of problem */
break;
}
}
/*
* Make sure to "free" the SAM instance ourself, since it
* points to static data.
*/
krb5_princ_component(kdc_context,newp,probeslot)->data = 0;
krb5_princ_component(kdc_context,newp,probeslot)->length = 0;
krb5_princ_size(context, newp)--;
/* Free the last component, to be replaced with a SAM type. */
probeslot--;
free(krb5_princ_component(context, newp, probeslot)->data);
krb5_princ_component(context, newp, probeslot)->data = 0;
krb5_princ_component(context, newp, probeslot)->length = 0;
krb5_princ_size(context, newp)--;
}
krb5_free_principal(kdc_context, newp);
if (!sam_type) {
if (*npr)
krb5_db_free_principal(kdc_context, assoc, *npr);
if (!retval)
retval = KRB5_PREAUTH_BAD_TYPE;
}
return retval;
}
================================
end of kdc/preauth/pa_sam.c
================================
================================
kdc/preauth/pa_sam_cryptocard.c
================================
/*
* kdc/preauth/pa_sam_cryptocard.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_SAM Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include "pa_sam.h"
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_generate_cryptocard(context, request, assoc, npr, sam_key, client_key,
mode, pa_data)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
krb5_keyblock sam_key;
krb5_keyblock client_key;
int mode;
krb5_pa_data * pa_data;
{
krb5_error_code retval;
krb5_sam_challenge sc;
krb5_rb1_track_data rb1_track_data;
krb5_tl_data tl_data;
krb5_data * scratch;
krb5_enc_data tmpdata;
krb5_keyblock session_key;
size_t enclen;
int i;
char inputblock[8];
char outputblock[8];
char response[9];
krb5_data predict_response;
memset(&sc, 0, sizeof(sc));
memset(&rb1_track_data, 0, sizeof(rb1_track_data));
memset(inputblock, 0, 8);
session_key.contents = 0;
sc.magic = KV5M_SAM_CHALLENGE;
sc.sam_type = PA_SAM_TYPE_CRYPTOCARD;
#ifndef AS_REP_105_SAM_COMPAT
sc.sam_flags = 0;
#else /* !AS_REP_105_SAM_COMPAT */
sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
#endif /* !AS_REP_105_SAM_COMPAT */
sc.sam_type_name.data = "CRYPTOCard RB-1 Authentication";
sc.sam_type_name.length = strlen(sc.sam_type_name.data);
#ifdef KRBCONF_KDC_MODIFIES_KDB
/*
* Determine if we should use a precomputed challenge.
* (Event Synchronous mode)
*/
tl_data.tl_data_type = KRB5_TL_RB1_CHALLENGE;
if ((retval = krb5_dbe_lookup_tl_data(kdc_context, &assoc, &tl_data)) ||
(tl_data.tl_data_length == 0)) {
#endif /* KRBCONF_KDC_MODIFIES_KDB */
/*
* No precomputed challenge, so let's make a new one.
*
* What happens here is close to the Digital Pathways challenge
* generation: We generate a random key and take each byte,
* divide by 2 and do a mod 10. Note that we do this for
* 8 bytes, instead of the SNK 6 bytes. The challenge length
* should probably be configurable ...
*/
sc.sam_challenge_label.data =
"Press CH/MAC and enter this on the keypad";
if (retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
&session_key)) {
com_err("krb5kdc", retval,
"generating random challenge for preauth");
goto cleanup;
}
/* Sanity check */
if (session_key.length != 8) {
com_err("krb5kdc", retval = KRB5KDC_ERR_ETYPE_NOSUPP,
"keytype didn't match code expectations");
goto cleanup;
}
/* Now session_key has a key which we can pick bits out of. */
/* We need eight decimal digits. Grab 8 bytes, div 2, mod 10 each. */
for (i = 0; i < 8; i++) {
inputblock[i] = '0' + ((session_key.contents[i] / 2) % 10);
}
#ifdef KRBCONF_KDC_MODIFIES_KDB
} else {
/* We had precomputed a challenge from before. */
sc.sam_challenge_label.data =
"Press ENT and compare this challenge to the one on your display";
memcpy(inputblock, tl_data.tl_data_contents, tl_data.tl_data_length);
}
#endif /* KRBCONF_KDC_MODIFIES_KDB */
sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
sc.sam_challenge.data = inputblock;
sc.sam_challenge.length = 8;
/* Calculate the response (SAD) */
sam_key.enctype = ENCTYPE_DES_CBC_RAW;
{
krb5_data plain;
krb5_enc_data cipher;
plain.length = 8;
plain.data = inputblock;
/*
* XXX I know this is enough because of the fixed raw enctype.
* if it's not, the underlying code will return a reasonable
* error, which should never happen.
*/
cipher.ciphertext.length = 8;
cipher.ciphertext.data = outputblock;
if (retval = krb5_c_encrypt(kdc_context, &sam_key, /* XXX */ 0, 0,
&plain, &cipher)) {
com_err("krb5kdc", retval, "snk4 response generation failed");
goto cleanup;
}
}
/*
* Now convert the output block to something visible. Note
* that we want to use raw hex as the output from the RB-1,
* so it's a little different than the SNK code.
*/
for (i = 0; i < 4; i++) {
char n[2];
n[0] = outputblock[i] & 0xf;
n[1] = (outputblock[i] >> 4) & 0xf;
/* for v4, we keygen: *(j+(char*)&key1) = (n[1]<<4) | n[0]; */
/* for v5, we just generate a string */
response[2 * i + 0] = n[1] > 9 ? n[1] - 10 + 'A' : '0' + n[1];
response[2 * i + 1] = n[0] > 9 ? n[0] - 10 + 'A' : '0' + n[0];
}
/* And now, response has "raw" RB-1 output. */
response[8] = '\0';
predict_response.data = response;
predict_response.length = 8;
/* Now, convert the response to the card mode. */
switch (mode) {
case RB1_MODE_H8:
case RB1_MODE_H8_RC:
/* no changes needed */
predict_response.length = 8;
break;
case RB1_MODE_H7:
case RB1_MODE_H7_RC:
memmove(&response[3], &response[4], 5);
response[7] = '\0';
predict_response.length = 7;
break;
case RB1_MODE_DEFAULT: /* move this as needed */
case RB1_MODE_D8:
case RB1_MODE_D8_RC:
for (i = 0; i < 8; i++) {
if (response[i] > '9')
response[i] -= ('A' - '0');
}
predict_response.length = 8;
break;
case RB1_MODE_D7:
case RB1_MODE_D7_RC:
for (i = 0; i < 8; i++) {
if (response[i] > '9')
response[i] -= ('A' - '0');
}
memmove(&response[3], &response[4], 5);
response[7] = '\0';
predict_response.length = 7;
break;
default:
com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
"Unknown mode generating RB-1 challenge");
goto cleanup;
} /* switch (mode) */
#ifdef KRBCONF_KDC_MODIFIES_KDB
/* Generate the next challenge if we are in a synchronous mode. */
switch (mode) {
case RB1_MODE_DEFAULT: /* move this as needed */
case RB1_MODE_H8:
case RB1_MODE_H7:
case RB1_MODE_D8:
case RB1_MODE_D7:
/*
* Simply take the full encrypted response block, convert it
* to ASCII numerals, and convert 0xa - 0xf to 0x0 - 0x05
*/
if ((rb1_track_data.next_challenge.data = malloc(8)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
rb1_track_data.next_challenge.length = 8;
for (i = 0; i < 8; i++) {
rb1_track_data.next_challenge.data[i] =
(outputblock[i] & 0x0f) | 0x30;
if (rb1_track_data.next_challenge.data[i] > 0x39)
rb1_track_data.next_challenge.data[i] -= 10;
}
break;
case RB1_MODE_H8_RC:
case RB1_MODE_H7_RC:
case RB1_MODE_D8_RC:
case RB1_MODE_D7_RC:
break;
} /* switch (mode) */
#endif /* KRBCONF_KDC_MODIFIES_KDB */
/* Convert the SAD into a key. */
rb1_track_data.magic = KV5M_PREDICTED_SAM_RESPONSE;
if (retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
&predict_response, 0 /* salt */,
&rb1_track_data.sam_key)) {
goto cleanup;
}
#ifndef AS_REP_105_SAM_COMPAT
/* And XOR with the principal's long term key (in place). */
/* XXX There should be an abstraction for this ... */
{
krb5_octet *p = rb1_track_data.sam_key.contents;
krb5_octet *q = client_key.contents;
for (i = 0; i < rb1_track_data.sam_key.length; i++)
p[i] ^= q[i];
}
mit_des_fixup_key_parity(rb1_track_data.sam_key.contents);
if (mit_des_is_weak_key(rb1_track_data.sam_key.contents))
((krb5_octet *) rb1_track_data.sam_key.contents)[7] ^= 0xf0;
#endif /* !AS_REP_105_SAM_COMPAT */
/*
* Now we have an encrypting key that the client will use for
* the sam response, and that the KDC should use for the AP_REP.
*/
if (retval = encode_krb5_rb1_track_data(&rb1_track_data, &scratch))
goto cleanup;
if (retval = krb5_c_encrypt_length(context, master_keyblock.enctype,
scratch->length, &enclen)) {
krb5_free_data(context, scratch);
goto cleanup;
}
if ((tmpdata.ciphertext.data = malloc(enclen)) == NULL) {
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
tmpdata.ciphertext.length = enclen;
sc.sam_track_id = tmpdata.ciphertext; /* So we know to free it. */
retval = krb5_c_encrypt(context, &master_keyblock,
/* XXX */ 0, 0, scratch, &tmpdata);
krb5_free_data(context, scratch);
if (retval)
goto cleanup;
sc.sam_response_prompt.data = "Enter the displayed response";
sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
sc.sam_pk_for_sad.length = 0;
sc.sam_nonce = 0;
/* Generate checksum */
sc.sam_cksum.length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES);
if ((sc.sam_cksum.contents = malloc(sc.sam_cksum.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
if (retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
sc.sam_challenge.data,
sc.sam_challenge.length,
/* key */ rb1_track_data.sam_key.contents,
/* key length */ rb1_track_data.sam_key.length,
&sc.sam_cksum)) {
goto cleanup;
}
if (retval = encode_krb5_sam_challenge(&sc, &scratch))
goto cleanup;
pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
pa_data->contents = malloc(scratch->length);
if (!pa_data->contents) {
pa_data->length = 0;
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
memcpy(pa_data->contents, scratch->data, scratch->length);
pa_data->length = scratch->length;
krb5_free_data(context, scratch);
cleanup:
if (rb1_track_data.sam_key.contents)
krb5_free_keyblock_contents(context, &rb1_track_data.sam_key);
if (rb1_track_data.next_challenge.data)
krb5_xfree(rb1_track_data.next_challenge.data);
/* Don't use the abstraction to free sc; most of it is static. */
if (sc.sam_track_id.data);
krb5_free_data_contents(context, &sc.sam_track_id);
if (sc.sam_cksum.contents)
krb5_xfree(sc.sam_cksum.contents);
if (session_key.contents)
krb5_free_keyblock_contents(context, &session_key);
return retval;
}
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_verify_cryptocard(context, request, assoc, npr, mode, sr, enc_tkt_reply,
as_key)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
int mode;
krb5_sam_response * sr;
krb5_enc_tkt_part * enc_tkt_reply;
krb5_keyblock * as_key;
{
krb5_error_code retval;
krb5_data scratch;
krb5_enc_data tmpdata;
krb5_tl_data tl_data;
krb5_rb1_track_data * rb1_track_data = 0;
krb5_enc_sam_response_enc * esre = 0;
krb5_timestamp timenow;
/* Sanity check */
if (sr->sam_type != PA_SAM_TYPE_CRYPTOCARD) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "inconsistent sam_type (RB1 expected)");
goto cleanup;
}
scratch.length = sr->sam_track_id.length;
if (!scratch.length) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "rb1 track_id data not found");
goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
tmpdata.enctype = ENCTYPE_UNKNOWN;
tmpdata.ciphertext = sr->sam_track_id;
if (retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
&tmpdata, &scratch)) {
/* Record our retval, but let the client see a generic error. */
com_err("krb5kdc", retval, "decrypt track_id failed");
retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_xfree(scratch.data);
goto cleanup;
}
retval = decode_krb5_rb1_track_data(&scratch, &rb1_track_data);
krb5_xfree(scratch.data);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_rb1_track_data failed");
goto cleanup;
}
scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
if (!scratch.length) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "RB1 sam_enc_nonce_or_ts not found");
goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
/* Now use rb1_track_data->sam_key to verify */
if (retval = krb5_c_decrypt(context, &rb1_track_data->sam_key,
/* XXX */ 0, 0,
&sr->sam_enc_nonce_or_ts, &scratch)) {
com_err("krb5kdc", retval, "decrypt nonce_or_ts failed");
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
retval = decode_krb5_enc_sam_response_enc(&scratch, &esre);
krb5_xfree(scratch.data);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_enc_sam_response_enc failed");
goto cleanup;
}
if (esre->sam_timestamp != sr->sam_patimestamp) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
if (retval = krb5_timeofday(context, &timenow))
goto cleanup;
if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
retval = KRB5KRB_AP_ERR_SKEW;
goto cleanup;
}
/* Success! Make sure we use the sam_key for the AS_REP. */
krb5_copy_keyblock_contents(context, &rb1_track_data->sam_key, as_key);
#ifdef KRBCONF_KDC_MODIFIES_KDB
/* Save the next challenge, if present. */
if (rb1_track_data->next_challenge.length == 8) {
tl_data.tl_data_type = KRB5_TL_RB1_CHALLENGE;
tl_data.tl_data_length = rb1_track_data->next_challenge.length;
tl_data.tl_data_contents = rb1_track_data->next_challenge.data;
if (retval = krb5_dbe_update_tl_data(kdc_context, &assoc, &tl_data))
com_err("krb5kdc", retval, "while updating RB-1 challenge tl_data");
if (retval = krb5_db_put_principal(kdc_context, &assoc, &npr))
com_err("krb5kdc", retval, "while storing RB-1 challenge tl_data");
}
#endif /* KRBCONF_KDC_MODIFIES_KDB */
cleanup:
if (rb1_track_data)
krb5_free_rb1_track_data(context, rb1_track_data);
if (esre)
krb5_free_enc_sam_response_enc(context, esre);
if (!retval) {
setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
}
return retval;
}
================================
end of kdc/preauth/pa_sam_cryptocard.c
================================
================================
kdc/preauth/pa_sam_digi_path.c
================================
/*
* kdc/preauth/pa_sam_digi_path.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_SAM Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include "pa_sam.h"
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_generate_digi_path(context, request, assoc, npr, sam_key, client_key,
mode, pa_data)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
krb5_keyblock sam_key;
krb5_keyblock client_key;
int mode;
krb5_pa_data * pa_data;
{
krb5_error_code retval;
krb5_sam_challenge sc;
krb5_predicted_sam_response psr;
krb5_data * scratch;
krb5_enc_data tmpdata;
krb5_keyblock session_key;
size_t enclen;
int i, j;
char inputblock[8];
char outputblock[8];
char response[9];
krb5_data predict_response;
memset(&sc, 0, sizeof(sc));
memset(&psr, 0, sizeof(psr));
memset(inputblock, 0, 8);
session_key.contents = 0;
sc.magic = KV5M_SAM_CHALLENGE;
sc.sam_type = PA_SAM_TYPE_DIGI_PATH;
#ifndef AS_REP_105_SAM_COMPAT
sc.sam_flags = 0;
#else /* !AS_REP_105_SAM_COMPAT */
sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
#endif /* !AS_REP_105_SAM_COMPAT */
sc.sam_type_name.data = "Digital Pathways Authentication";
sc.sam_type_name.length = strlen(sc.sam_type_name.data);
sc.sam_challenge_label.data = "Enter the following on your keypad";
sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
/* Generate a random challenge, take it mod 1000000 (six digits.) */
if (retval = krb5_c_make_random_key(kdc_context, ENCTYPE_DES_CBC_CRC,
&session_key)) {
com_err("krb5kdc", retval, "generating random challenge for preauth");
goto cleanup;
}
/* Sanity check */
if (session_key.length != 8) {
com_err("krb5kdc", retval = KRB5KDC_ERR_ETYPE_NOSUPP,
"keytype didn't match code expectations");
goto cleanup;
}
/* Now session_key has a key which we can pick bits out of. */
/* We need six decimal digits. Grab 6 bytes, div 2, mod 10 each. */
for (i = 0; i < 6; i++) {
inputblock[i] = '0' + ((session_key.contents[i] / 2) % 10);
}
/* Now we have inputblock containing the input to DES... */
sc.sam_challenge.data = inputblock;
sc.sam_challenge.length = 6;
/* Calculate the response (SAD) */
sam_key.enctype = ENCTYPE_DES_CBC_RAW;
{
krb5_data plain;
krb5_enc_data cipher;
plain.length = 8;
plain.data = inputblock;
/*
* XXX I know this is enough because of the fixed raw enctype.
* if it's not, the underlying code will return a reasonable
* error, which should never happen.
*/
cipher.ciphertext.length = 8;
cipher.ciphertext.data = outputblock;
if (retval = krb5_c_encrypt(kdc_context, &sam_key, /* XXX */ 0, 0,
&plain, &cipher)) {
com_err("krb5kdc", retval, "snk4 response generation failed");
goto cleanup;
}
}
/*
* Now outputblock is the raw bits of the response;
* convert it to display form.
*/
for (i = 0; i < 4; i++) {
char n[2];
n[0] = outputblock[i] & 0xf;
n[1] = (outputblock[i] >> 4) & 0xf;
for (j = 0; j < 2; j++) {
if (n[j] > 9)
n[j] = ((n[j] - 1) >> 2);
/*
* This is equivalent to:
* if (n[j] >= 0xa && n[j] <= 0xc) n[j] = 2;
* if (n[j] >= 0xd && n[j] <= 0xf) n[j] = 3;
*/
}
/* for v4, we keygen: *(i+(char*)&key1) = (n[1]<<4) | n[0]; */
/* for v5, we just generate a string */
response[2 * i + 0] = '0' + n[1];
response[2 * i + 1] = '0' + n[0];
}
/* And now, response has what we work with (the SAD). */
response[8] = 0;
predict_response.data = response;
predict_response.length = 8;
/* Convert the SAD into a key. */
psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
if (retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
&predict_response, 0 /* salt */,
&psr.sam_key)) {
goto cleanup;
}
#ifndef AS_REP_105_SAM_COMPAT
/* And XOR with the principal's long term key (in place). */
/* XXX There should be an abstraction for this ... */
{
krb5_octet *p = psr.sam_key.contents;
krb5_octet *q = client_key.contents;
for (i = 0; i < psr.sam_key.length; i++)
p[i] ^= q[i];
}
mit_des_fixup_key_parity(psr.sam_key.contents);
if (mit_des_is_weak_key(psr.sam_key.contents))
((krb5_octet *) psr.sam_key.contents)[7] ^= 0xf0;
#endif /* !AS_REP_105_SAM_COMPAT */
/*
* Now we have an encrypting key that the client will use for
* the sam response, and that the KDC should use for the AP_REP.
*/
if (retval = encode_krb5_predicted_sam_response(&psr, &scratch))
goto cleanup;
if (retval = krb5_c_encrypt_length(context, master_keyblock.enctype,
scratch->length, &enclen)) {
krb5_free_data(context, scratch);
goto cleanup;
}
if ((tmpdata.ciphertext.data = malloc(enclen)) == NULL) {
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
tmpdata.ciphertext.length = enclen;
sc.sam_track_id = tmpdata.ciphertext; /* So we know to free it. */
retval = krb5_c_encrypt(context, &master_keyblock,
/* XXX */ 0, 0, scratch, &tmpdata);
krb5_free_data(context, scratch);
if (retval)
goto cleanup;
sc.sam_response_prompt.data = "Enter the displayed response";
sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
sc.sam_pk_for_sad.length = 0;
sc.sam_nonce = 0;
/* Generate checksum */
sc.sam_cksum.length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES);
if ((sc.sam_cksum.contents = malloc(sc.sam_cksum.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
if (retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
sc.sam_challenge.data,
sc.sam_challenge.length,
psr.sam_key.contents, /* key */
psr.sam_key.length, /* key length */
&sc.sam_cksum)) {
goto cleanup;
}
if (retval = encode_krb5_sam_challenge(&sc, &scratch))
goto cleanup;
pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
pa_data->contents = malloc(scratch->length);
if (!pa_data->contents) {
pa_data->length = 0;
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
memcpy(pa_data->contents, scratch->data, scratch->length);
pa_data->length = scratch->length;
krb5_free_data(context, scratch);
cleanup:
krb5_free_predicted_sam_response_contents(context, &psr);
/* Don't use the abstraction to free sc; most of it is static. */
if (sc.sam_track_id.data);
krb5_xfree(sc.sam_track_id.data);
if (sc.sam_cksum.contents)
krb5_xfree(sc.sam_cksum.contents);
if (session_key.contents)
krb5_free_keyblock_contents(context, &session_key);
return retval;
}
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_verify_digi_path(context, request, assoc, npr, mode, sr, enc_tkt_reply,
as_key)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
int mode;
krb5_sam_response * sr;
krb5_enc_tkt_part * enc_tkt_reply;
krb5_keyblock * as_key;
{
krb5_error_code retval;
krb5_data scratch;
krb5_enc_data tmpdata;
krb5_predicted_sam_response * psr = 0;
krb5_enc_sam_response_enc * esre = 0;
krb5_timestamp timenow;
/* Sanity check */
if (sr->sam_type != PA_SAM_TYPE_DIGI_PATH) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "inconsistent sam_type (snk4 expected)");
goto cleanup;
}
scratch.length = sr->sam_track_id.length;
if (!scratch.length) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "snk4 track_id data not found");
goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
tmpdata.enctype = ENCTYPE_UNKNOWN;
tmpdata.ciphertext = sr->sam_track_id;
if (retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
&tmpdata, &scratch)) {
/* Record our retval, but let the client see a generic error. */
com_err("krb5kdc", retval, "decrypt track_id failed");
retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_xfree(scratch.data);
goto cleanup;
}
retval = decode_krb5_predicted_sam_response(&scratch, &psr);
krb5_xfree(scratch.data);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_predicted_sam_response failed");
goto cleanup;
}
scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
if (!scratch.length) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "Grail sam_enc_nonce_or_ts not found");
goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
/* Now use psr->sam_key to verify */
if (retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0, 0,
&sr->sam_enc_nonce_or_ts, &scratch)) {
com_err("krb5kdc", retval, "decrypt nonce_or_ts failed");
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
retval = decode_krb5_enc_sam_response_enc(&scratch, &esre);
krb5_xfree(scratch.data);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_enc_sam_response_enc failed");
goto cleanup;
}
if (esre->sam_timestamp != sr->sam_patimestamp) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
if (retval = krb5_timeofday(context, &timenow))
goto cleanup;
if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
retval = KRB5KRB_AP_ERR_SKEW;
goto cleanup;
}
/* Success! Make sure we use the sam_key for the AS_REP. */
krb5_copy_keyblock_contents(context, &psr->sam_key, as_key);
cleanup:
if (psr)
krb5_free_predicted_sam_response(context, psr);
if (esre)
krb5_free_enc_sam_response_enc(context, esre);
if (!retval) {
setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
}
return retval;
}
================================
end of kdc/preauth/pa_sam_digi_path.c
================================
================================
kdc/preauth/pa_sam_enigma.c
================================
/*
* kdc/preauth/pa_sam_enigma.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_SAM Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include "pa_sam.h"
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_generate_enigma(context, request, assoc, npr, sam_key, client_key,
mode, pa_data)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
krb5_keyblock sam_key;
krb5_keyblock client_key;
int mode;
krb5_pa_data * pa_data;
{
return KRB5KDC_ERR_PREAUTH_FAILED;
}
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_verify_enigma(context, request, assoc, npr, mode, sr, enc_tkt_reply, as_key)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
int mode;
krb5_sam_response * sr;
krb5_enc_tkt_part * enc_tkt_reply;
krb5_keyblock * as_key;
{
return KRB5KDC_ERR_PREAUTH_FAILED;
}
================================
end of kdc/preauth/pa_sam_enigma.c
================================
================================
kdc/preauth/pa_sam_grail.c
================================
/*
* kdc/preauth/pa_sam_grail.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_SAM Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include "pa_sam.h"
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_generate_grail(context, request, assoc, npr, sam_key, client_key,
mode, pa_data)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
krb5_keyblock sam_key;
krb5_keyblock client_key;
int mode;
krb5_pa_data * pa_data;
{
krb5_error_code retval;
krb5_sam_challenge sc;
krb5_predicted_sam_response psr;
krb5_data * scratch;
krb5_enc_data tmpdata;
size_t enclen;
memset(&sc, 0, sizeof(sc));
memset(&psr, 0, sizeof(psr));
sc.magic = KV5M_SAM_CHALLENGE;
sc.sam_type = PA_SAM_TYPE_GRAIL;
/*
* XXX Generally, you don't want to set use-sad-as-key. We set
* it on purpose just to illustrate it's use. The difference is
* that XOR w/ the longterm key doesn't happen.
*/
sc.sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
sc.sam_type_name.data = "Experimental System Authentication";
sc.sam_type_name.length = strlen(sc.sam_type_name.data);
sc.sam_challenge_label.data = "Experimental challenge label";
sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
sc.sam_challenge.data = "12345";
sc.sam_challenge.length = strlen(sc.sam_challenge.data);
psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
/* For GRAIL, we ignore the sam_key, and use our own fixed key. */
if (retval = krb5_c_string_to_key(context, ENCTYPE_DES_CBC_MD5,
&sc.sam_challenge, 0 /* salt */,
&psr.sam_key)) {
return retval;
}
if (retval = encode_krb5_predicted_sam_response(&psr, &scratch))
goto cleanup;
if (retval = krb5_c_encrypt_length(context, master_keyblock.enctype,
scratch->length, &enclen)) {
krb5_free_data(context, scratch);
goto cleanup;
}
if ((tmpdata.ciphertext.data = malloc(enclen)) == NULL) {
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
tmpdata.ciphertext.length = enclen;
sc.sam_track_id = tmpdata.ciphertext; /* So we know to free it. */
retval = krb5_c_encrypt(context, &master_keyblock,
/* XXX */ 0, 0, scratch, &tmpdata);
krb5_free_data(context, scratch);
if (retval)
goto cleanup;
sc.sam_response_prompt.data = "Response prompt";
sc.sam_response_prompt.length = strlen(sc.sam_response_prompt.data);
sc.sam_pk_for_sad.length = 0;
sc.sam_nonce = 0;
/* Generate checksum */
sc.sam_cksum.length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES);
if ((sc.sam_cksum.contents = malloc(sc.sam_cksum.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
if (retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
sc.sam_challenge.data,
sc.sam_challenge.length,
psr.sam_key.contents, /* key */
psr.sam_key.length, /* key length */
&sc.sam_cksum)) {
goto cleanup;
}
if (retval = encode_krb5_sam_challenge(&sc, &scratch))
goto cleanup;
pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
pa_data->contents = malloc(scratch->length);
if (!pa_data->contents) {
pa_data->length = 0;
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
memcpy(pa_data->contents, scratch->data, scratch->length);
pa_data->length = scratch->length;
krb5_free_data(context, scratch);
cleanup:
krb5_free_predicted_sam_response_contents(context, &psr);
/* Don't use the abstraction to free sc; most of it is static. */
if (sc.sam_track_id.data);
krb5_xfree(sc.sam_track_id.data);
if (sc.sam_cksum.contents)
krb5_xfree(sc.sam_cksum.contents);
return retval;
}
/*
* Any SAM that doesn't store persistent data (eg for synchronous modes)
* should be able to use this routine (just change the errors, and
* the type sanity-check) for verification, since all the verification
* data is stored in the predicted_sam_response.
*/
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_verify_grail(context, request, assoc, npr, mode, sr, enc_tkt_reply, as_key)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
int mode;
krb5_sam_response * sr;
krb5_enc_tkt_part * enc_tkt_reply;
krb5_keyblock * as_key;
{
krb5_error_code retval;
krb5_data scratch;
krb5_enc_data tmpdata;
krb5_predicted_sam_response * psr = 0;
krb5_enc_sam_response_enc * esre = 0;
krb5_timestamp timenow;
/* Sanity check */
if (sr->sam_type != PA_SAM_TYPE_GRAIL) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "inconsistent sam_type (Grail expected)");
goto cleanup;
}
scratch.length = sr->sam_track_id.length;
if (!scratch.length) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "Grail track_id data not found");
goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
tmpdata.enctype = ENCTYPE_UNKNOWN;
tmpdata.ciphertext = sr->sam_track_id;
if (retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
&tmpdata, &scratch)) {
/* Record our retval, but let the client see a generic error. */
com_err("krb5kdc", retval, "decrypt track_id failed");
retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_xfree(scratch.data);
goto cleanup;
}
retval = decode_krb5_predicted_sam_response(&scratch, &psr);
krb5_xfree(scratch.data);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_predicted_sam_response failed");
goto cleanup;
}
scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
if (!scratch.length) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "Grail sam_enc_nonce_or_ts not found");
goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
/* Now use psr->sam_key to verify */
if (retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0, 0,
&sr->sam_enc_nonce_or_ts, &scratch)) {
com_err("krb5kdc", retval, "decrypt nonce_or_ts failed");
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
retval = decode_krb5_enc_sam_response_enc(&scratch, &esre);
krb5_xfree(scratch.data);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_enc_sam_response_enc failed");
goto cleanup;
}
if (esre->sam_timestamp != sr->sam_patimestamp) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
if (retval = krb5_timeofday(context, &timenow))
goto cleanup;
if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
retval = KRB5KRB_AP_ERR_SKEW;
goto cleanup;
}
/* Success! Make sure we use the sam_key for the AS_REP. */
krb5_copy_keyblock_contents(context, &psr->sam_key, as_key);
cleanup:
if (psr)
krb5_free_predicted_sam_response(context, psr);
if (esre)
krb5_free_enc_sam_response_enc(context, esre);
if (!retval) {
setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
}
return retval;
}
================================
end of kdc/preauth/pa_sam_grail.c
================================
================================
kdc/preauth/pa_sam_securid.c
================================
/*
* kdc/preauth/pa_sam_securid.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_SAM Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include "pa_sam.h"
/* XXX This code does not work yet. */
/*
* XXX This code depends on the client contacting the same KDC with
* a response containing pa_data as it initially contacted when it got
* the KRB_ERROR message. This means this specifically will not work
* with any kind of DNS load balancing/round-robin/etc.
*
* SecurID functionality stolen from securid.c: (modified to "eliminate"
* global state problems)
*
* Author: Kenneth D. Renard
* Army Research Lab
* Date: 9 Oct 96
* Version: 1.0
*/
#if defined(SECURID) && defined(KRBCONF_KDC_MODIFIES_KDB)
#include <sdi_athd.h>
#include <sdi_defs.h>
#include <sdconf.h>
#include <sdacmvls.h>
/*
* XXX struct SD_CLIENT must not contain any pointers.
* Also, kind of poor that we only support IP addresses.
*/
typedef struct _krb5_securid_state {
krb5_int32 state; /* states */
krb5_timestamp tl_time; /* to avoid stale data */
char newpin[LENPRNST + 1]; /* for PIN verification */
struct SD_CLIENT sd_info; /* SecurID state info */
krb5_addrtype addrtype; /* For verifying state info */
krb5_int32 addr1; /* IPv4 address for state */
krb5_int32 addr2; /* Reserved for IPv6 */
krb5_int32 addr3; /* Reserved for IPv6 */
krb5_int32 addr4; /* Reserved for IPv6 */
} krb5_securid_state;
union config_record configure;
int need_to_creadcfg = 1;
int need_to_sd_init = 1;
#endif /* SECURID && KRBCONF_KDC_MODIFIES_KDB */
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_generate_securid(context, request, assoc, npr, sam_key, client_key,
mode, pa_data)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
krb5_keyblock sam_key;
krb5_keyblock client_key;
int mode;
krb5_pa_data * pa_data;
{
#if defined(SECURID) && defined(KRBCONF_KDC_MODIFIES_KDB)
krb5_error_code retval;
krb5_sam_challenge sc;
krb5_predicted_sam_response psr;
krb5_data * scratch;
krb5_enc_data tmpdata;
size_t enclen;
krb5_tl_data tl_data;
krb5_securid_state securid_state;
krb5_timestamp timenow;
memset(&sc, 0, sizeof(sc));
memset(&psr, 0, sizeof(psr));
sc.magic = KV5M_SAM_CHALLENGE;
sc.sam_type = PA_SAM_TYPE_SECURID;
sc.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
sc.sam_type_name.data = "SecurID Authentication";
sc.sam_type_name.length = strlen(sc.sam_type_name.data);
/* Generate prompt based on current state. */
tl_data.tl_data_type = KRB5_TL_SECURID_STATE;
if ((retval = krb5_dbe_lookup_tl_data(kdc_context, &assoc, &tl_data)) ||
(tl_data.tl_data_length == 0)) {
if (tl_data.tl_data_length != sizeof(securid_state)) {
com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
"corrupted tl_data!");
return retval;
}
memcpy(securid_state, tl_data.tl_data_contents, sizeof(securid_state));
/*
* Verify that the tl_data is not stale (from a user abort or
* *shudder* possibly a crash). We have to use at least a one
* minute window; a next PRN could take at least that long.
*/
if (retval = krb5_timeofday(context, &timenow))
return retval;
if ((timenow - securid_state.tl_time) > 90) {
memset(&securid_state, 0, sizeof(securid_state));
securid_state.state = SECURID_STATE_INITIAL;
}
/* XXX Get local address, compare against saved address. */
if (securid_state.state != SECURID_STATE_INITIAL) {
if (krb5_address_compare(context, addr1, addr2) == FALSE) {
securid_state.state = SECURID_STATE_INITIAL;
}
}
*/
switch (securid_state.state) {
case SECURID_STATE_INITIAL:
sc.sam_reponse_prompt.data = "SecurID Passcode";
break;
case SECURID_STATE_NEXT_CODE:
sc.sam_response_prompt.data = "Enter next Passcode";
break;
case SECURID_STATE_NEW_PIN:
sc.sam_challenge_label.data = "Select a new PIN";
sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
sc.sam_response_prompt.data = "Enter new PIN";
break;
case SECURID_STATE_NEW_PIN_AGAIN:
sc.sam_response_prompt.data = "Verify the new PIN";
break;
default:
com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
"bad state in securid track_data");
return retval;
} /* switch (securid_state) */
} else {
/* tl_data not found, this is a new user. */
sc.sam_reponse_prompt.data = "SecurID Passcode";
}
sc.sam_response_prompt.length = strlen(sc.sam_challenge_label.data);
psr.magic = KV5M_PREDICTED_SAM_RESPONSE;
/* Encrypt with longterm key (send-encrypted-sad). */
krb5_copy_keyblock_contents(context, &client_key, &psr.sam_key);
if (retval = encode_krb5_predicted_sam_response(&psr, &scratch))
goto cleanup;
if (retval = krb5_c_encrypt_length(context, master_keyblock.enctype,
scratch->length, &enclen)) {
krb5_free_data(context, scratch);
goto cleanup;
}
if ((tmpdata.ciphertext.data = malloc(enclen)) == NULL) {
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
tmpdata.ciphertext.length = enclen;
sc.sam_track_id = tmpdata.ciphertext; /* So we know to free it. */
retval = krb5_c_encrypt(context, &master_keyblock,
/* XXX */ 0, 0, scratch, &tmpdata);
krb5_free_data(context, scratch);
if (retval)
goto cleanup;
sc.sam_pk_for_sad.length = 0;
sc.sam_nonce = 0;
/* Generate checksum */
sc.sam_cksum.length = krb5_checksum_size(context, CKSUMTYPE_RSA_MD5_DES);
if ((sc.sam_cksum.contents = malloc(sc.sam_cksum.length)) == NULL) {
return ENOMEM;
}
if (retval = krb5_calculate_checksum(context, CKSUMTYPE_RSA_MD5_DES,
sc.sam_challenge.data,
sc.sam_challenge.length,
client_key.contents, /* key */
client_key.length, /* key length */
&sc.sam_cksum)) {
goto cleanup;
}
if (retval = encode_krb5_sam_challenge(&sc, &scratch))
goto cleanup;
pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
pa_data->contents = malloc(scratch->length);
if (!pa_data->contents) {
pa_data->length = 0;
krb5_free_data(context, scratch);
retval = ENOMEM;
goto cleanup;
}
memcpy(pa_data->contents, scratch->data, scratch->length);
pa_data->length = scratch->length;
krb5_free_data(context, scratch);
cleanup:
krb5_free_predicted_sam_response_contents(context, &psr);
/* Don't use the abstraction to free sc; most of it is static. */
if (sc.sam_track_id.data);
krb5_xfree(sc.sam_track_id.data);
if (sc.sam_cksum.contents)
krb5_xfree(sc.sam_cksum.contents);
return retval;
#else /* SECURID && KRBCONF_KDC_MODIFIES_KDB */
return KRB5KDC_ERR_PREAUTH_FAILED;
#endif /* SECURID && KRBCONF_KDC_MODIFIES_KDB */
}
/*
* The new PIN/next PRN functions force passing in the enc_tkt_reply.
* Because of this, we also have to keep per-user state somewhere,
* the obvious place is the tl_data. This generates it's own problems.
*/
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_verify_securid(context, request, assoc, npr, mode, sr, enc_tkt_reply,
as_key)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
int mode;
krb5_sam_response * sr;
krb5_enc_tkt_part * enc_tkt_reply;
krb5_keyblock * as_key;
{
#if defined(SECURID) && defined(KRBCONF_KDC_MODIFIES_KDB)
krb5_error_code retval, retval2;
krb5_data scratch;
krb5_enc_data tmpdata;
krb5_tl_data tl_data;
krb5_securid_state securid_state;
krb5_int32 old_state;
krb5_predicted_sam_response * psr = 0;
krb5_enc_sam_response_enc * esre = 0;
krb5_timestamp timenow;
struct SD_CLIENT sd_dat, *sd;
char passcode[LENPRNST + 1];
char * user = 0, cp;
memset(&securid_state, 0, sizeof(securid_state));
securid_state.state = old_state = SECURID_STATE_INITIAL;
/* Sanity check */
if (sr->sam_type != PA_SAM_TYPE_SECURID) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "inconsistent sam_type (SecurID expected)");
goto cleanup;
}
scratch.length = sr->sam_track_id.length;
if (!scratch.length) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "securid track_id data not found");
goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
tmpdata.enctype = ENCTYPE_UNKNOWN;
tmpdata.ciphertext = sr->sam_track_id;
if (retval = krb5_c_decrypt(context, &master_keyblock, /* XXX */ 0, 0,
&tmpdata, &scratch)) {
/* Record our retval, but let the client see a generic error. */
com_err("krb5kdc", retval, "decrypt track_id failed");
retval = KRB5KDC_ERR_PREAUTH_FAILED;
krb5_xfree(scratch.data);
goto cleanup;
}
retval = decode_krb5_predicted_sam_response(&scratch, &psr);
krb5_xfree(scratch.data);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_predicted_sam_response failed");
goto cleanup;
}
scratch.length = sr->sam_enc_nonce_or_ts.ciphertext.length;
if (!scratch.length) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
com_err("krb5kdc", retval, "Grail sam_enc_nonce_or_ts not found");
goto cleanup;
}
if ((scratch.data = malloc(scratch.length)) == NULL) {
retval = ENOMEM;
goto cleanup;
}
/* Now use psr->sam_key to verify */
if (retval = krb5_c_decrypt(context, &psr->sam_key, /* XXX */ 0, 0,
&sr->sam_enc_nonce_or_ts, &scratch)) {
com_err("krb5kdc", retval, "decrypt nonce_or_ts failed");
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
retval = decode_krb5_enc_sam_response_enc(&scratch, &esre);
krb5_xfree(scratch.data);
if (retval) {
com_err("krb5kdc", retval, "decode_krb5_enc_sam_response_enc failed");
goto cleanup;
}
if (esre->sam_timestamp != sr->sam_patimestamp) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
if (retval = krb5_timeofday(context, &timenow))
goto cleanup;
if (labs(timenow - sr->sam_patimestamp) > context->clockskew) {
retval = KRB5KRB_AP_ERR_SKEW;
goto cleanup;
}
/* Success! Unlike most SAM types, we don't set the as_key. */
/* Now verify the Passcode. */
if (need_to_creadcfg) {
creadcfg();
need_to_creadcfg = 0;
}
memset(&sd_dat, 0, sizeof(sd_dat));
if (retval = krb5_unparse_name(context, request->client->princ, &user)) {
com_err("krb5kdc", retval, "Cannot unparse name for SecurID check");
goto cleanup;
}
if (cp = strchr(user, '@'))
*cp = '\0';
/* What did the user return to us? */
tl_data.tl_data_type = KRB5_TL_SECURID_STATE;
if ((retval = krb5_dbe_lookup_tl_data(kdc_context, &assoc, &tl_data)) ||
(tl_data.tl_data_length == 0)) {
if (tl_data.tl_data_length != sizeof(securid_state)) {
com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
"corrupted tl_data!");
goto cleanup;
}
memcpy(securid_state, tl_data.tl_data_contents, sizeof(securid_state));
old_state = securid_state.state;
/*
* Verify that the tl_data is not stale (see comment in generate).
* XXX By setting old_state we might introduce a condition where
* user cannot login (if there is a problem with updating the db).
*/
if ((timenow - securid_state.tl_time) > 90) {
memset(&securid_state, 0, sizeof(securid_state));
securid_state.state = old_state = SECURID_STATE_INITIAL;
}
}
switch (securid_state.state) {
case SECURID_STATE_INITIAL:
if (esre->sam_passcode.length > LENPRNST) {
/* User entered too much data, keep same state. */
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
memcpy(&passcode, esre->sam_passcode.data, esre->sam_passcode.length);
passcode[esre->sam_passcode.length] = '\0';
if (need_to_sd_init) {
if (sd_init(&sd_dat)) {
com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
"Cannot initialize SecurID communications");
goto cleanup;
}
need_to_sd_init = 0;
}
retval = sd_check(passcode, user, &sd_dat);
switch (retval) {
case ACM_OK:
setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
retval = 0;
break;
case ACM_ACCESS_DENIED:
retval = KRB5_PREAUTH_FAILED;
break;
case ACM_NEXT_CODE_REQUIRED:
/*
* Trickery. We set retval = 0 so the KDC doesn't return
* KRB5KDC_ERR_PREAUTH_FAILED to client. But we don't
* set the HW_AUTH flag. This causes the KDC to generate
* another error message "Preauth required". This should
* make the client prompt the user again instead of reporting
* a failure.
*/
retval = 0;
securid_state.state = SECURID_STATE_NEXT_CODE;
securid_state.sd_info = sd_dat;
break;
case ACM_NEW_PIN_REQUIRED:
/*
* Same trickery as above. Unfortunately, GUI clients won't
* display the PIN prompt twice in one window,
* like the new password does. Unfortunately, we can't
* "simply" send back a key expired message as that is
* handled by the client (we won't get the message back,
* also, the prompt will be wrong.)
*/
if (sd_dat.user_selectable == CANNOT_CHOOSE_PIN) {
com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
"SecurID new PIN needed but not user-selectable");
goto cleanup;
}
retval = 0;
securid_state.state = SECURID_STATE_NEW_PIN;
securid_state.sd_info = sd_dat;
break;
default:
/* Punt */
com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
"SecurID sd_check() returned %d", retval);
retval = KRB5KDC_ERR_PREAUTH_FAILED;
need_to_creadcfg = 1;
need_to_sd_init = 1;
goto cleanup;
} /* switch (retval) [from sd_check] */
break;
case SECURID_STATE_NEXT_CODE:
sd = &securid_state.sd_info;
retval = sd_next(passcode, sd);
if (retval == ACM_OK) {
setflag(enc_tkt_reply->flags, TKT_FLG_HW_AUTH);
setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);
retval = 0;
} else {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
}
securid_state.state = SECURID_STATE_INITIAL;
break;
case SECURID_STATE_NEW_PIN:
/*
* Store the user supplied PIN for verification.
* Boundary checking has already been done.
*/
retval = 0;
strcpy(securid_state.newpin, passcode);
securid_state.state = SECURID_STATE_NEW_PIN_AGAIN;
break;
case SECURID_STATE_NEW_PIN_AGAIN:
sd = &securid_state.sd_info;
/* Verify 2nd entry with the previous. */
if (strncmp(passcode, securid_state.newpin, LENPRNST) != 0) {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
} else {
retval = sd_pin(passcode, '\0', sd);
if (retval == ACM_NEW_PIN_ACCEPTED) {
retval = 0;
} else {
retval = KRB5KDC_ERR_PREAUTH_FAILED;
}
}
securid_state.state = SECURID_STATE_INITIAL;
break;
default:
com_err("krb5kdc", retval = KRB5KDC_ERR_PREAUTH_FAILED,
"bad state in securid tl_data");
securid_state.state = SECURID_STATE_INITIAL;
break;
} /* switch (securid_state.state) */
/*
* Update state information if it has changed.
* Unlike CRYPTOCard, we might need to fail if we can't update.
* XXX This might be bad when the databases sync properly.
*/
if (old_state != securid_state.state) {
if (retval2 = krb5_timeofday(context, &securid_state.tl_time)) {
com_err("krb5kdc", retval2, "updating tl_time");
if (securid_state.state != SECURID_STATE_INITIAL)
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
tl_data.tl_data_length = sizeof(securid_state);
tl_data.tl_data_contents = &securid_state;
/* XXX add our IP address/unique info */
if (retval2 = krb5_dbe_update_tl_data(kdc_context, &assoc, &tl_data)) {
com_err("krb5kdc", retval2, "while updating SecurID tl_data");
if (securid_state.state != SECURID_STATE_INITIAL)
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
if (retval2 = krb5_db_put_principal(kdc_context, &assoc, &npr)) {
com_err("krb5kdc", retval2, "while storing SecurID tl_data");
if (securid_state.state != SECURID_STATE_INITIAL)
retval = KRB5KDC_ERR_PREAUTH_FAILED;
goto cleanup;
}
}
cleanup:
if (psr)
krb5_free_predicted_sam_response(context, psr);
if (esre)
krb5_free_enc_sam_response_enc(context, esre);
if (user)
krb5_xfree(user);
return retval;
#else /* SECURID && KRBCONF_KDC_MODIFIES_KDB */
return KRB5KDC_ERR_PREAUTH_FAILED;
#endif /* SECURID && KRBCONF_KDC_MODIFIES_KDB */
}
================================
end of kdc/preauth/pa_sam_securid.c
================================
================================
kdc/preauth/pa_sam_skey.c
================================
/*
* kdc/preauth/pa_sam_skey.c
*
* Copyright 1995 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* PA_SAM Preauthentication routine for the KDC.
*/
#include "k5-int.h"
#include "kdc_util.h"
#include "extern.h"
#include "pa_sam.h"
#ifdef OPIE
#include <opie.h>
#endif /* OPIE */
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_generate_skey(context, request, assoc, npr, sam_key, client_key,
mode, pa_data)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
krb5_keyblock sam_key;
krb5_keyblock client_key;
int mode;
krb5_pa_data * pa_data;
{
#ifdef OPIE
#else /* OPIE */
return KRB5KDC_ERR_PREAUTH_FAILED;
#endif /* OPIE */
}
KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
sam_verify_skey(context, request, assoc, npr, mode, sr, enc_tkt_reply, as_key)
krb5_context context;
krb5_kdc_req * request;
krb5_db_entry assoc;
int npr;
int mode;
krb5_sam_response * sr;
krb5_enc_tkt_part * enc_tkt_reply;
krb5_keyblock * as_key;
{
#ifdef OPIE
#else /* OPIE */
return KRB5KDC_ERR_PREAUTH_FAILED;
#endif /* OPIE */
}
================================
end of kdc/preauth/pa_sam_skey.c
================================
>Audit-Trail:
>Unformatted: