WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Individual source code files are copyright MIT, Cygnus Support,
-OpenVision, Oracle, Sun Soft, FundsXpress, and others.
+Novell, OpenVision, Oracle, Redhat, Sun Soft, FundsXpress, and others.
Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
and Zephyr are trademarks of the Massachusetts Institute of Technology
in respect of any properties, including, but not limited to, correctness
and fitness for purpose.
+---Code, including the preauthentication plugins contains the following copyright:
+ Copyright (c) 2006 Red Hat, Inc.
+ Portions copyright (c) 2006 Massachusetts Institute of Technology
+ All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ Neither the name of Red Hat, Inc., nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
--- The implementations of GSSAPI mechglue in GSSAPI-SPNEGO in
src/lib/gssapi, including the following files:
@vskip 12pt
@end iftex
+Portions contributed by Redhat contain the following copyright:
+
+@quotation
+ Copyright (c) 2006 Red Hat, Inc.
+Portions copyright (c) 2006 Massachusetts Institute of Technology
+ All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ Neither the name of Red Hat, Inc., nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+@end quotation
+
@value{PRODUCT} includes documentation and software developed at the
University of California at Berkeley, which includes this copyright
notice:
thisconfigdir=.
myfulldir=.
mydir=.
-# Don't build sample by default: plugins/locate/python
+# Don't build sample by default: plugins/locate/python plugins/preauth/wpse plugins/preauth/cksum_body
SUBDIRS=util include lib @krb524@ kdc kadmin @ldap_plugin_dir@ slave clients \
plugins/kdb/db2 \
appl tests \
instead. This field is ignored when its value is incompatible with
the session key type.
+.IP preferred_preauth_types
+This allows you to set the preferred preauthentication types which the
+client will attempt before others which may be advertised by a KDC. The
+default value for this setting is "17, 16, 15, 14", which forces libkrb5
+to attempt to use PKINIT if it is supported.
+
.IP ccache_type
User this parameter on systems which are DCE clients, to specify the
type of cache to be created by kinit, or when forwarded tickets are
"/etc/srvtab".
.IP krb4_config
-Specifies the location of hte Kerberos V4 configuration file. Default
+Specifies the location of the Kerberos V4 configuration file. Default
is "/etc/krb.conf".
.IP krb4_realms
KRB5_INCDIR = @includedir@
MODULE_DIR = @libdir@/krb5/plugins
KRB5_DB_MODULE_DIR = $(MODULE_DIR)/kdb
+KRB5_PA_MODULE_DIR = $(MODULE_DIR)/preauth
KRB5_LIBKRB5_MODULE_DIR = $(MODULE_DIR)/libkrb5
KRB5_INCSUBDIRS = \
$(KRB5_INCDIR)/krb5 \
fi
AC_SUBST(ldap_plugin_dir)
-AC_CONFIG_SUBDIRS(lib/apputils plugins/kdb/db2 appl tests)
+AC_CONFIG_SUBDIRS(lib/apputils plugins/kdb/db2 plugins/preauth/wpse plugins/preauth/cksum_body appl tests)
dnl
if false; then
AC_CHECK_HEADERS(Python.h python2.3/Python.h)
#endif /* KRB5_LIBOS_PROTO__ */
+#include <krb5/preauth_plugin.h>
+typedef struct _krb5_preauth_context krb5_preauth_context;
+
/* new encryption provider api */
struct krb5_enc_provider {
krb5_error_code krb5_do_preauth
-(krb5_context, krb5_kdc_req *,
- krb5_pa_data **, krb5_pa_data ***,
+(krb5_context, krb5_preauth_context **, krb5_kdc_req *, krb5_data *,
+ krb5_data *, krb5_pa_data **, krb5_pa_data ***,
krb5_data *salt, krb5_data *s2kparams,
krb5_enctype *,
krb5_keyblock *,
krb5_prompter_fct, void *,
krb5_gic_get_as_key_fct, void *);
+void KRB5_CALLCONV krb5_init_preauth_context
+ (krb5_context, krb5_preauth_context **);
+void KRB5_CALLCONV krb5_clear_preauth_context_use_counts
+ (krb5_context, krb5_preauth_context *);
+void KRB5_CALLCONV krb5_preauth_prepare_request
+ (krb5_context, krb5_preauth_context **,
+ krb5_get_init_creds_opt *, krb5_kdc_req *);
+void KRB5_CALLCONV krb5_free_preauth_context
+ (krb5_context, krb5_preauth_context *);
void KRB5_CALLCONV krb5_free_sam_challenge
(krb5_context, krb5_sam_challenge * );
struct krb5plugin_service_locate_ftable *vtbl;
void (**locate_fptrs)(void);
+ /* preauth module stuff */
+ struct plugin_dir_handle preauth_plugins;
+
/* error detail info */
struct errinfo err;
};
#define KRB5_TL_SECURID_STATE 0x0006
#define KRB5_TL_DB_ARGS 0x7fff
#endif /* SECURID */
+#define KRB5_TL_USER_CERTIFICATE 0x0007
/*
* Determines the number of failed KDC requests before DISALLOW_ALL_TIX is set
#define KRB5_PADATA_ETYPE_INFO 11 /* Etype info for preauth */
#define KRB5_PADATA_SAM_CHALLENGE 12 /* draft challenge system */
#define KRB5_PADATA_SAM_RESPONSE 13 /* draft challenge system response */
-#define KRB5_PADATA_PK_AS_REQ 14 /* PKINIT */
-#define KRB5_PADATA_PK_AS_REP 15 /* PKINIT */
+#define KRB5_PADATA_PK_AS_REQ_OLD 14 /* PKINIT */
+#define KRB5_PADATA_PK_AS_REP_OLD 15 /* PKINIT */
+#define KRB5_PADATA_PK_AS_REQ 16 /* PKINIT */
+#define KRB5_PADATA_PK_AS_REP 17 /* PKINIT */
#define KRB5_PADATA_ETYPE_INFO2 19
+#define KRB5_PADATA_USE_SPECIFIED_KVNO 20
+#define KRB5_PADATA_SAM_REDIRECT 21
+#define KRB5_PADATA_GET_FROM_TYPED_DATA 22
#define KRB5_PADATA_REFERRAL 25 /* draft referral system */
#define KRB5_PADATA_SAM_CHALLENGE_2 30 /* draft challenge system, updated */
#define KRB5_PADATA_SAM_RESPONSE_2 31 /* draft challenge system, updated */
--- /dev/null
+/*
+ * <krb5/preauth_plugin.h>
+ *
+ * Copyright (c) 2006 Red Hat, Inc.
+ * Portions copyright (c) 2006 Massachusetts Institute of Technology
+ * All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Red Hat, Inc., nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Preauthentication plugin definitions for Kerberos 5.
+ */
+
+#ifndef KRB5_PREAUTH_PLUGIN_H_INCLUDED
+#define KRB5_PREAUTH_PLUGIN_H_INCLUDED
+#include <krb5/krb5.h>
+
+/*
+ * While arguments of these types are passed-in, for the most part a preauth
+ * module can treat them as opaque. If we need keying data, we can ask for
+ * it directly.
+ */
+struct _krb5_db_entry_new;
+struct _krb5_key_data;
+
+/*
+ * Preauth mechanism property flags, unified from previous definitions in the
+ * KDC and libkrb5 sources.
+ */
+
+/* Provides a real answer which we can send back to the KDC (client-only). The
+ * client assumes that one real answer will be enough. */
+#define PA_REAL 0x00000001
+
+/* Doesn't provide a real answer, but must be given a chance to run before any
+ * REAL mechanism callbacks (client-only). */
+#define PA_INFO 0x00000002
+
+/* Causes the KDC to include this mechanism in a list of supported preauth
+ * types if the user's DB entry flags the user as requiring hardware-based
+ * preauthentication (server-only). */
+#define PA_HARDWARE 0x00000004
+
+/* Causes the KDC to include this mechanism in a list of supported preauth
+ * types if the user's DB entry flags the user as requiring preauthentication,
+ * and to fail preauthentication if we can't verify the client data. The
+ * flipside of PA_SUFFICIENT (server-only). */
+#define PA_REQUIRED 0x00000008
+
+/* Causes the KDC to include this mechanism in a list of supported preauth
+ * types if the user's DB entry flags the user as requiring preauthentication,
+ * and to mark preauthentication as successful if we can verify the client
+ * data. The flipside of PA_REQUIRED (server-only). */
+#define PA_SUFFICIENT 0x00000010
+
+/* Marks this preauthentication mechanism as one which changes the key which is
+ * used for encrypting the response to the client. Modules which have this
+ * flag have their server_return_proc called before modules which do not, and
+ * are passed over if a previously-called module has modified the encrypting
+ * key (server-only). */
+#define PA_REPLACES_KEY 0x00000020
+
+/* Causes the KDC to check with this preauthentication module even if the
+ * client has no entry in the realm database. If the module returns a success
+ * code, continue processing and assume that its return_padata callback will
+ * supply us with a key for encrypting the AS reply (server-only). */
+/* #define PA_VIRTUAL (0x00000040 | PA_REPLACES_KEY) */
+
+/* Not really a padata type, so don't include it in any list of preauth types
+ * which gets sent over the wire. */
+#define PA_PSEUDO 0x00000080
+
+/*
+ * A server module's callback functions are allowed to request specific types
+ * of information about the given client or server record or request, even
+ * though the database records themselves are opaque to the module.
+ */
+enum krb5plugin_preauth_entry_request_type {
+ /* The returned krb5_data item holds a DER-encoded X.509 certificate. */
+ krb5plugin_preauth_entry_request_certificate = 1,
+ /* The returned krb5_data_item holds a krb5_deltat. */
+ krb5plugin_preauth_entry_max_time_skew = 2,
+ /* The returned krb5_data_item holds an array of krb5_keyblock structures,
+ * terminated by an entry with key type = 0.
+ * Each keyblock should have its contents freed in turn, and then the data
+ * item itself should be freed. */
+ krb5plugin_preauth_keys = 3,
+ /* The returned krb5_data_item holds the request structure, re-encoded
+ * using DER. Unless the client implementation is the same as the server
+ * implementation, there's a good chance that the result will not match
+ * what the client sent, so don't go creating any fatal errors if it
+ * doesn't match up. */
+ krb5plugin_preauth_request_body = 4,
+};
+typedef krb5_error_code
+(*preauth_get_entry_data_proc)(krb5_context,
+ krb5_kdc_req *,
+ struct _krb5_db_entry_new *,
+ enum krb5plugin_preauth_entry_request_type,
+ krb5_data **);
+
+/*
+ * A callback which will obtain the user's long-term AS key by prompting the
+ * user for the password, then salting it properly, and so on. For the moment,
+ * it's identical to the get_as_key callback used inside of libkrb5, but we
+ * define a new typedef here instead of making the existing one public to
+ * isolate ourselves from potential future changes.
+ */
+typedef krb5_error_code
+(*preauth_get_as_key_proc)(krb5_context,
+ krb5_principal,
+ krb5_enctype,
+ krb5_prompter_fct,
+ void *prompter_data,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ krb5_keyblock *as_key,
+ void *gak_data);
+
+/*
+ * The function table / structure which a preauth module must export as
+ * "preauthentication0". NOTE: replace "0" with "1" for the type and variable
+ * names if this gets picked up by upstream. If the interfaces work correctly,
+ * future versions of the table will add either more callbacks or more
+ * arguments to callbacks, and in both cases we'll be able to wrap the v0
+ * functions.
+ */
+typedef struct krb5plugin_preauth_ftable_v0 {
+ /* Not-usually-visible name. */
+ char *name;
+
+ /* Pointer to zero-terminated lists of pa_types which this module can
+ * provide services for. */
+ krb5_preauthtype *client_pa_type_list;
+ krb5_preauthtype *server_pa_type_list;
+
+ /* Pointer to zero-terminated list of enc_types which this module claims
+ * to add support for. */
+ krb5_enctype *client_enctype_list;
+
+ /* Per-module initialization/cleanup. The init function is called
+ * by libkrb5 when the module is loaded, and the fini function is
+ * called before the module is unloaded. Both are optional and
+ * may be called multiple times in case the module is used in
+ * multiple contexts.*/
+ krb5_error_code (*client_init)(krb5_context, krb5_preauthtype, void **);
+ void (*client_fini)(krb5_context, krb5_preauthtype, void *);
+ /* A callback which returns flags indicating if the module is a "real" or
+ * an "info" mechanism, and so on. This function is called for each entry
+ * in the client_pa_type_list. */
+ int (*client_flags)(krb5_context, krb5_preauthtype);
+ /* Clean up a client context. Can be NULL. */
+ void (*client_cleanup)(krb5_context context, void *module_context,
+ void **request_context);
+ /* Client function which processes server-supplied data in pa_data,
+ * returns created data in out_pa_data, storing any of its own state in
+ * client_context if data for the associated preauthentication type is
+ * needed. It is also called after the AS-REP is received if the AS-REP
+ * includes preauthentication data of the associated type.
+ * NOTE! the encoded_previous_request will be NULL the first time this
+ * function is called, because it is expected to only ever contain the data
+ * obtained from a previous call to this function. */
+ krb5_error_code (*client_process)(krb5_context context,
+ void *module_context,
+ void **request_context,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *pa_data,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ krb5_data *salt, krb5_data *s2kparams,
+ void *gak_data,
+ krb5_keyblock *as_key,
+ krb5_pa_data **out_pa_data);
+ /* Client function which can attempt to use e-data in the error response to
+ * try to recover from the given error. If this function is not NULL, and
+ * it stores data in out_pa_data which is different data from the contents
+ * of in_pa_data, then the client library will retransmit the request. */
+ krb5_error_code (*client_tryagain)(krb5_context context,
+ void *module_context,
+ void **request_context,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_error *error,
+ krb5_pa_data *in_pa_data,
+ krb5_pa_data **out_pa_data);
+
+ /* Per-module initialization/cleanup. The init function is called by the
+ * KDC when the module is loaded, and the fini function is called before
+ * the module is unloaded. Both are optional. */
+ krb5_error_code (*server_init_proc)(krb5_context, krb5_preauthtype,
+ void **);
+ void (*server_fini_proc)(krb5_context, krb5_preauthtype, void *);
+ /* Return the flags which the KDC should use for this module. This is a
+ * callback instead of a static value because the module may or may not
+ * wish to count itself as a hardware preauthentication module (in other
+ * words, the flags may be affected by the configuration, for example if a
+ * site administrator can force a particular preauthentication type to be
+ * supported using only hardware). This function is called for each entry
+ * entry in the server_pa_type_list. */
+ int (*server_flags_proc)(krb5_context, krb5_preauthtype);
+ /* Get preauthentication data to send to the client as part of the "you
+ * need to use preauthentication" error. The module doesn't need to
+ * actually provide data if the protocol doesn't require it, but it should
+ * return either zero or non-zero to control whether its padata type is
+ * included in the list which is sent back to the client. Is not allowed
+ * to create a context because we have no guarantee that the client will
+ * ever call again (or that it will hit this server if it does), in which
+ * case a context might otherwise hang around forever. */
+ krb5_error_code (*server_edata_proc)(krb5_context, krb5_kdc_req *request,
+ struct _krb5_db_entry_new *client,
+ struct _krb5_db_entry_new *server,
+ preauth_get_entry_data_proc,
+ void *pa_module_context,
+ krb5_pa_data *data);
+ /* Verify preauthentication data sent by the client, setting the
+ * TKT_FLG_PRE_AUTH or TKT_FLG_HW_AUTH flag in the enc_tkt_reply's "flags"
+ * field as appropriate, and returning nonzero on failure. Can create
+ * context data for consumption by the return_proc or freepa_proc below. */
+ krb5_error_code (*server_verify_proc)(krb5_context,
+ struct _krb5_db_entry_new *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_pa_data *data,
+ preauth_get_entry_data_proc,
+ void *pa_module_context,
+ void **pa_request_context);
+ /* Generate preauthentication response data to send to the client as part
+ * of the AS-REP. If it needs to override the key which is used to encrypt
+ * the response, it can do so. The module is expected (but not required,
+ * if a freepa_proc is also provided) to free any context data it saved in
+ * "request_pa_context". */
+ krb5_error_code (*server_return_proc)(krb5_context, krb5_pa_data * padata,
+ struct _krb5_db_entry_new *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_kdc_rep *reply,
+ struct _krb5_key_data *client_keys,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc,
+ void *pa_module_context,
+ void **pa_request_context);
+ /* Free up the server-side per-request context, in cases where
+ * server_return_proc() didn't or for whatever reason was not called. Can
+ * be NULL. */
+ krb5_error_code (*server_freepa_reqcontext_proc)(krb5_context,
+ void *pa_module_context,
+ void **request_pa_context);
+} krb5plugin_preauth_ftable_v0;
+
+#endif /* KRB5_PREAUTH_PLUGIN_H_INCLUDED */
KDB5_LIB_DEPS=$(DL_LIB) $(THREAD_LINKOPTS)
PROG_RPATH=$(KRB5_LIBDIR)
FAKEKA=@FAKEKA@
-DEFS=
+DEFS=-DLIBDIR=\"$(KRB5_LIBDIR)\"
all:: krb5kdc rtest $(FAKEKA)
* pointer.
*/
if (!(retval = setup_server_realm(as_req->server))) {
- retval = process_as_req(as_req, from, response);
+ retval = process_as_req(as_req, pkt, from, response);
}
krb5_free_kdc_req(kdc_context, as_req);
}
/*ARGSUSED*/
krb5_error_code
-process_as_req(krb5_kdc_req *request, const krb5_fulladdr *from,
- krb5_data **response)
+process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
+ const krb5_fulladdr *from, krb5_data **response)
{
krb5_db_entry client, server;
krb5_kdc_rep reply;
char ktypestr[128];
char rep_etypestr[128];
char fromstringbuf[70];
+ void *pa_context = NULL;
ticket_reply.enc_part.ciphertext.data = 0;
e_data.data = 0;
* 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, req_pkt, request,
+ &enc_tkt_reply, &pa_context);
if (errcode) {
#ifdef KRBCONF_KDC_MODIFIES_KDB
/*
reply_encpart.caddrs = enc_tkt_reply.caddrs;
/* Fetch the padata info to be returned */
- errcode = return_padata(kdc_context, &client, request, &reply, client_key,
- &encrypting_key);
+ errcode = return_padata(kdc_context, &client, req_pkt, request,
+ &reply, client_key, &encrypting_key, &pa_context);
if (errcode) {
status = "KDC_RETURN_PADATA";
goto errout;
#endif /* KRBCONF_KDC_MODIFIES_KDB */
errout:
+ if (pa_context)
+ free_padata_context(kdc_context, &pa_context);
+
if (status) {
char * emsg = 0;
if (errcode)
#include <syslog.h>
#include <assert.h>
+#include "../include/krb5/preauth_plugin.h"
+
+#if TARGET_OS_MAC
+static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
+#else
+static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
+#endif
/* XXX This is ugly and should be in a header file somewhere */
#ifndef KRB5INT_DES_TYPES_DEFINED
typedef krb5_error_code (*verify_proc)
(krb5_context, krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
+ krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context);
typedef krb5_error_code (*edata_proc)
(krb5_context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
krb5_pa_data *data);
typedef krb5_error_code (*return_proc)
(krb5_context, krb5_pa_data * padata,
krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa);
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context);
+
+typedef krb5_error_code (*freepa_proc)
+ (krb5_context, void *pa_module_context, void **pa_request_context);
+
+typedef krb5_error_code (*init_proc)
+ (krb5_context, krb5_preauthtype, void **);
+typedef void (*fini_proc)
+ (krb5_context, krb5_preauthtype, void *);
typedef struct _krb5_preauth_systems {
- char * name;
+ const char *name;
int type;
int flags;
+ void *pa_sys_context;
+ init_proc init;
+ fini_proc fini;
edata_proc get_edata;
verify_proc verify_padata;
return_proc return_padata;
+ freepa_proc free_pa_request_context;
} krb5_preauth_systems;
static krb5_error_code verify_enc_timestamp
(krb5_context, krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
+ krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context);
static krb5_error_code get_etype_info
(krb5_context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
krb5_pa_data *data);
static krb5_error_code
get_etype_info2(krb5_context context, krb5_kdc_req *request,
- krb5_db_entry *client, krb5_db_entry *server,
- krb5_pa_data *pa_data);
+ krb5_db_entry *client, krb5_db_entry *server,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ krb5_pa_data *pa_data);
static krb5_error_code
etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
krb5_db_entry *client,
static krb5_error_code
return_etype_info(krb5_context, krb5_pa_data * padata,
krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa);
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context);
static krb5_error_code
return_etype_info2(krb5_context, krb5_pa_data * padata,
krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa);
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context);
static krb5_error_code return_pw_salt
(krb5_context, krb5_pa_data * padata,
krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa);
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context);
/* SAM preauth support */
static krb5_error_code verify_sam_response
(krb5_context, krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data);
+ krb5_enc_tkt_part * enc_tkt_reply, krb5_pa_data *data,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context);
static krb5_error_code get_sam_edata
(krb5_context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
krb5_pa_data *data);
static krb5_error_code return_sam_data
(krb5_context, krb5_pa_data * padata,
krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa);
-/*
- * 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
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context);
-static krb5_preauth_systems preauth_systems[] = {
+static krb5_preauth_systems static_preauth_systems[] = {
{
"timestamp",
KRB5_PADATA_ENC_TIMESTAMP,
0,
+ NULL,
+ NULL,
+ NULL,
0,
verify_enc_timestamp,
0
"etype-info",
KRB5_PADATA_ETYPE_INFO,
0,
+ NULL,
+ NULL,
+ NULL,
get_etype_info,
0,
return_etype_info
"etype-info2",
KRB5_PADATA_ETYPE_INFO2,
0,
+ NULL,
+ NULL,
+ NULL,
get_etype_info2,
0,
return_etype_info2
"pw-salt",
KRB5_PADATA_PW_SALT,
PA_PSEUDO, /* Don't include this in the error list */
+ NULL,
+ NULL,
+ NULL,
0,
0,
return_pw_salt
"sam-response",
KRB5_PADATA_SAM_RESPONSE,
0,
+ NULL,
+ NULL,
+ NULL,
0,
verify_sam_response,
return_sam_data
"sam-challenge",
KRB5_PADATA_SAM_CHALLENGE,
PA_HARDWARE, /* causes get_preauth_hint_list to use this */
+ NULL,
+ NULL,
+ NULL,
get_sam_edata,
0,
0
{ "[end]", -1,}
};
-#define MAX_PREAUTH_SYSTEMS (sizeof(preauth_systems)/sizeof(preauth_systems[0]))
+static krb5_preauth_systems *preauth_systems;
+static int n_preauth_systems;
+static struct plugin_dir_handle preauth_plugins;
+
+krb5_error_code
+load_preauth_plugins(krb5_context context)
+{
+ struct errinfo err;
+ void **preauth_plugins_ftables;
+ struct krb5plugin_preauth_ftable_v0 *ftable;
+ int module_count, i, j, k;
+ krb5_preauthtype pa_type;
+ void *pa_sys_context;
+ init_proc server_init_proc;
+
+ /* Attempt to load all of the preauth plugins we can find. */
+ PLUGIN_DIR_INIT(&preauth_plugins);
+ if (PLUGIN_DIR_OPEN(&preauth_plugins) == 0) {
+ if (krb5int_open_plugin_dirs(objdirs, NULL,
+ &preauth_plugins, &err) != 0) {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ }
+
+ /* Get the method tables provided by the loaded plugins. */
+ memset(&err, 0, sizeof(err));
+ preauth_plugins_ftables = NULL;
+ if (krb5int_get_plugin_dir_data(&preauth_plugins, "preauthentication0",
+ &preauth_plugins_ftables, &err) != 0) {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ /* Count the valid modules. */
+ module_count = sizeof(static_preauth_systems)
+ / sizeof(static_preauth_systems[0]);
+ if (preauth_plugins_ftables != NULL) {
+ for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
+ ftable = preauth_plugins_ftables[i];
+ if ((ftable->server_flags_proc == NULL) &&
+ (ftable->server_edata_proc == NULL) &&
+ (ftable->server_verify_proc == NULL) &&
+ (ftable->server_return_proc == NULL)) {
+ continue;
+ }
+ for (j = 0;
+ ftable->server_pa_type_list != NULL &&
+ ftable->server_pa_type_list[j] > 0;
+ j++) {
+ module_count++;
+ }
+ }
+ }
+
+ /* Build the complete list of supported preauthentication options, and
+ * leave room for a terminator entry. */
+ preauth_systems = malloc(sizeof(krb5_preauth_systems) * (module_count + 1));
+ if (preauth_systems == NULL) {
+ return ENOMEM;
+ }
+
+ /* Add the locally-supplied mechanisms to the dynamic list first. */
+ for (i = 0, k = 0;
+ i < sizeof(static_preauth_systems) / sizeof(static_preauth_systems[0]);
+ i++) {
+ if (static_preauth_systems[i].type == -1)
+ break;
+ preauth_systems[k] = static_preauth_systems[i];
+ /* Try to initialize the preauth system. If it fails, we'll remove it
+ * from the list of systems we'll be using. */
+ pa_sys_context = NULL;
+ pa_type = static_preauth_systems[i].type;
+ server_init_proc = static_preauth_systems[i].init;
+ if ((server_init_proc != NULL) &&
+ ((*server_init_proc)(context, pa_type, &pa_sys_context) != 0)) {
+ memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
+ continue;
+ }
+ preauth_systems[k].pa_sys_context = pa_sys_context;
+ k++;
+ }
+
+ /* Now add the dynamically-loaded mechanisms to the list. */
+ if (preauth_plugins_ftables != NULL) {
+ for (i = 0; preauth_plugins_ftables[i] != NULL; i++) {
+ ftable = preauth_plugins_ftables[i];
+ if ((ftable->server_flags_proc == NULL) &&
+ (ftable->server_edata_proc == NULL) &&
+ (ftable->server_verify_proc == NULL) &&
+ (ftable->server_return_proc == NULL)) {
+ continue;
+ }
+ for (j = 0;
+ ftable->server_pa_type_list != NULL &&
+ ftable->server_pa_type_list[j] > 0;
+ j++) {
+ /* Try to initialize the module. If it fails, we'll remove it
+ * from the list of modules we'll be using. */
+ pa_sys_context = NULL;
+ server_init_proc = ftable->server_init_proc;
+ pa_type = ftable->server_pa_type_list[j];
+ if ((server_init_proc != NULL) &&
+ ((*server_init_proc)(context, pa_type,
+ &pa_sys_context) != 0)) {
+ memset(&preauth_systems[k], 0, sizeof(preauth_systems[k]));
+ continue;
+ }
+ preauth_systems[k].name = ftable->name;
+ pa_type = ftable->server_pa_type_list[j];
+ preauth_systems[k].type = pa_type;
+ preauth_systems[k].flags = ftable->server_flags_proc(context,
+ pa_type);
+ preauth_systems[k].pa_sys_context = pa_sys_context;
+ preauth_systems[k].init = server_init_proc;
+ preauth_systems[k].fini = ftable->server_fini_proc;
+ preauth_systems[k].get_edata = ftable->server_edata_proc;
+ preauth_systems[k].verify_padata = ftable->server_verify_proc;
+ preauth_systems[k].return_padata = ftable->server_return_proc;
+ preauth_systems[k].free_pa_request_context =
+ ftable->server_freepa_reqcontext_proc;
+ k++;
+ }
+ }
+ }
+ n_preauth_systems = k;
+ /* Add the end-of-list marker. */
+ preauth_systems[k].name = "[end]";
+ preauth_systems[k].type = -1;
+}
+
+krb5_error_code
+unload_preauth_plugins(krb5_context context)
+{
+ int i;
+ struct errinfo err;
+ if (preauth_systems != NULL) {
+ for (i = 0; i < n_preauth_systems; i++) {
+ if (preauth_systems[i].fini != NULL) {
+ (*preauth_systems[i].fini)(context,
+ preauth_systems[i].type,
+ preauth_systems[i].pa_sys_context);
+ }
+ memset(&preauth_systems[i], 0, sizeof(preauth_systems[i]));
+ }
+ free(preauth_systems);
+ preauth_systems = NULL;
+ n_preauth_systems = 0;
+ krb5int_close_plugin_dirs(&preauth_plugins);
+ }
+ return 0;
+}
+
+/*
+ * The make_padata_context() function creates a space for storing any context
+ * information which will be needed by return_padata() later. Each preauth
+ * type gets a context storage location of its own.
+ */
+struct request_pa_context {
+ int n_contexts;
+ struct {
+ krb5_preauth_systems *pa_system;
+ void *pa_context;
+ } *contexts;
+};
+
+static krb5_error_code
+make_padata_context(krb5_context context, void **padata_context)
+{
+ int i;
+ struct request_pa_context *ret;
+
+ ret = malloc(sizeof(*ret));
+ if (ret == NULL) {
+ return ENOMEM;
+ }
+
+ ret->n_contexts = n_preauth_systems;
+ ret->contexts = malloc(sizeof(ret->contexts[0]) * ret->n_contexts);
+ if (ret->contexts == NULL) {
+ free(ret);
+ return ENOMEM;
+ }
+
+ memset(ret->contexts, 0, sizeof(ret->contexts[0]) * ret->n_contexts);
+
+ for (i = 0; i < ret->n_contexts; i++) {
+ ret->contexts[i].pa_system = &preauth_systems[i];
+ ret->contexts[i].pa_context = NULL;
+ }
+
+ *padata_context = ret;
+
+ return 0;
+}
+
+/*
+ * The free_padata_context function frees any context information pointers
+ * which the check_padata() function created but which weren't already cleaned
+ * up by return_padata().
+ */
+krb5_error_code
+free_padata_context(krb5_context kcontext, void **padata_context)
+{
+ struct request_pa_context *context;
+ krb5_preauth_systems *preauth_system;
+ void **pctx, *mctx;
+ int i;
+
+ if (padata_context == NULL)
+ return 0;
+
+ context = *padata_context;
+
+ for (i = 0; i < context->n_contexts; i++) {
+ if (context->contexts[i].pa_context != NULL) {
+ preauth_system = context->contexts[i].pa_system;
+ mctx = preauth_system->pa_sys_context;
+ if (preauth_system->free_pa_request_context != NULL) {
+ pctx = &context->contexts[i].pa_context;
+ (*preauth_system->free_pa_request_context)(kcontext, mctx,
+ pctx);
+ }
+ context->contexts[i].pa_context = NULL;
+ }
+ }
+
+ free(context->contexts);
+ free(context);
+
+ return 0;
+}
+
+/* Retrieve a specified tl_data item from the given entry, and return its
+ * contents in a new krb5_data, which must be freed by the caller. */
+static krb5_error_code
+get_entry_tl_data(krb5_context context, krb5_db_entry *entry,
+ krb5_int16 tl_data_type, krb5_data **result)
+{
+ krb5_tl_data *tl;
+ for (tl = entry->tl_data; tl != NULL; tl = tl->tl_data_next) {
+ if (tl->tl_data_type == tl_data_type) {
+ *result = malloc(sizeof(krb5_data));
+ if (*result == NULL) {
+ return ENOMEM;
+ }
+ (*result)->magic = KV5M_DATA;
+ (*result)->data = malloc(tl->tl_data_length);
+ if ((*result)->data == NULL) {
+ free(*result);
+ *result = NULL;
+ return ENOMEM;
+ }
+ memcpy((*result)->data, tl->tl_data_contents, tl->tl_data_length);
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+
+/*
+ * Retrieve a specific piece of information pertaining to the entry or the
+ * request and return it in a new krb5_data item which the caller must free.
+ *
+ * This may require massaging data into a contrived format, but it will
+ * hopefully keep us from having to reveal library-internal functions to
+ * modules.
+ */
+static krb5_error_code
+get_entry_data(krb5_context context,
+ krb5_kdc_req *request, krb5_db_entry *entry,
+ enum krb5plugin_preauth_entry_request_type type,
+ krb5_data **result)
+{
+ int i, k;
+ krb5_tl_data *tl;
+ krb5_data *ret;
+ krb5_deltat *delta;
+ krb5_keyblock *keys;
+ krb5_key_data *entry_key;
+
+ switch (type) {
+ case krb5plugin_preauth_entry_request_certificate:
+ return get_entry_tl_data(context, entry,
+ KRB5_TL_USER_CERTIFICATE, result);
+ break;
+ case krb5plugin_preauth_entry_max_time_skew:
+ ret = malloc(sizeof(krb5_data));
+ if (ret == NULL)
+ return ENOMEM;
+ delta = malloc(sizeof(krb5_deltat));
+ if (delta == NULL) {
+ free(ret);
+ return ENOMEM;
+ }
+ *delta = context->clockskew;
+ ret->data = (char *) delta;
+ ret->length = sizeof(*delta);
+ *result = ret;
+ return 0;
+ break;
+ case krb5plugin_preauth_keys:
+ ret = malloc(sizeof(krb5_data));
+ if (ret == NULL)
+ return ENOMEM;
+ keys = malloc(sizeof(krb5_keyblock) * (request->nktypes + 1));
+ if (keys == NULL) {
+ free(ret);
+ return ENOMEM;
+ }
+ ret->data = (char *) keys;
+ ret->length = sizeof(krb5_keyblock) * (request->nktypes + 1);
+ memset(ret->data, 0, ret->length);
+ k = 0;
+ for (i = 0; i < request->nktypes; i++) {
+ entry_key = NULL;
+ if (krb5_dbe_find_enctype(context, entry, request->ktype[i],
+ -1, 0, &entry_key) != 0)
+ continue;
+ if (krb5_dbekd_decrypt_key_data(context, &master_keyblock,
+ entry_key, &keys[k], NULL) != 0) {
+ if (keys[k].contents != NULL)
+ krb5_free_keyblock_contents(context, &keys[k]);
+ memset(&keys[k], 0, sizeof(keys[k]));
+ continue;
+ }
+ k++;
+ }
+ if (k > 0) {
+ *result = ret;
+ return 0;
+ } else {
+ free(keys);
+ free(ret);
+ }
+ break;
+ case krb5plugin_preauth_request_body:
+ ret = NULL;
+ encode_krb5_kdc_req_body(request, &ret);
+ if (ret != NULL) {
+ *result = ret;
+ return 0;
+ }
+ return ASN1_PARSE_ERROR;
+ break;
+ default:
+ break;
+ }
+ return ENOENT;
+}
static krb5_error_code
find_pa_system(int type, krb5_preauth_systems **preauth)
{
- krb5_preauth_systems *ap = preauth_systems;
-
+ krb5_preauth_systems *ap;
+
+ ap = preauth_systems ? preauth_systems : static_preauth_systems;
while ((ap->type != -1) && (ap->type != type))
ap++;
if (ap->type == -1)
return 0;
}
+static krb5_error_code
+find_pa_context(krb5_preauth_systems *pa_sys,
+ struct request_pa_context *context,
+ void ***pa_context)
+{
+ krb5_preauth_systems *preauth_system;
+ int i;
+
+ *pa_context = 0;
+
+ if (context == NULL)
+ return KRB5KRB_ERR_GENERIC;
+
+ for (i = 0; i < context->n_contexts; i++) {
+ if (context->contexts[i].pa_system == pa_sys) {
+ *pa_context = &context->contexts[i].pa_context;
+ return 0;
+ }
+ }
+
+ return KRB5KRB_ERR_GENERIC;
+}
+
+/*
+ * Create a list of indices into the preauth_systems array, sorted by order of
+ * preference.
+ */
+static krb5_boolean
+pa_list_includes(krb5_pa_data **pa_data, krb5_preauthtype pa_type)
+{
+ while (*pa_data != NULL) {
+ if ((*pa_data)->pa_type == pa_type)
+ return TRUE;
+ pa_data++;
+ }
+ return FALSE;
+}
+static void
+sort_pa_order(krb5_context context, krb5_kdc_req *request, int *pa_order)
+{
+ int i, j, k, n_repliers, n_key_replacers;
+
+ /* First, set up the default order. */
+ i = 0;
+ for (j = 0; j < n_preauth_systems; j++) {
+ if (preauth_systems[j].return_padata != NULL)
+ pa_order[i++] = j;
+ }
+ n_repliers = i;
+ pa_order[n_repliers] = -1;
+
+ /* Reorder so that PA_REPLACES_KEY modules are listed first. */
+ for (i = 0; i < n_repliers; i++) {
+ /* If this module replaces the key, then it's okay to leave it where it
+ * is in the order. */
+ if (preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY)
+ continue;
+ /* If not, search for a module which does, and swap in the first one we
+ * find. */
+ for (j = i + 1; j < n_repliers; j++) {
+ if (preauth_systems[pa_order[j]].flags & PA_REPLACES_KEY) {
+ k = pa_order[j];
+ pa_order[j] = pa_order[i];
+ pa_order[i] = k;
+ break;
+ }
+ }
+ }
+
+ /* Now sort just the modules which replace the key, placing those which
+ * handle the pa_data types provided by the client ahead of the others. */
+ for (i = 0; preauth_systems[pa_order[i]].flags & PA_REPLACES_KEY; i++) {
+ continue;
+ }
+ n_key_replacers = i;
+ for (i = 0; i < n_key_replacers; i++) {
+ if (pa_list_includes(request->padata,
+ preauth_systems[pa_order[i]].type))
+ continue;
+ for (j = i + 1; j < n_key_replacers; j++) {
+ if (pa_list_includes(request->padata,
+ preauth_systems[pa_order[j]].type)) {
+ k = pa_order[j];
+ pa_order[j] = pa_order[i];
+ pa_order[i] = k;
+ break;
+ }
+ }
+ }
+#ifdef DEBUG
+ krb5_klog_syslog(LOG_DEBUG, "original preauth mechanism list:");
+ for (i = 0; i < n_preauth_systems; i++) {
+ if (preauth_systems[i].return_padata != NULL)
+ krb5_klog_syslog(LOG_DEBUG, "... %s(%d)", preauth_systems[i].name,
+ preauth_systems[i].type);
+ }
+ krb5_klog_syslog(LOG_DEBUG, "sorted preauth mechanism list:");
+ for (i = 0; pa_order[i] != -1; i++) {
+ krb5_klog_syslog(LOG_DEBUG, "... %s(%d)",
+ preauth_systems[pa_order[i]].name,
+ preauth_systems[pa_order[i]].type);
+ }
+#endif
+}
+
const char *missing_required_preauth(krb5_db_entry *client,
krb5_db_entry *server,
krb5_enc_tkt_part *enc_tkt_reply)
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));
+ pa_data = malloc(sizeof(krb5_pa_data *) * (n_preauth_systems+1));
if (pa_data == 0)
return;
- memset(pa_data, 0, sizeof(krb5_pa_data *) * (MAX_PREAUTH_SYSTEMS+1));
+ memset(pa_data, 0, sizeof(krb5_pa_data *) * (n_preauth_systems+1));
pa = pa_data;
for (ap = preauth_systems; ap->type != -1; ap++) {
(*pa)->magic = KV5M_PA_DATA;
(*pa)->pa_type = ap->type;
if (ap->get_edata) {
- retval = (ap->get_edata)(kdc_context, request, client, server, *pa);
+ retval = (ap->get_edata)(kdc_context, request, client, server,
+ get_entry_data, ap->pa_sys_context, *pa);
if (retval) {
/* just failed on this type, continue */
free(*pa);
/*
* This routine is called to verify the preauthentication information
* for a V5 request.
- *
+ *
* Returns 0 if the pre-authentication is valid, non-zero to indicate
* an error code of some sort.
*/
krb5_error_code
-check_padata (krb5_context context, krb5_db_entry *client,
- krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply)
+check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
+ void **padata_context)
{
krb5_error_code retval = 0;
krb5_pa_data **padata;
krb5_preauth_systems *pa_sys;
- int pa_ok = 0, pa_found = 0;
+ void **pa_context;
+ int pa_ok = 0, pa_found = 0;
if (request->padata == 0)
return 0;
+ if (make_padata_context(context, padata_context) != 0) {
+ return KRB5KRB_ERR_GENERIC;
+ }
+
#ifdef DEBUG
krb5_klog_syslog (LOG_DEBUG, "checking padata");
#endif
#endif
if (find_pa_system((*padata)->pa_type, &pa_sys))
continue;
+ if (find_pa_context(pa_sys, *padata_context, &pa_context))
+ continue;
#ifdef DEBUG
krb5_klog_syslog (LOG_DEBUG, ".. pa_type %s", pa_sys->name);
#endif
if (pa_sys->verify_padata == 0)
continue;
pa_found++;
- retval = pa_sys->verify_padata(context, client, request,
- enc_tkt_reply, *padata);
+ retval = pa_sys->verify_padata(context, client, req_pkt, request,
+ enc_tkt_reply, *padata,
+ get_entry_data, pa_sys->pa_sys_context,
+ pa_context);
if (retval) {
char * emsg = krb5_get_error_message (context, retval);
krb5_klog_syslog (LOG_INFO, "preauth (%s) verify failure: %s",
/* The following switch statement allows us
* to return some preauth system errors back to the client.
*/
- switch(retval) {
- case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ switch(retval) {
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
case KRB5KRB_AP_ERR_SKEW:
+ case KRB5KDC_ERR_ETYPE_NOSUPP:
+ /* rfc 4556 */
+ case KRB5KDC_ERR_CLIENT_NOT_TRUSTED:
+ case KRB5KDC_ERR_INVALID_SIG:
+ case KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED:
+ case KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE:
+ case KRB5KDC_ERR_INVALID_CERTIFICATE:
+ case KRB5KDC_ERR_REVOKED_CERTIFICATE:
+ case KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN:
+ case KRB5KDC_ERR_CLIENT_NAME_MISMATCH:
+ case KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE:
+ case KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED:
+ case KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED:
+ case KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED:
+ case KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED:
+ /* earlier drafts of what became rfc 4556 */
+ case KRB5KDC_ERR_CERTIFICATE_MISMATCH:
+ case KRB5KDC_ERR_KDC_NOT_TRUSTED:
+ case KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE:
+ /* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
+ /* case KRB5KDC_ERR_KEY_TOO_WEAK: */
return retval;
default:
return KRB5KDC_ERR_PREAUTH_FAILED;
* structures which should be returned by the KDC to the client
*/
krb5_error_code
-return_padata(krb5_context context, krb5_db_entry *client,
+return_padata(krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
krb5_kdc_req *request, krb5_kdc_rep *reply,
- krb5_key_data *client_key, krb5_keyblock *encrypting_key)
+ krb5_key_data *client_key, krb5_keyblock *encrypting_key,
+ void **padata_context)
{
krb5_error_code retval;
krb5_pa_data ** padata;
krb5_pa_data ** send_pa;
krb5_pa_data * pa = 0;
krb5_preauth_systems * ap;
+ int * pa_order;
+ int * pa_type;
int size = 0;
+ void ** pa_context;
+ krb5_boolean key_modified;
+ krb5_keyblock original_key;
for (ap = preauth_systems; ap->type != -1; ap++) {
if (ap->return_padata)
if ((send_pa_list = malloc((size+1) * sizeof(krb5_pa_data *))) == NULL)
return ENOMEM;
+ if ((pa_order = malloc((size+1) * sizeof(int))) == NULL) {
+ free(send_pa_list);
+ return ENOMEM;
+ }
+ sort_pa_order(context, request, pa_order);
+
+ retval = krb5_copy_keyblock_contents(context, encrypting_key,
+ &original_key);
+ if (retval) {
+ free(send_pa_list);
+ free(pa_order);
+ return retval;
+ }
+ key_modified = FALSE;
send_pa = send_pa_list;
*send_pa = 0;
-
- for (ap = preauth_systems; ap->type != -1; ap++) {
+
+ for (pa_type = pa_order; *pa_type != -1; pa_type++) {
+ ap = &preauth_systems[*pa_type];
+ if (!key_modified)
+ if (original_key.enctype != encrypting_key->enctype)
+ key_modified = TRUE;
+ if (!key_modified)
+ if (original_key.length != encrypting_key->length)
+ key_modified = TRUE;
+ if (!key_modified)
+ if (memcmp(original_key.contents, encrypting_key->contents,
+ original_key.length) != 0)
+ key_modified = TRUE;
+ if (key_modified && (ap->flags & PA_REPLACES_KEY))
+ continue;
if (ap->return_padata == 0)
continue;
+ if (find_pa_context(ap, *padata_context, &pa_context))
+ continue;
pa = 0;
if (request->padata) {
for (padata = request->padata; *padata; padata++) {
}
}
}
- if ((retval = ap->return_padata(context, pa, client, request, reply,
- client_key, encrypting_key, send_pa)))
+ if ((retval = ap->return_padata(context, pa, client, req_pkt, request, reply,
+ client_key, encrypting_key, send_pa,
+ get_entry_data, ap->pa_sys_context,
+ pa_context))) {
goto cleanup;
+ }
if (*send_pa)
send_pa++;
}
cleanup:
+ krb5_free_keyblock_contents(context, &original_key);
+ free(pa_order);
if (send_pa_list)
krb5_free_pa_data(context, send_pa_list);
return (retval);
static krb5_error_code
verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
- krb5_pa_data *pa)
+ krb5_pa_data *pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context)
{
krb5_pa_enc_ts * pa_enc = 0;
krb5_error_code retval;
static krb5_error_code
get_etype_info(krb5_context context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
krb5_pa_data *pa_data)
{
int i;
static krb5_error_code
get_etype_info2(krb5_context context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
krb5_pa_data *pa_data)
{
return etype_info_helper( context, request, client, server, pa_data, 1);
static krb5_error_code
return_etype_info2(krb5_context context, krb5_pa_data * padata,
krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa)
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context)
{
return etype_info_as_rep_helper(context, padata, client, request, reply,
client_key, encrypting_key, send_pa, 1);
static krb5_error_code
return_etype_info(krb5_context context, krb5_pa_data * padata,
krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_kdc_rep *reply,
krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
- krb5_pa_data **send_pa)
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context)
{
return etype_info_as_rep_helper(context, padata, client, request, reply,
client_key, encrypting_key, send_pa, 0);
static krb5_error_code
return_pw_salt(krb5_context context, krb5_pa_data *in_padata,
- krb5_db_entry *client, krb5_kdc_req *request,
+ krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
krb5_kdc_rep *reply, krb5_key_data *client_key,
- krb5_keyblock *encrypting_key, krb5_pa_data **send_pa)
+ krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context)
{
krb5_error_code retval;
krb5_pa_data * padata;
static krb5_error_code
return_sam_data(krb5_context context, krb5_pa_data *in_padata,
- krb5_db_entry *client, krb5_kdc_req *request,
+ krb5_db_entry *client, krb5_data *req_pkt, krb5_kdc_req *request,
krb5_kdc_rep *reply, krb5_key_data *client_key,
- krb5_keyblock *encrypting_key, krb5_pa_data **send_pa)
+ krb5_keyblock *encrypting_key, krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context)
{
krb5_error_code retval;
krb5_data scratch;
static krb5_error_code
get_sam_edata(krb5_context context, krb5_kdc_req *request,
krb5_db_entry *client, krb5_db_entry *server,
- krb5_pa_data *pa_data)
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context, krb5_pa_data *pa_data)
{
krb5_error_code retval;
krb5_sam_challenge sc;
static krb5_error_code
verify_sam_response(krb5_context context, krb5_db_entry *client,
+ krb5_data *req_pkt,
krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
- krb5_pa_data *pa)
+ krb5_pa_data *pa,
+ preauth_get_entry_data_proc get_entry_data,
+ void *pa_system_context,
+ void **pa_request_context)
{
krb5_error_code retval;
krb5_data scratch;
rep_etypes2str(char *s, size_t len, krb5_kdc_rep *rep);
/* do_as_req.c */
-krb5_error_code process_as_req (krb5_kdc_req *,
+krb5_error_code process_as_req (krb5_kdc_req *, krb5_data *,
const krb5_fulladdr *,
krb5_data ** );
krb5_db_entry *client,
krb5_db_entry *server,
krb5_data *e_data);
+krb5_error_code load_preauth_plugins(krb5_context context);
+krb5_error_code unload_preauth_plugins(krb5_context context);
+
krb5_error_code check_padata
- (krb5_context context, krb5_db_entry *client,
- krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply);
+ (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
+ krb5_kdc_req *request, krb5_enc_tkt_part *enc_tkt_reply,
+ void **padata_context);
krb5_error_code return_padata
(krb5_context context, krb5_db_entry *client,
- krb5_kdc_req *request, krb5_kdc_rep *reply,
- krb5_key_data *client_key, krb5_keyblock *encrypting_key);
+ krb5_data *req_pkt, krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key, krb5_keyblock *encrypting_key,
+ void **padata_context);
+krb5_error_code free_padata_context
+ (krb5_context context, void **padata_context);
+
/* replay.c */
krb5_boolean kdc_check_lookaside (krb5_data *, krb5_data **);
void kdc_insert_lookaside (krb5_data *, krb5_data *);
((X) == ADDRTYPE_INET ? AF_INET : -1)
#endif
+/* RFC 4120: KRB5KDC_ERR_KEY_TOO_WEAK
+ * RFC 4556: KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED */
+#define KRB5KDC_ERR_KEY_TOO_WEAK KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED
+
#endif /* __KRB5_KDC_UTIL__ */
setup_signal_handlers();
+ load_preauth_plugins(kcontext);
+
retval = setup_sam();
if (retval) {
com_err(argv[0], retval, "while initializing SAM");
errout++;
}
krb5_klog_syslog(LOG_INFO, "shutting down");
+ unload_preauth_plugins(kcontext);
krb5_klog_close(kdc_context);
finish_realms(argv[0]);
if (kdc_realmlist)
error_code KRB5PLACEHOLD_59, "KRB5 error code 59"
error_code KRB5KRB_ERR_GENERIC, "Generic error (see e-text)"
error_code KRB5KRB_ERR_FIELD_TOOLONG, "Field is too long for this implementation"
-error_code KRB5PLACEHOLD_62, "KRB5 error code 62"
-error_code KRB5PLACEHOLD_63, "KRB5 error code 63"
-error_code KRB5PLACEHOLD_64, "KRB5 error code 64"
-error_code KRB5PLACEHOLD_65, "KRB5 error code 65"
-error_code KRB5PLACEHOLD_66, "KRB5 error code 66"
+error_code KRB5KDC_ERR_CLIENT_NOT_TRUSTED, "Client not trusted"
+error_code KRB5KDC_ERR_KDC_NOT_TRUSTED, "KDC not trusted"
+error_code KRB5KDC_ERR_INVALID_SIG, "Invalid signature"
+error_code KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED, "Key parameters not accepted"
+error_code KRB5KDC_ERR_CERTIFICATE_MISMATCH, "Certificate mismatch"
error_code KRB5PLACEHOLD_67, "KRB5 error code 67"
error_code KRB5PLACEHOLD_68, "KRB5 error code 68"
error_code KRB5PLACEHOLD_69, "KRB5 error code 69"
-error_code KRB5PLACEHOLD_70, "KRB5 error code 70"
-error_code KRB5PLACEHOLD_71, "KRB5 error code 71"
-error_code KRB5PLACEHOLD_72, "KRB5 error code 72"
-error_code KRB5PLACEHOLD_73, "KRB5 error code 73"
-error_code KRB5PLACEHOLD_74, "KRB5 error code 74"
-error_code KRB5PLACEHOLD_75, "KRB5 error code 75"
-error_code KRB5PLACEHOLD_76, "KRB5 error code 76"
-error_code KRB5PLACEHOLD_77, "KRB5 error code 77"
-error_code KRB5PLACEHOLD_78, "KRB5 error code 78"
-error_code KRB5PLACEHOLD_79, "KRB5 error code 79"
-error_code KRB5PLACEHOLD_80, "KRB5 error code 80"
-error_code KRB5PLACEHOLD_81, "KRB5 error code 81"
+error_code KRB5KDC_ERR_CANT_VERIFY_CERTIFICATE, "Can't verify certificate"
+error_code KRB5KDC_ERR_INVALID_CERTIFICATE, "Invalid certificate"
+error_code KRB5KDC_ERR_REVOKED_CERTIFICATE, "Revoked certificate"
+error_code KRB5KDC_ERR_REVOCATION_STATUS_UNKNOWN, "Revocation status unknown"
+error_code KRB5KDC_ERR_REVOCATION_STATUS_UNAVAILABLE, "Revocation status unavailable"
+error_code KRB5KDC_ERR_CLIENT_NAME_MISMATCH, "Client name mismatch"
+error_code KRB5KDC_ERR_KDC_NAME_MISMATCH, "KDC name mismatch"
+error_code KRB5KDC_ERR_INCONSISTENT_KEY_PURPOSE, "Inconsistent key purpose"
+error_code KRB5KDC_ERR_DIGEST_IN_CERT_NOT_ACCEPTED, "Digest in certificate not accepted"
+error_code KRB5KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED, "Checksum must be included"
+error_code KRB5KDC_ERR_DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signed-data not accepted"
+error_code KRB5KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
error_code KRB5PLACEHOLD_82, "KRB5 error code 82"
error_code KRB5PLACEHOLD_83, "KRB5 error code 83"
error_code KRB5PLACEHOLD_84, "KRB5 error code 84"
PROG_LIBPATH=-L$(TOPLIBD)
PROG_RPATH=$(KRB5_LIBDIR)
LOCALINCLUDES = -I$(srcdir)/../os -I$(SRCTOP)
-DEFS=
+DEFS=-DLIBDIR=\"$(KRB5_LIBDIR)\"
##DOS##BUILDTOP = ..\..\..
##DOS##PREFIXDIR=krb
static krb5_error_code make_preauth_list (krb5_context,
krb5_preauthtype *,
int, krb5_pa_data ***);
+static krb5_error_code sort_krb5_padata_sequence(krb5_context context,
+ krb5_data *realm,
+ krb5_pa_data **padata);
/*
* This function performs 32 bit bounded addition so we can generate
static krb5_error_code
send_as_request(krb5_context context,
krb5_kdc_req *request,
- krb5_timestamp *time_now,
krb5_error ** ret_err_reply,
krb5_kdc_rep ** ret_as_reply,
int *use_master)
krb5_data reply;
char k4_version; /* same type as *(krb5_data::data) */
int tcp_only = 0;
+ krb5_timestamp time_now;
reply.data = 0;
-
- if ((retval = krb5_timeofday(context, time_now)))
- goto cleanup;
- /*
- * XXX we know they are the same size... and we should do
- * something better than just the current time
- */
- request->nonce = (krb5_int32) *time_now;
+ /* set the nonce if the caller expects us to do it */
+ if (request->nonce == 0) {
+ if ((retval = krb5_timeofday(context, &time_now)))
+ goto cleanup;
+ request->nonce = (krb5_int32) time_now;
+ }
/* encode & send to KDC */
if ((retval = encode_krb5_as_req(request, &packet)) != 0)
0
};
-
krb5_error_code KRB5_CALLCONV
krb5_get_in_tkt(krb5_context context,
const krb5_flags options,
request.kdc_options = options;
request.client = creds->client;
request.server = creds->server;
+ request.nonce = 0;
request.from = creds->times.starttime;
request.till = creds->times.endtime;
request.rtime = creds->times.renew_till;
err_reply = 0;
as_reply = 0;
- if ((retval = send_as_request(context, &request, &time_now, &err_reply,
+
+ if ((retval = krb5_timeofday(context, &time_now)))
+ goto cleanup;
+
+ /*
+ * XXX we know they are the same size... and we should do
+ * something better than just the current time
+ */
+ request.nonce = (krb5_int32) time_now;
+
+ if ((retval = send_as_request(context, &request, &err_reply,
&as_reply, &use_master)))
goto cleanup;
retval = decode_krb5_padata_sequence(&err_reply->e_data,
&preauth_to_use);
krb5_free_error(context, err_reply);
+ if (retval)
+ goto cleanup;
+ retval = sort_krb5_padata_sequence(context,
+ &request.server->realm,
+ padata);
if (retval)
goto cleanup;
continue;
return(0);
}
+/* Sort a pa_data sequence so that types named in the "preferred_preauth_types"
+ * libdefaults entry are listed before any others. */
+static krb5_error_code KRB5_CALLCONV
+sort_krb5_padata_sequence(krb5_context context, krb5_data *realm,
+ krb5_pa_data **padata)
+{
+ int i, j, base;
+ krb5_error_code ret;
+ const char *p;
+ long l;
+ char *q, *preauth_types = NULL;
+ krb5_pa_data *tmp;
+
+ if ((padata == NULL) || (padata[0] == NULL)) {
+ return 0;
+ }
+
+ ret = krb5_libdefault_string(context, realm, "preferred_preauth_types",
+ &preauth_types);
+ if ((ret != 0) || (preauth_types == NULL)) {
+ /* Try to use PKINIT first. */
+ preauth_types = "17, 16, 15, 14";
+ }
+
+#ifdef DEBUG
+ fprintf (stderr, "preauth data types before sorting:");
+ for (i = 0; padata[i]; i++) {
+ fprintf (stderr, " %d", padata[i]->pa_type);
+ }
+ fprintf (stderr, "\n");
+#endif
+
+ base = 0;
+ for (p = preauth_types; *p != '\0'; p++) {
+ /* skip whitespace to find an entry */
+ p += strspn(p, ", ");
+ if (*p != '\0') {
+ /* see if we can extract a number */
+ l = strtol(p, &q, 10);
+ if ((q != NULL) && (q > p)) {
+ /* got a valid number; search for a matchin entry */
+ for (i = base; padata[i] != NULL; i++) {
+ /* bubble the matching entry to the front of the list */
+ if (padata[i]->pa_type == l) {
+ tmp = padata[i];
+ for (j = i; j > base; j--)
+ padata[j] = padata[j - 1];
+ padata[base] = tmp;
+ base++;
+ break;
+ }
+ }
+ p = q;
+ } else {
+ break;
+ }
+ }
+ }
+
+#ifdef DEBUG
+ fprintf (stderr, "preauth data types after sorting:");
+ for (i = 0; padata[i]; i++)
+ fprintf (stderr, " %d", padata[i]->pa_type);
+ fprintf (stderr, "\n");
+#endif
+
+ return 0;
+}
+
krb5_error_code KRB5_CALLCONV
krb5_get_init_creds(krb5_context context,
krb5_creds *creds,
{
krb5_error_code ret;
krb5_kdc_req request;
- krb5_pa_data **padata;
+ krb5_data *encoded_request_body, *encoded_previous_request;
+ krb5_pa_data **preauth_to_use, **kdc_padata;
int tempint;
char *tempstr;
krb5_deltat tkt_life;
krb5_kdc_rep *local_as_reply;
krb5_timestamp time_now;
krb5_enctype etype = 0;
+ krb5_preauth_context *preauth_context;
/* initialize everything which will be freed at cleanup */
request.ktype = NULL;
request.addresses = NULL;
request.padata = NULL;
- padata = NULL;
+ encoded_request_body = NULL;
+ encoded_previous_request = NULL;
+ preauth_to_use = NULL;
+ kdc_padata = NULL;
as_key.length = 0;
salt.length = 0;
salt.data = NULL;
local_as_reply = 0;
+ preauth_context = NULL;
+ err_reply = NULL;
+
/*
* Set up the basic request structure
*/
request.magic = KV5M_KDC_REQ;
request.msg_type = KRB5_AS_REQ;
+ /* request.nonce is filled in when we send a request to the kdc */
+ request.nonce = 0;
+
/* request.padata is filled in later */
request.kdc_options = context->kdc_default_options;
goto cleanup;
}
- /* nonce is filled in by send_as_request */
+ krb5_init_preauth_context(context, &preauth_context);
+
+ /* nonce is filled in by send_as_request if we don't take care of it */
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST)) {
request.ktype = options->etype_list;
if (options && (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) {
if ((ret = make_preauth_list(context, options->preauth_list,
- options->preauth_list_length,
- &padata)))
+ options->preauth_list_length,
+ &preauth_to_use)))
goto cleanup;
}
salt.data = NULL;
}
- /* now, loop processing preauth data and talking to the kdc */
+ /* set the request nonce */
+ if ((ret = krb5_timeofday(context, &time_now)))
+ goto cleanup;
+ /*
+ * XXX we know they are the same size... and we should do
+ * something better than just the current time
+ */
+ request.nonce = (krb5_int32) time_now;
+
+ /* give the preauth plugins a chance to prep the request body */
+ krb5_preauth_prepare_request(context, &preauth_context, options, &request);
+ ret = encode_krb5_kdc_req_body(&request, &encoded_request_body);
+ if (ret)
+ goto cleanup;
+
+ /* now, loop processing preauth data and talking to the kdc */
for (loopcount = 0; loopcount < MAX_IN_TKT_LOOPS; loopcount++) {
- if (request.padata) {
- krb5_free_pa_data(context, request.padata);
- request.padata = NULL;
+ if (!err_reply) {
+ /* either our first attempt, or retrying after PREAUTH_NEEDED */
+ if (request.padata) {
+ krb5_free_pa_data(context, request.padata);
+ request.padata = NULL;
+ }
+ if ((ret = krb5_do_preauth(context, &preauth_context,
+ &request,
+ encoded_request_body,
+ encoded_previous_request,
+ preauth_to_use, &request.padata,
+ &salt, &s2kparams, &etype, &as_key,
+ prompter, prompter_data,
+ gak_fct, gak_data)))
+ goto cleanup;
+ } else {
+ /* retrying after an error other than PREAUTH_NEEDED, using e-data
+ * to figure out what to change */
+ if (krb5_do_preauth_tryagain(context, &preauth_context,
+ &request,
+ encoded_request_body,
+ encoded_previous_request,
+ preauth_to_use, err_reply,
+ &request.padata,
+ &salt, &s2kparams,
+ &etype, &as_key,
+ prompter, prompter_data,
+ gak_fct, gak_data)) {
+ /* couldn't come up with anything better */
+ ret = err_reply->error + ERROR_TABLE_BASE_krb5;
+ krb5_free_error(context, err_reply);
+ err_reply = NULL;
+ goto cleanup;
+ }
+ krb5_free_error(context, err_reply);
+ err_reply = NULL;
}
- if ((ret = krb5_do_preauth(context, &request,
- padata, &request.padata,
- &salt, &s2kparams, &etype, &as_key, prompter,
- prompter_data, gak_fct, gak_data)))
+ if (encoded_previous_request != NULL) {
+ krb5_free_data(context, encoded_previous_request);
+ encoded_previous_request = NULL;
+ }
+ ret = encode_krb5_as_req(&request, &encoded_previous_request);
+ if (ret)
goto cleanup;
- if (padata) {
- krb5_free_pa_data(context, padata);
- padata = 0;
- }
-
err_reply = 0;
local_as_reply = 0;
- if ((ret = send_as_request(context, &request, &time_now, &err_reply,
+ if ((ret = send_as_request(context, &request, &err_reply,
&local_as_reply, use_master)))
goto cleanup;
if (err_reply) {
if (err_reply->error == KDC_ERR_PREAUTH_REQUIRED &&
err_reply->e_data.length > 0) {
+ /* reset the list of preauth types to try */
+ if (preauth_to_use) {
+ krb5_free_pa_data(context, preauth_to_use);
+ preauth_to_use = NULL;
+ }
ret = decode_krb5_padata_sequence(&err_reply->e_data,
- &padata);
+ &preauth_to_use);
krb5_free_error(context, err_reply);
+ err_reply = NULL;
+ if (ret)
+ goto cleanup;
+ ret = sort_krb5_padata_sequence(context,
+ &request.server->realm,
+ preauth_to_use);
if (ret)
goto cleanup;
+ /* continue to next iteration */
} else {
- ret = (krb5_error_code) err_reply->error
- + ERROR_TABLE_BASE_krb5;
- krb5_free_error(context, err_reply);
- goto cleanup;
+ if (err_reply->e_data.length > 0) {
+ /* continue to next iteration */
+ } else {
+ /* error + no hints = give up */
+ ret = (krb5_error_code) err_reply->error
+ + ERROR_TABLE_BASE_krb5;
+ krb5_free_error(context, err_reply);
+ goto cleanup;
+ }
}
} else if (local_as_reply) {
break;
}
/* process any preauth data in the as_reply */
-
- if ((ret = krb5_do_preauth(context, &request,
- local_as_reply->padata, &padata,
+ krb5_clear_preauth_context_use_counts(context, preauth_context);
+ if ((ret = sort_krb5_padata_sequence(context, &request.server->realm,
+ local_as_reply->padata)))
+ goto cleanup;
+ if ((ret = krb5_do_preauth(context, &preauth_context,
+ &request,
+ encoded_request_body, encoded_previous_request,
+ local_as_reply->padata, &kdc_padata,
&salt, &s2kparams, &etype, &as_key, prompter,
prompter_data, gak_fct, gak_data)))
goto cleanup;
- /* XXX if there's padata on output, something is wrong, but it's
- not obviously an error */
-
/* XXX For 1.1.1 and prior KDC's, when SAM is used w/ USE_SAD_AS_KEY,
the AS_REP comes back encrypted in the user's longterm key
instead of in the SAD. If there was a SAM preauth, there
ret = 0;
cleanup:
+ if (preauth_context != NULL) {
+ krb5_free_preauth_context(context, preauth_context);
+ preauth_context = NULL;
+ }
+ if (encoded_previous_request != NULL) {
+ krb5_free_data(context, encoded_previous_request);
+ encoded_previous_request = NULL;
+ }
+ if (encoded_request_body != NULL) {
+ krb5_free_data(context, encoded_request_body);
+ encoded_request_body = NULL;
+ }
if (request.server)
krb5_free_principal(context, request.server);
if (request.ktype &&
(!(options &&
(options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST))))
krb5_free_addresses(context, request.addresses);
- if (padata)
- krb5_free_pa_data(context, padata);
+ if (preauth_to_use)
+ krb5_free_pa_data(context, preauth_to_use);
+ if (kdc_padata)
+ krb5_free_pa_data(context, kdc_padata);
if (request.padata)
krb5_free_pa_data(context, request.padata);
if (as_key.length)
nctx->prompt_types = NULL;
nctx->os_context->default_ccname = NULL;
+ memset(&nctx->preauth_plugins, 0, sizeof(nctx->preauth_plugins));
+
memset(&nctx->libkrb5_plugins, 0, sizeof(nctx->libkrb5_plugins));
nctx->vtbl = NULL;
nctx->locate_fptrs = NULL;
*/
#include "k5-int.h"
+#include "osconf.h"
+#include <krb5/preauth_plugin.h>
+
+#include <unistd.h>
+
+#if TARGET_OS_MAC
+static const char *objdirs[] = { KRB5_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/plugins/preauth", NULL }; /* should be a list */
+#else
+static const char *objdirs[] = { LIBDIR "/krb5/plugins/preauth", NULL };
+#endif
typedef krb5_error_code (*pa_function)(krb5_context,
krb5_kdc_req *request,
int flags;
} pa_types_t;
-#define PA_REAL 0x0001
-#define PA_INFO 0x0002
+/* This structure lets us keep track of all of the modules which are loaded,
+ * turning the list of modules and their lists of implemented preauth types
+ * into a single list which we can walk easily. */
+struct _krb5_preauth_context {
+ int n_modules;
+ struct _krb5_preauth_context_module {
+ /* Which of the possibly more than one preauth types which the
+ * module supports we're using at this point in the list. */
+ krb5_preauthtype pa_type;
+ /* Encryption types which the client claims to support -- we
+ * copy them directly into the krb5_kdc_req structure during
+ * krb5_preauth_prepare_request(). */
+ krb5_enctype *enctypes;
+ /* The module's per-module context and a function to clear it. */
+ void *module_context;
+ void (*client_fini)(krb5_context context, krb5_preauthtype pa_type,
+ void *module_context);
+ /* The module's table, and some of its members, copied here for
+ * convenience when we populated the list. */
+ struct krb5plugin_preauth_ftable_v0 *ftable;
+ const char *name;
+ int flags, use_count;
+ krb5_error_code (*client_process)(krb5_context context,
+ void *module_context,
+ void **request_context,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *pa_data,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ void *gak_data,
+ krb5_keyblock *as_key,
+ krb5_pa_data **out_pa_data);
+ krb5_error_code (*client_tryagain)(krb5_context context,
+ void *module_context,
+ void **request_context,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_error *err_reply,
+ krb5_pa_data *old_pa_data,
+ krb5_pa_data **new_pa_data);
+ void (*client_cleanup)(krb5_context context, void *module_context,
+ void **request_context);
+ /* The per-pa_type context which the client_process() function
+ * might allocate, which we'll need to clean up later by
+ * calling the client_cleanup() function. */
+ void *request_context;
+ } *modules;
+};
+
+/* Create the per-AS-REQ context. This means loading the modules if we haven't
+ * done that yet (applications which never obtain initial credentials should
+ * never hit this routine), breaking up the module's list of support pa_types
+ * so that we can iterate over the modules more easily, and copying over the
+ * relevant parts of the module's table. */
+void
+krb5_init_preauth_context(krb5_context kcontext,
+ krb5_preauth_context **preauth_context)
+{
+ int n_modules, n_tables, i, j, k;
+ void **tables;
+ struct krb5plugin_preauth_ftable_v0 *table;
+ krb5_preauth_context *context;
+ void *module_context;
+ krb5_preauthtype pa_type;
+
+ /* load the plugins for the current context */
+ if (PLUGIN_DIR_OPEN(&kcontext->preauth_plugins) == 0) {
+ if (krb5int_open_plugin_dirs(objdirs, NULL,
+ &kcontext->preauth_plugins,
+ &kcontext->err) != 0) {
+ return;
+ }
+ }
+
+ /* pull out the module function tables for all of the modules */
+ tables = NULL;
+ if (krb5int_get_plugin_dir_data(&kcontext->preauth_plugins,
+ "preauthentication0",
+ &tables,
+ &kcontext->err) != 0) {
+ return;
+ }
+ if (tables == NULL) {
+ return;
+ }
+
+ /* count how many modules we ended up loading, and how many preauth
+ * types we may claim to support as a result */
+ n_modules = 0;
+ for (n_tables = 0;
+ (tables != NULL) && (tables[n_tables] != NULL);
+ n_tables++) {
+ table = tables[n_tables];
+ if ((table->client_pa_type_list != NULL) &&
+ (table->client_process != NULL)) {
+ for (j = 0; table->client_pa_type_list[j] > 0; j++) {
+ n_modules++;
+ }
+ }
+ }
+
+ /* allocate the space we need */
+ context = malloc(sizeof(*context));
+ if (context == NULL) {
+ return;
+ }
+ context->modules = malloc(sizeof(context->modules[0]) * n_modules);
+ if (context->modules == NULL) {
+ free(context);
+ return;
+ }
+ memset(context->modules, 0, sizeof(context->modules[0]) * n_modules);
+ context->n_modules = n_modules;
+
+ /* fill in the structure */
+ k = 0;
+ for (i = 0; i < n_tables; i++) {
+ table = tables[i];
+ if ((table->client_pa_type_list != NULL) &&
+ (table->client_process != NULL)) {
+ for (j = 0; table->client_pa_type_list[j] > 0; j++) {
+ pa_type = table->client_pa_type_list[j];
+ module_context = NULL;
+ if ((table->client_init != NULL) &&
+ ((*table->client_init)(kcontext, pa_type,
+ &module_context) != 0)) {
+#ifdef DEBUG
+ fprintf (stderr, "skip module \"%s\", pa_type %d\n",
+ table->name, pa_type);
+#endif
+ continue;
+ }
+ context->modules[k].pa_type = pa_type;
+ context->modules[k].enctypes = table->client_enctype_list;
+ context->modules[k].module_context = module_context;
+ context->modules[k].client_fini = table->client_fini;
+ context->modules[k].ftable = table;
+ context->modules[k].name = table->name;
+ context->modules[k].flags = (*table->client_flags)(kcontext,
+ pa_type);
+ context->modules[k].use_count = 0;
+ context->modules[k].client_process = table->client_process;
+ context->modules[k].client_tryagain = table->client_tryagain;
+ context->modules[k].client_cleanup = table->client_cleanup;
+ context->modules[k].request_context = NULL;
+#ifdef DEBUG
+ fprintf (stderr, "init module \"%s\", pa_type %d, flag %d\n",
+ context->modules[k].name,
+ context->modules[k].pa_type,
+ context->modules[k].flags);
+#endif
+ k++;
+ }
+ }
+ }
+
+ /* return the result */
+ *preauth_context = context;
+}
+
+/* Zero the use counts for the modules herein. Usually used before we
+ * start processing any data from the server, at which point every module
+ * will again be able to take a crack at whatever the server sent. */
+void
+krb5_clear_preauth_context_use_counts(krb5_context context,
+ krb5_preauth_context *preauth_context)
+{
+ int i;
+ if (preauth_context != NULL) {
+ for (i = 0; i < preauth_context->n_modules; i++) {
+ preauth_context->modules[i].use_count = 0;
+ }
+ }
+}
+
+/* Free the per-AS-REQ context. This means clearing any module-specific or
+ * request-specific context which the modules may have created, and then
+ * freeing the context itself. */
+void
+krb5_free_preauth_context(krb5_context context,
+ krb5_preauth_context *preauth_context)
+{
+ int i;
+ krb5_preauthtype pa_type;
+ void **rctx, *mctx;
+ if (preauth_context != NULL) {
+ for (i = 0; i < preauth_context->n_modules; i++) {
+ mctx = preauth_context->modules[i].module_context;
+ if (preauth_context->modules[i].request_context != NULL) {
+ if (preauth_context->modules[i].client_cleanup != NULL) {
+ rctx = &preauth_context->modules[i].request_context;
+ preauth_context->modules[i].client_cleanup(context,
+ mctx, rctx);
+ }
+ preauth_context->modules[i].request_context = NULL;
+ }
+ if (preauth_context->modules[i].client_fini != NULL) {
+ pa_type = preauth_context->modules[i].pa_type;
+ (*preauth_context->modules[i].client_fini)(context, pa_type,
+ mctx);
+ }
+ memset(&preauth_context->modules[i], 0,
+ sizeof(preauth_context->modules[i]));
+ }
+ if (preauth_context->modules != NULL) {
+ free(preauth_context->modules);
+ preauth_context->modules = NULL;
+ }
+ free(preauth_context);
+ }
+}
+
+/* Add the named encryption type to the existing list of ktypes. */
+static void
+grow_ktypes(krb5_enctype **out_ktypes, int *out_nktypes, krb5_enctype ktype)
+{
+ int i;
+ krb5_enctype *ktypes;
+ for (i = 0; i < *out_nktypes; i++) {
+ if ((*out_ktypes)[i] == ktype)
+ return;
+ }
+ ktypes = malloc((*out_nktypes + 2) * sizeof(ktype));
+ if (ktypes) {
+ for (i = 0; i < *out_nktypes; i++)
+ ktypes[i] = (*out_ktypes)[i];
+ ktypes[i++] = ktype;
+ ktypes[i] = 0;
+ free(*out_ktypes);
+ *out_ktypes = ktypes;
+ *out_nktypes = i;
+ }
+}
+
+/* Add the given pa_data item to the list of items. Factored out here to make
+ * reading the do_preauth logic easier to read. */
+static int
+grow_pa_list(krb5_pa_data ***out_pa_list, int *out_pa_list_size,
+ krb5_pa_data *addition)
+{
+ krb5_pa_data **pa_list;
+ int i;
+
+ if (out_pa_list == NULL) {
+ return EINVAL;
+ }
+
+ if (*out_pa_list == NULL) {
+ /* Allocate room for one entry and a NULL terminator. */
+ pa_list = malloc(2 * sizeof(krb5_pa_data *));
+ if (pa_list == NULL)
+ return ENOMEM;
+ pa_list[0] = addition;
+ pa_list[1] = NULL;
+ *out_pa_list = pa_list;
+ *out_pa_list_size = 1;
+ } else {
+ /* Allocate room for one more entry and a NULL terminator. */
+ pa_list = malloc((*out_pa_list_size + 2) * sizeof(krb5_pa_data *));
+ if (pa_list == NULL)
+ return ENOMEM;
+ for (i = 0; i < *out_pa_list_size; i++)
+ pa_list[i] = (*out_pa_list)[i];
+ pa_list[i++] = addition;
+ pa_list[i++] = NULL;
+ free(*out_pa_list);
+ *out_pa_list = pa_list;
+ *out_pa_list_size = i;
+ }
+ return 0;
+}
+
+/* Tweak the request body, for now adding any enctypes which the module claims
+ * to add support for to the list, but in the future perhaps doing more
+ * involved things. */
+void
+krb5_preauth_prepare_request(krb5_context kcontext,
+ krb5_preauth_context **preauth_context,
+ krb5_get_init_creds_opt *options,
+ krb5_kdc_req *request)
+{
+ int i, j, k;
+ krb5_enctype *ktypes;
+
+ if ((preauth_context == NULL) || (*preauth_context == NULL)) {
+ return;
+ }
+ /* Add the module-specific enctype list to the request, but only if
+ * it's something we can safely modify. */
+ if (!(options && (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST))) {
+ for (i = 0; i < (*preauth_context)->n_modules; i++) {
+ if ((*preauth_context)->modules[i].enctypes == NULL)
+ continue;
+ for (j = 0; (*preauth_context)->modules[i].enctypes[j] != 0; j++) {
+ grow_ktypes(&request->ktype, &request->nktypes,
+ (*preauth_context)->modules[i].enctypes[j]);
+ }
+ }
+ }
+}
+
+/* Find the first module which provides for the named preauth type which also
+ * hasn't had a chance to run yet (INFO modules don't count, because as a rule
+ * they don't generate preauth data), and run it. */
+static krb5_error_code
+krb5_run_preauth_plugins(krb5_context kcontext,
+ krb5_preauth_context *preauth_context,
+ int module_required_flags,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *in_padata,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ krb5_data *salt,
+ krb5_data *s2kparams,
+ void *gak_data,
+ krb5_keyblock *as_key,
+ krb5_pa_data ***out_pa_list,
+ int *out_pa_list_size,
+ int *module_ret,
+ int *module_flags)
+{
+ int i;
+ krb5_pa_data *out_pa_data;
+ krb5_error_code ret;
+ struct _krb5_preauth_context_module *module;
+
+ if (preauth_context == NULL) {
+ return ENOENT;
+ }
+ /* iterate over all loaded modules */
+ for (i = 0; i < preauth_context->n_modules; i++) {
+ module = &preauth_context->modules[i];
+ /* skip over those which don't match the preauth type */
+ if (module->pa_type != in_padata->pa_type)
+ continue;
+ /* skip over those which don't match the flags (INFO vs REAL, mainly) */
+ if ((module->flags & module_required_flags) == 0)
+ continue;
+ /* if it's a REAL module, try to call it only once per library call */
+ if (module_required_flags & PA_REAL) {
+ if (module->use_count > 0) {
+#ifdef DEBUG
+ fprintf(stderr, "skipping already-used module \"%s\"(%d)\n",
+ module->name, module->pa_type);
+#endif
+ continue;
+ }
+ module->use_count++;
+ }
+ /* run the module's callback function */
+ out_pa_data = NULL;
+#ifdef DEBUG
+ fprintf(stderr, "using module \"%s\" (%d), flags = %d\n",
+ module->name, module->pa_type, module->flags);
+#endif
+ ret = module->client_process(kcontext,
+ module->module_context,
+ &module->request_context,
+ request,
+ encoded_request_body,
+ encoded_previous_request,
+ in_padata,
+ prompter, prompter_data,
+ gak_fct, salt, s2kparams, gak_data,
+ as_key,
+ &out_pa_data);
+ /* Make note of the module's flags and status. */
+ *module_flags = module->flags;
+ *module_ret = ret;
+ /* Save the new preauth data item. */
+ if (out_pa_data != NULL) {
+ ret = grow_pa_list(out_pa_list, out_pa_list_size, out_pa_data);
+ if (ret != 0)
+ return ret;
+ }
+ break;
+ }
+ if (i >= preauth_context->n_modules) {
+ return ENOENT;
+ }
+ return 0;
+}
static
krb5_error_code pa_salt(krb5_context context,
},
};
+/*
+ * If one of the modules can adjust its AS_REQ data using the contents of the
+ * err_reply, return 0. If it's the sort of correction which requires that we
+ * ask the user another question, we let the calling application deal with it.
+ */
+krb5_error_code
+krb5_do_preauth_tryagain(krb5_context kcontext,
+ krb5_preauth_context **preauth_context,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_error *err_reply, krb5_pa_data **padata)
+{
+ krb5_error_code ret;
+ krb5_pa_data *out_padata;
+ krb5_preauth_context *context;
+ struct _krb5_preauth_context_module *module;
+ int i, j;
+
+ ret = KRB_ERR_GENERIC;
+ if (preauth_context == NULL) {
+ return KRB_ERR_GENERIC;
+ }
+ context = *preauth_context;
+ if (context == NULL) {
+ return KRB_ERR_GENERIC;
+ }
+
+ for (i = 0; padata[i]->pa_type != 0; i++) {
+ out_padata = NULL;
+ for (j = 0; j < context->n_modules; j++) {
+ module = &context->modules[j];
+ if (module->pa_type != padata[i]->pa_type) {
+ continue;
+ }
+ if (module->client_tryagain == NULL) {
+ continue;
+ }
+ if ((*module->client_tryagain)(kcontext,
+ module->module_context,
+ module->request_context,
+ request,
+ encoded_request_body,
+ err_reply,
+ padata[i],
+ &out_padata) == 0) {
+ if (out_padata != NULL) {
+ if (padata[i]->contents != NULL)
+ free(padata[i]->contents);
+ free(padata[i]);
+ padata[i] = out_padata;
+ return 0;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
krb5_error_code
krb5_do_preauth(krb5_context context,
+ krb5_preauth_context **preauth_context,
krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
krb5_pa_data **in_padata, krb5_pa_data ***out_padata,
krb5_data *salt, krb5_data *s2kparams,
krb5_enctype *etype,
default:
;
}
- for (j=0; pa_types[j].type >= 0; j++) {
+ /* Try the internally-provided preauth type list. */
+ if (!realdone) for (j=0; pa_types[j].type >= 0; j++) {
if ((in_padata[i]->pa_type == pa_types[j].type) &&
(pa_types[j].flags & paorder[h])) {
+#ifdef DEBUG
+ fprintf (stderr, "calling internal function for pa_type "
+ "%d, flag %d\n", pa_types[j].type, paorder[h]);
+#endif
out_pa = NULL;
if ((ret = ((*pa_types[j].fct)(context, request,
goto cleanup;
}
- if (out_pa) {
- if (out_pa_list == NULL) {
- if ((out_pa_list =
- (krb5_pa_data **)
- malloc(2*sizeof(krb5_pa_data *)))
- == NULL) {
- ret = ENOMEM;
- goto cleanup;
- }
- } else {
- if ((out_pa_list =
- (krb5_pa_data **)
- realloc(out_pa_list,
- (out_pa_list_size+2)*
- sizeof(krb5_pa_data *)))
- == NULL) {
- /* XXX this will leak the pointers which
- have already been allocated. oh well. */
- ret = ENOMEM;
- goto cleanup;
- }
- }
-
- out_pa_list[out_pa_list_size++] = out_pa;
+ ret = grow_pa_list(&out_pa_list, &out_pa_list_size,
+ out_pa);
+ if (ret != 0) {
+ goto cleanup;
}
if (paorder[h] == PA_REAL)
realdone = 1;
}
}
+
+ /* Try to use plugins now. */
+ if ((!realdone) && (preauth_context != NULL)) {
+ if (*preauth_context == NULL) {
+ krb5_init_preauth_context(context, preauth_context);
+ }
+ if (*preauth_context != NULL) {
+ int module_ret, module_flags;
+#ifdef DEBUG
+ fprintf (stderr, "trying modules for pa_type %d, flag %d\n",
+ in_padata[i]->pa_type, paorder[h]);
+#endif
+ ret = krb5_run_preauth_plugins(context,
+ *preauth_context,
+ paorder[h],
+ request,
+ encoded_request_body,
+ encoded_previous_request,
+ in_padata[i],
+ prompter,
+ prompter_data,
+ gak_fct,
+ salt, s2kparams,
+ gak_data,
+ as_key,
+ &out_pa_list,
+ &out_pa_list_size,
+ &module_ret,
+ &module_flags);
+ if (ret == 0) {
+ if (module_ret == 0) {
+ if (paorder[h] == PA_REAL) {
+ realdone = 1;
+ }
+ }
+ }
+ }
+ }
}
}
- if (out_pa_list)
- out_pa_list[out_pa_list_size++] = NULL;
-
*out_padata = out_pa_list;
if (etype_info)
krb5_free_etype_info(context, etype_info);
ctx->vtbl = 0;
PLUGIN_DIR_INIT(&ctx->libkrb5_plugins);
+ PLUGIN_DIR_INIT(&ctx->preauth_plugins);
retval = os_init_paths(ctx, kdc);
/*
ctx->profile = 0;
}
+ krb5int_close_plugin_dirs (&ctx->preauth_plugins);
krb5int_close_plugin_dirs (&ctx->libkrb5_plugins);
#ifdef _WIN32
--- /dev/null
+thisconfigdir=.
+myfulldir=plugins/preauth/cksum_body
+mydir=.
+BUILDTOP=$(REL)..$(S)..$(S)..
+KRB5_RUN_ENV = @KRB5_RUN_ENV@
+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ;
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
+DEFS=@DEFS@
+
+LOCALINCLUDES = -I../../../include/krb5
+
+LIBBASE=cksum_body
+LIBMAJOR=0
+LIBMINOR=0
+SO_EXT=.so
+RELDIR=../plugins/preauth/cksum_body
+# Depends on libk5crypto and libkrb5
+SHLIB_EXPDEPS = \
+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+ $(TOPLIBD)/libkrb5$(SHLIBEXT)
+SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(LIBS)
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
+STOBJLISTS=OBJS.ST
+STLIBOBJS=src/cksum_body.o
+
+SRCS= $(srcdir)/src/cksum_body.c
+
+all-unix:: $(LIBBASE)$(SO_EXT)
+install-unix:: install-libs
+clean-unix:: clean-libs clean-libobjs
+
+clean::
+ $(RM) lib$(LIBBASE)$(SO_EXT)
+
+@libnover_frag@
+@libobj_frag@
+
+# +++ Dependency line eater +++
--- /dev/null
+preauthentication0
--- /dev/null
+K5_AC_INIT(configure.in)
+enable_shared=yes
+build_dynobj=yes
+CONFIG_RULES
+
+AC_CHECK_HEADERS(errno.h string.h)
+
+KRB5_RUN_FLAGS
+dnl The following is for check...
+KRB5_BUILD_PROGRAM
+KRB5_BUILD_LIBOBJS
+KRB5_BUILD_LIBRARY_WITH_DEPS
+AC_CONFIG_HEADERS(config.h)
+V5_AC_OUTPUT_MAKEFILE
--- /dev/null
+/*
+ * Copyright (C) 2006 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Red Hat, Inc., nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Checksum the request body with the user's long-term key.
+ *
+ * The e-data from the KDC is a list of network-byte-order 32-bit integers
+ * listing key types which the KDC has for the user.
+ *
+ * The client uses one of these key types to generate a checksum over the body
+ * of the request, and includes the checksum in the AS-REQ as preauthentication
+ * data.
+ *
+ * The AS-REP carries no preauthentication data for this scheme.
+ */
+
+#ident "$Id$"
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <arpa/inet.h>
+#include <stdio.h>
+
+#include <krb5/krb5.h>
+#include <krb5/preauth_plugin.h>
+
+/* This is not a standardized value. It's defined here only to make it easier
+ * to change in this module. */
+#define KRB5_PADATA_CKSUM_BODY_REQ 130
+
+struct server_stats{
+ int successes, failures;
+};
+
+static int
+client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
+{
+ return PA_REAL;
+}
+
+static krb5_error_code
+client_process(krb5_context kcontext,
+ void *client_module_context,
+ void **client_request_context,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *pa_data,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ krb5_data *salt, krb5_data *s2kparams,
+ void *gak_data,
+ krb5_keyblock *as_key,
+ krb5_pa_data **out_pa_data)
+{
+ krb5_pa_data *send_pa;
+ krb5_checksum checksum;
+ krb5_enctype enctype;
+ krb5_cksumtype *cksumtypes;
+ krb5_error_code status;
+ krb5_int32 cksumtype, *enctypes;
+ unsigned int i, n_enctypes, cksumtype_count;
+
+ memset(&checksum, 0, sizeof(checksum));
+
+ /* Get the user's long-term key if we haven't asked for it yet. Try
+ * all of the encryption types which the server supports. */
+ if (as_key->length == 0) {
+ if ((pa_data != NULL) && (pa_data->length >= 4)) {
+#ifdef DEBUG
+ fprintf(stderr, "%d bytes of preauth data.\n", pa_data->length);
+#endif
+ n_enctypes = pa_data->length / 4;
+ enctypes = (krb5_int32*) pa_data->contents;
+ } else {
+ n_enctypes = request->nktypes;
+ }
+ for (i = 0; i < n_enctypes; i++) {
+ if ((pa_data != NULL) && (pa_data->length >= 4)) {
+ memcpy(&enctype, pa_data->contents + 4 * i, 4);
+ enctype = ntohl(enctype);
+ } else {
+ enctype = request->ktype[i];
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Asking for AS key (type = %d).\n", enctype);
+#endif
+ status = (*gak_fct)(kcontext, request->client, enctype,
+ prompter, prompter_data,
+ salt, s2kparams, as_key, gak_data);
+ if (status == 0)
+ break;
+ }
+ if (status != 0)
+ return status;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Got AS key (type = %d).\n", as_key->enctype);
+#endif
+
+ /* Determine an appropriate checksum type for this key. */
+ cksumtype_count = 0;
+ cksumtypes = NULL;
+ status = krb5_c_keyed_checksum_types(kcontext, as_key->enctype,
+ &cksumtype_count, &cksumtypes);
+ if (status != 0)
+ return status;
+
+ /* Generate the checksum. */
+ for (i = 0; i < cksumtype_count; i++) {
+ status = krb5_c_make_checksum(kcontext, cksumtypes[i], as_key,
+ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ encoded_request_body,
+ &checksum);
+ if (status == 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Made checksum (type = %d, %d bytes).\n",
+ checksum.checksum_type, encoded_request_body->length);
+#endif
+ break;
+ }
+ }
+ cksumtype = htonl(cksumtypes[i]);
+ krb5_free_cksumtypes(kcontext, cksumtypes);
+ if (status != 0) {
+ if (checksum.length > 0)
+ krb5_free_checksum_contents(kcontext, &checksum);
+ return status;
+ }
+
+ /* Allocate the preauth data structure. */
+ send_pa = malloc(sizeof(krb5_pa_data));
+ if (send_pa == NULL) {
+ krb5_free_checksum_contents(kcontext, &checksum);
+ return ENOMEM;
+ }
+ send_pa->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
+ send_pa->length = 4 + checksum.length;
+ send_pa->contents = malloc(4 + checksum.length);
+ if (send_pa->contents == NULL) {
+ krb5_free_checksum_contents(kcontext, &checksum);
+ free(send_pa);
+ return ENOMEM;
+ }
+
+ /* Store the checksum. */
+ memcpy(send_pa->contents, &cksumtype, 4);
+ memcpy(send_pa->contents + 4, checksum.contents, checksum.length);
+ *out_pa_data = send_pa;
+
+ /* Clean up. */
+ krb5_free_checksum_contents(kcontext, &checksum);
+
+ return 0;
+}
+
+/* Initialize and tear down the server-side module, and do stat tracking. */
+static krb5_error_code
+server_init(krb5_context kcontext, krb5_preauthtype pa_type,
+ void **module_context)
+{
+ struct server_stats *stats;
+ stats = malloc(sizeof(struct server_stats));
+ if (stats == NULL)
+ return ENOMEM;
+ stats->successes = 0;
+ stats->failures = 0;
+ *module_context = stats;
+ return 0;
+}
+static void
+server_fini(krb5_context kcontext, krb5_preauthtype pa_type,
+ void *module_context)
+{
+ struct server_stats *stats;
+ stats = module_context;
+ if (stats != NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "Total %d clients failed pa_type %d, %d succeeded.\n",
+ stats->failures, pa_type, stats->successes);
+#endif
+ free(stats);
+ }
+}
+
+/* Obtain and return any preauthentication data (which is destined for the
+ * client) which matches type data->pa_type. */
+static krb5_error_code
+server_get_edata(krb5_context kcontext,
+ krb5_kdc_req *request,
+ struct _krb5_db_entry_new *client,
+ struct _krb5_db_entry_new *server,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_module_context,
+ krb5_pa_data *data)
+{
+ krb5_data *key_data;
+ krb5_keyblock *keys, *key;
+ krb5_int32 *enctypes, enctype;
+ int i;
+
+ /* Retrieve the client's keys. */
+ key_data = NULL;
+ if ((*server_get_entry_data)(kcontext, request, client,
+ krb5plugin_preauth_keys, &key_data) != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Error retrieving client keys.\n");
+#endif
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ }
+
+ /* Count which types of keys we've got, freeing the contents, which we
+ * don't need at this point. */
+ keys = (krb5_keyblock *) key_data->data;
+ key = NULL;
+ for (i = 0; keys[i].enctype != 0; i++)
+ krb5_free_keyblock_contents(kcontext, &keys[i]);
+
+ /* Return the list of encryption types. */
+ enctypes = malloc(i * 4);
+ if (enctypes == NULL) {
+ krb5_free_data(kcontext, key_data);
+ return ENOMEM;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Supported enctypes = {");
+#endif
+ for (i = 0; keys[i].enctype != 0; i++) {
+#ifdef DEBUG
+ fprintf(stderr, "%s%d", (i > 0) ? ", " : "", keys[i].enctype);
+#endif
+ enctype = htonl(keys[i].enctype);
+ memcpy(&enctypes[i], &enctype, 4);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "}.\n");
+#endif
+ data->pa_type = KRB5_PADATA_CKSUM_BODY_REQ;
+ data->length = (i * 4);
+ data->contents = (unsigned char *) enctypes;
+ krb5_free_data(kcontext, key_data);
+ return 0;
+}
+
+/* Verify a request from a client. */
+static krb5_error_code
+server_verify(krb5_context kcontext,
+ struct _krb5_db_entry_new *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_pa_data *data,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context)
+{
+ krb5_int32 cksumtype;
+ krb5_checksum checksum;
+ krb5_boolean valid;
+ krb5_data *key_data, *req_body;
+ krb5_keyblock *keys, *key;
+ size_t length;
+ int i;
+ unsigned int j, cksumtypes_count;
+ krb5_cksumtype *cksumtypes;
+ krb5_error_code status;
+ struct server_stats *stats;
+
+ stats = pa_module_context;
+
+ /* Verify the preauth data. Start with the checksum type. */
+ if (data->length < 4) {
+ stats->failures++;
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ memcpy(&cksumtype, data->contents, 4);
+ memset(&checksum, 0, sizeof(checksum));
+ checksum.checksum_type = ntohl(cksumtype);
+
+ /* Verify that the amount of data we have left is what we expect. */
+ if (krb5_c_checksum_length(kcontext, checksum.checksum_type,
+ &length) != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Error determining checksum size (type = %d). "
+ "Is it supported?\n", checksum.checksum_type);
+#endif
+ stats->failures++;
+ return KRB5KDC_ERR_SUMTYPE_NOSUPP;
+ }
+ if (data->length - 4 != length) {
+#ifdef DEBUG
+ fprintf(stderr, "Checksum size doesn't match client packet size.\n");
+#endif
+ stats->failures++;
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ checksum.length = length;
+
+ /* Pull up the client's keys. */
+ key_data = NULL;
+ if ((*server_get_entry_data)(kcontext, request, client,
+ krb5plugin_preauth_keys, &key_data) != 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Error retrieving client keys.\n");
+#endif
+ stats->failures++;
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Find the key which would have been used to generate the checksum. */
+ keys = (krb5_keyblock *) key_data->data;
+ key = NULL;
+ for (i = 0; keys[i].enctype != 0; i++) {
+ key = &keys[i];
+ cksumtypes_count = 0;
+ cksumtypes = NULL;
+ if (krb5_c_keyed_checksum_types(kcontext, key->enctype,
+ &cksumtypes_count, &cksumtypes) != 0)
+ continue;
+ for (j = 0; j < cksumtypes_count; j++) {
+ if (cksumtypes[j] == checksum.checksum_type)
+ break;
+ }
+ if (cksumtypes != NULL)
+ krb5_free_cksumtypes(kcontext, cksumtypes);
+ if (j < cksumtypes_count) {
+#ifdef DEBUG
+ fprintf(stderr, "Found checksum key.\n");
+#endif
+ break;
+ }
+ }
+ if ((key == NULL) || (key->enctype == 0)) {
+ for (i = 0; keys[i].enctype != 0; i++)
+ krb5_free_keyblock_contents(kcontext, &keys[i]);
+ krb5_free_data(kcontext, key_data);
+ stats->failures++;
+ return KRB5KDC_ERR_SUMTYPE_NOSUPP;
+ }
+
+ /* Save a copy of the key. */
+ if (krb5_copy_keyblock(kcontext, &keys[i], &key) != 0) {
+ for (i = 0; keys[i].enctype != 0; i++)
+ krb5_free_keyblock_contents(kcontext, &keys[i]);
+ krb5_free_data(kcontext, key_data);
+ stats->failures++;
+ return KRB5KDC_ERR_SUMTYPE_NOSUPP;
+ }
+ for (i = 0; keys[i].enctype != 0; i++)
+ krb5_free_keyblock_contents(kcontext, &keys[i]);
+ krb5_free_data(kcontext, key_data);
+
+ /* Rebuild a copy of the client's request-body. If we were serious
+ * about doing this with any chance of working interoperability, we'd
+ * extract the structure directly from the req_pkt structure. This
+ * will probably work if it's us on both ends, though. */
+ req_body = NULL;
+ if ((*server_get_entry_data)(kcontext, request, client,
+ krb5plugin_preauth_request_body,
+ &req_body) != 0) {
+ krb5_free_keyblock(kcontext, key);
+ stats->failures++;
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "AS key type %d, checksum type %d, %d bytes.\n",
+ key->enctype, checksum.checksum_type, req_body->length);
+#endif
+
+ /* Verify the checksum itself. */
+ checksum.contents = data->contents + 4;
+ valid = FALSE;
+ status = krb5_c_verify_checksum(kcontext, key,
+ KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
+ req_body, &checksum, &valid);
+
+ /* Clean up. */
+ krb5_free_data(kcontext, req_body);
+ krb5_free_keyblock(kcontext, key);
+
+ /* Evaluate our results. */
+ if ((status != 0) || (!valid)) {
+#ifdef DEBUG
+ if (status != 0) {
+ fprintf(stderr, "Error in checksum verification.\n");
+ } else {
+ fprintf(stderr, "Checksum mismatch.\n");
+ }
+#endif
+ stats->failures++;
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ /* Note that preauthentication succeeded. */
+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+ stats->successes++;
+ return 0;
+}
+
+/* Create the response for a client. */
+static krb5_error_code
+server_return(krb5_context kcontext,
+ krb5_pa_data *padata,
+ struct _krb5_db_entry_new *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_kdc_rep *reply,
+ struct _krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context)
+{
+ /* We don't need to send data back on the return trip. */
+ *send_pa = NULL;
+ return 0;
+}
+
+static int
+server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
+{
+ return PA_SUFFICIENT;
+}
+
+static krb5_preauthtype supported_client_pa_types[] = {
+ KRB5_PADATA_CKSUM_BODY_REQ, 0,
+};
+static krb5_preauthtype supported_server_pa_types[] = {
+ KRB5_PADATA_CKSUM_BODY_REQ, 0,
+};
+
+struct krb5plugin_preauth_ftable_v0 preauthentication0 = {
+ "cksum_body",
+ &supported_client_pa_types[0],
+ &supported_server_pa_types[0],
+ NULL,
+ NULL,
+ NULL,
+ client_get_flags,
+ NULL,
+ client_process,
+ NULL,
+ server_init,
+ server_fini,
+ server_get_flags,
+ server_get_edata,
+ server_verify,
+ server_return,
+ NULL
+};
--- /dev/null
+thisconfigdir=.
+myfulldir=plugins/preauth/wpse
+mydir=.
+BUILDTOP=$(REL)..$(S)..$(S)..
+KRB5_RUN_ENV = @KRB5_RUN_ENV@
+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ;
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
+DEFS=@DEFS@
+
+LOCALINCLUDES = -I../../../include/krb5
+
+LIBBASE=wpse
+LIBMAJOR=0
+LIBMINOR=0
+SO_EXT=.so
+RELDIR=../plugins/preauth/wpse
+# Depends on libk5crypto and libkrb5
+SHLIB_EXPDEPS = \
+ $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+ $(TOPLIBD)/libkrb5$(SHLIBEXT)
+SHLIB_EXPLIBS= -lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(LIBS)
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
+STOBJLISTS=OBJS.ST
+STLIBOBJS=src/wpse.o
+
+SRCS= $(srcdir)/src/wpse.c
+
+all-unix:: $(LIBBASE)$(SO_EXT)
+install-unix:: install-libs
+clean-unix:: clean-libs clean-libobjs
+
+clean::
+ $(RM) lib$(LIBBASE)$(SO_EXT)
+
+@libnover_frag@
+@libobj_frag@
+
+# +++ Dependency line eater +++
--- /dev/null
+K5_AC_INIT(configure.in)
+enable_shared=yes
+build_dynobj=yes
+CONFIG_RULES
+
+AC_CHECK_HEADERS(errno.h string.h)
+
+KRB5_RUN_FLAGS
+dnl The following is for check...
+KRB5_BUILD_PROGRAM
+KRB5_BUILD_LIBOBJS
+KRB5_BUILD_LIBRARY_WITH_DEPS
+AC_CONFIG_HEADERS(config.h)
+V5_AC_OUTPUT_MAKEFILE
--- /dev/null
+/*
+ * Copyright (C) 2006 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Red Hat, Inc., nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Worst. Preauthentication. Scheme. Ever. */
+
+#ident "$Id$"
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <arpa/inet.h>
+#include <stdio.h>
+
+#include <krb5/krb5.h>
+#include <krb5/preauth_plugin.h>
+
+/* This is not a standardized value. It's defined here only to make it easier
+ * to change in this module. */
+#define KRB5_PADATA_WPSE_REQ 131
+
+static int
+client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
+{
+ return PA_REAL;
+}
+
+static krb5_error_code
+client_init(krb5_context kcontext, krb5_preauthtype pa_type, void **ctx)
+{
+ int *mctx;
+
+ mctx = malloc(sizeof(int));
+ if (mctx == NULL)
+ return ENOMEM;
+ *mctx = 0;
+ *ctx = mctx;
+ return 0;
+}
+
+static void
+client_fini(krb5_context kcontext, krb5_preauthtype pa_type, void *ctx)
+{
+ int *mctx;
+
+ mctx = ctx;
+ if (mctx) {
+#ifdef DEBUG
+ fprintf(stderr, "wpse module called total of %d times\n", *mctx);
+#endif
+ free(mctx);
+ }
+}
+
+static krb5_error_code
+client_process(krb5_context kcontext,
+ void *module_context,
+ void **request_context,
+ krb5_kdc_req *request,
+ krb5_data *encoded_request_body,
+ krb5_data *encoded_previous_request,
+ krb5_pa_data *pa_data,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ preauth_get_as_key_proc gak_fct,
+ krb5_data *salt, krb5_data *s2kparams,
+ void *gak_data,
+ krb5_keyblock *as_key,
+ krb5_pa_data **out_pa_data)
+{
+ krb5_pa_data *send_pa;
+ krb5_int32 nnonce, enctype;
+ krb5_keyblock *kb;
+ krb5_error_code status;
+ int *mctx;
+
+#ifdef DEBUG
+ fprintf(stderr, "%d bytes of preauthentication data (type %d)\n",
+ pa_data->length, pa_data->pa_type);
+#endif
+
+ mctx = module_context;
+ if (mctx) {
+ (*mctx)++;
+ }
+
+ if (pa_data->length == 0) {
+ /* Create preauth data. */
+ send_pa = malloc(sizeof(krb5_pa_data));
+ if (send_pa == NULL)
+ return ENOMEM;
+ send_pa->pa_type = KRB5_PADATA_WPSE_REQ;
+ send_pa->length = 4;
+ send_pa->contents = malloc(4);
+ if (send_pa->contents == NULL) {
+ free(send_pa);
+ return ENOMEM;
+ }
+ /* Store the preauth data. */
+ nnonce = htonl(request->nonce);
+ memcpy(send_pa->contents, &nnonce, 4);
+ *out_pa_data = send_pa;
+ /* Allocate a context. Useful for verifying that we do in fact
+ * do per-request cleanup. */
+ if (*request_context == NULL)
+ *request_context = malloc(4);
+ } else {
+ /* A reply from the KDC. Conventionally this would be
+ * indicated by a different preauthentication type, but this
+ * mechanism/implementation doesn't do that. */
+ if (pa_data->length > 4) {
+ memcpy(&enctype, pa_data->contents, 4);
+ kb = NULL;
+ status = krb5_init_keyblock(kcontext, ntohl(enctype),
+ pa_data->length - 4, &kb);
+ if (status != 0)
+ return status;
+ memcpy(kb->contents, pa_data->contents + 4, pa_data->length - 4);
+#ifdef DEBUG
+ fprintf(stderr, "Recovered key type=%d, length=%d.\n",
+ kb->enctype, kb->length);
+#endif
+ status = krb5_copy_keyblock_contents(kcontext, kb, as_key);
+ krb5_free_keyblock(kcontext, kb);
+ return status;
+ }
+ return KRB5KRB_ERR_GENERIC;
+ }
+ return 0;
+}
+
+static void
+client_cleanup(krb5_context kcontext, void *module_context,
+ void **request_context)
+{
+ if (*request_context != NULL) {
+ free(*request_context);
+ *request_context = NULL;
+ }
+ return;
+}
+
+/* Free state. */
+static krb5_error_code
+server_free_pa_request_context(krb5_context kcontext, void *module_context,
+ void **request_context)
+{
+ if (*request_context != NULL) {
+ free(*request_context);
+ *request_context = NULL;
+ }
+ return 0;
+}
+
+/* Obtain and return any preauthentication data (which is destined for the
+ * client) which matches type data->pa_type. */
+static krb5_error_code
+server_get_edata(krb5_context kcontext,
+ krb5_kdc_req *request,
+ struct _krb5_db_entry_new *client,
+ struct _krb5_db_entry_new *server,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_module_context,
+ krb5_pa_data *data)
+{
+ /* Return zero bytes of data. */
+ data->length = 0;
+ data->contents = NULL;
+ return 0;
+}
+
+/* Verify a request from a client. */
+static krb5_error_code
+server_verify(krb5_context kcontext,
+ struct _krb5_db_entry_new *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_pa_data *data,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context)
+{
+ krb5_int32 nnonce;
+ /* Verify the preauth data. */
+ if (data->length != 4)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ memcpy(&nnonce, data->contents, 4);
+ nnonce = ntohl(nnonce);
+ if (memcmp(&nnonce, &request->nonce, 4) != 0)
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ /* Note that preauthentication succeeded. */
+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+ enc_tkt_reply->flags |= TKT_FLG_HW_AUTH;
+ /* Allocate a context. Useful for verifying that we do in fact do
+ * per-request cleanup. */
+ if (*pa_request_context == NULL)
+ *pa_request_context = malloc(4);
+ return 0;
+}
+
+/* Create the response for a client. */
+static krb5_error_code
+server_return(krb5_context kcontext,
+ krb5_pa_data *padata,
+ struct _krb5_db_entry_new *client,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_kdc_rep *reply,
+ struct _krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa,
+ preauth_get_entry_data_proc server_get_entry_data,
+ void *pa_module_context,
+ void **pa_request_context)
+{
+ /* This module does a couple of dumb things. It tags its reply with
+ * the same type as the initial challenge (expecting the client to sort
+ * out whether there's anything useful in there). Oh, and it replaces
+ * the AS reply key with one which is sent in the clear. */
+ krb5_keyblock *kb;
+ krb5_int32 enctype;
+ int i;
+
+ *send_pa = NULL;
+
+ /* We'll want a key with the first supported enctype. */
+ for (i = 0; i < request->nktypes; i++) {
+ kb = NULL;
+ if (krb5_init_keyblock(kcontext, request->ktype[i], 0, &kb) == 0) {
+ break;
+ }
+ }
+ if (i >= request->nktypes) {
+ /* No matching cipher type found. */
+ return 0;
+ }
+
+ /* Randomize a key and save it for the client. */
+ if (krb5_c_make_random_key(kcontext, request->ktype[i], kb) != 0) {
+ krb5_free_keyblock(kcontext, kb);
+ return 0;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Generated random key, type=%d, length=%d.\n",
+ kb->enctype, kb->length);
+#endif
+
+ *send_pa = malloc(sizeof(krb5_pa_data));
+ if (*send_pa == NULL) {
+ krb5_free_keyblock(kcontext, kb);
+ return ENOMEM;
+ }
+ (*send_pa)->pa_type = KRB5_PADATA_WPSE_REQ;
+ (*send_pa)->length = 4 + kb->length;
+ (*send_pa)->contents = malloc(4 + kb->length);
+ if ((*send_pa)->contents == NULL) {
+ free(*send_pa);
+ *send_pa = NULL;
+ krb5_free_keyblock(kcontext, kb);
+ return ENOMEM;
+ }
+
+ /* Store the preauth data. */
+ enctype = htonl(kb->enctype);
+ memcpy((*send_pa)->contents, &enctype, 4);
+ memcpy((*send_pa)->contents + 4, kb->contents, kb->length);
+ krb5_copy_keyblock_contents(kcontext, kb, encrypting_key);
+
+ /* Clean up. */
+ krb5_free_keyblock(kcontext, kb);
+
+ return 0;
+}
+
+static int
+server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
+{
+ return PA_HARDWARE | PA_REPLACES_KEY;
+}
+
+static krb5_preauthtype supported_client_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
+static krb5_preauthtype supported_server_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
+
+struct krb5plugin_preauth_ftable_v0 preauthentication0 = {
+ "wpse",
+ &supported_client_pa_types[0],
+ &supported_server_pa_types[0],
+ NULL,
+ client_init,
+ client_fini,
+ client_get_flags,
+ client_cleanup,
+ client_process,
+ NULL,
+ NULL,
+ NULL,
+ server_get_flags,
+ server_get_edata,
+ server_verify,
+ server_return,
+ server_free_pa_request_context,
+};
--- /dev/null
+preauthentication0