5164 lines
163 KiB
Diff
5164 lines
163 KiB
Diff
From 9a0bb5ada335017c5da35cb41333632ae5577a93 Mon Sep 17 00:00:00 2001
|
|
From: Greg Hudson <ghudson@mit.edu>
|
|
Date: Tue, 15 Jan 2013 11:11:27 -0500
|
|
Subject: [PATCH 1/4] Add internal KDC_DIR macro
|
|
|
|
Define KDC_DIR in osconf.hin and use it for paths within the KDC
|
|
directory.
|
|
---
|
|
src/include/osconf.hin | 21 +++++++++++----------
|
|
1 file changed, 11 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/src/include/osconf.hin b/src/include/osconf.hin
|
|
index c3a33c2..90ab86d 100644
|
|
--- a/src/include/osconf.hin
|
|
+++ b/src/include/osconf.hin
|
|
@@ -58,14 +58,15 @@
|
|
#define DEFAULT_PLUGIN_BASE_DIR "@LIBDIR/krb5/plugins"
|
|
#define PLUGIN_EXT "@DYNOBJEXT"
|
|
|
|
-#define DEFAULT_KDB_FILE "@LOCALSTATEDIR/krb5kdc/principal"
|
|
-#define DEFAULT_KEYFILE_STUB "@LOCALSTATEDIR/krb5kdc/.k5."
|
|
-#define KRB5_DEFAULT_ADMIN_ACL "@LOCALSTATEDIR/krb5kdc/krb5_adm.acl"
|
|
+#define KDC_DIR "@LOCALSTATEDIR/krb5kdc"
|
|
+#define DEFAULT_KDB_FILE KDC_DIR "/principal"
|
|
+#define DEFAULT_KEYFILE_STUB KDC_DIR "/.k5."
|
|
+#define KRB5_DEFAULT_ADMIN_ACL KDC_DIR "/krb5_adm.acl"
|
|
/* Used by old admin server */
|
|
-#define DEFAULT_ADMIN_ACL "@LOCALSTATEDIR/krb5kdc/kadm_old.acl"
|
|
+#define DEFAULT_ADMIN_ACL KDC_DIR "/kadm_old.acl"
|
|
|
|
/* Location of KDC profile */
|
|
-#define DEFAULT_KDC_PROFILE "@LOCALSTATEDIR/krb5kdc/kdc.conf"
|
|
+#define DEFAULT_KDC_PROFILE KDC_DIR "/kdc.conf"
|
|
#define KDC_PROFILE_ENV "KRB5_KDC_PROFILE"
|
|
|
|
#if TARGET_OS_MAC
|
|
@@ -93,8 +94,8 @@
|
|
/*
|
|
* Defaults for the KADM5 admin system.
|
|
*/
|
|
-#define DEFAULT_KADM5_KEYTAB "@LOCALSTATEDIR/krb5kdc/kadm5.keytab"
|
|
-#define DEFAULT_KADM5_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kadm5.acl"
|
|
+#define DEFAULT_KADM5_KEYTAB KDC_DIR "/kadm5.keytab"
|
|
+#define DEFAULT_KADM5_ACL_FILE KDC_DIR "/kadm5.acl"
|
|
#define DEFAULT_KADM5_PORT 749 /* assigned by IANA */
|
|
|
|
#define KRB5_DEFAULT_SUPPORTED_ENCTYPES \
|
|
@@ -116,12 +117,12 @@
|
|
* krb5 slave support follows
|
|
*/
|
|
|
|
-#define KPROP_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/slave_datatrans"
|
|
-#define KPROPD_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/from_master"
|
|
+#define KPROP_DEFAULT_FILE KDC_DIR "/slave_datatrans"
|
|
+#define KPROPD_DEFAULT_FILE KDC_DIR "/from_master"
|
|
#define KPROPD_DEFAULT_KDB5_UTIL "@SBINDIR/kdb5_util"
|
|
#define KPROPD_DEFAULT_KPROP "@SBINDIR/kprop"
|
|
#define KPROPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE
|
|
-#define KPROPD_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kpropd.acl"
|
|
+#define KPROPD_ACL_FILE KDC_DIR "/kpropd.acl"
|
|
|
|
/*
|
|
* GSS mechglue
|
|
--
|
|
1.8.2.1
|
|
|
|
|
|
From 4ba748915908a45564d2e8a098cf107e39de3315 Mon Sep 17 00:00:00 2001
|
|
From: Nathaniel McCallum <npmccallum@redhat.com>
|
|
Date: Tue, 9 Apr 2013 11:17:04 -0400
|
|
Subject: [PATCH 2/4] add k5memdup()
|
|
|
|
---
|
|
src/include/k5-int.h | 11 +++++++++++
|
|
1 file changed, 11 insertions(+)
|
|
|
|
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
|
|
index 75e6783..7b5ab2c 100644
|
|
--- a/src/include/k5-int.h
|
|
+++ b/src/include/k5-int.h
|
|
@@ -2600,6 +2600,17 @@ k5alloc(size_t len, krb5_error_code *code)
|
|
return ptr;
|
|
}
|
|
|
|
+/* Return a copy of the len bytes of memory at in; set *code to 0 or ENOMEM. */
|
|
+static inline void *
|
|
+k5memdup(const void *in, size_t len, krb5_error_code *code)
|
|
+{
|
|
+ void *ptr = k5alloc(len, code);
|
|
+
|
|
+ if (ptr != NULL)
|
|
+ memcpy(ptr, in, len);
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
krb5_error_code KRB5_CALLCONV
|
|
krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
|
|
krb5_ccache ccache,
|
|
--
|
|
1.8.2.1
|
|
|
|
|
|
From f98e3f2569996d84c968852b50f76173203e568f Mon Sep 17 00:00:00 2001
|
|
From: Nathaniel McCallum <npmccallum@redhat.com>
|
|
Date: Thu, 4 Apr 2013 13:39:21 -0400
|
|
Subject: [PATCH 3/4] add libkrad
|
|
|
|
---
|
|
src/configure.in | 2 +-
|
|
src/include/Makefile.in | 1 +
|
|
src/include/krad.h | 267 ++++++++++++++++++++++
|
|
src/lib/Makefile.in | 2 +-
|
|
src/lib/krad/Makefile.in | 74 ++++++
|
|
src/lib/krad/attr.c | 317 ++++++++++++++++++++++++++
|
|
src/lib/krad/attrset.c | 244 ++++++++++++++++++++
|
|
src/lib/krad/client.c | 335 ++++++++++++++++++++++++++++
|
|
src/lib/krad/code.c | 111 +++++++++
|
|
src/lib/krad/deps | 156 +++++++++++++
|
|
src/lib/krad/internal.h | 155 +++++++++++++
|
|
src/lib/krad/libkrad.exports | 23 ++
|
|
src/lib/krad/packet.c | 470 +++++++++++++++++++++++++++++++++++++++
|
|
src/lib/krad/remote.c | 519 +++++++++++++++++++++++++++++++++++++++++++
|
|
src/lib/krad/t_attr.c | 89 ++++++++
|
|
src/lib/krad/t_attrset.c | 98 ++++++++
|
|
src/lib/krad/t_client.c | 126 +++++++++++
|
|
src/lib/krad/t_code.c | 54 +++++
|
|
src/lib/krad/t_daemon.h | 92 ++++++++
|
|
src/lib/krad/t_daemon.py | 76 +++++++
|
|
src/lib/krad/t_packet.c | 194 ++++++++++++++++
|
|
src/lib/krad/t_remote.c | 170 ++++++++++++++
|
|
src/lib/krad/t_test.c | 50 +++++
|
|
src/lib/krad/t_test.h | 60 +++++
|
|
24 files changed, 3683 insertions(+), 2 deletions(-)
|
|
create mode 100644 src/include/krad.h
|
|
create mode 100644 src/lib/krad/Makefile.in
|
|
create mode 100644 src/lib/krad/attr.c
|
|
create mode 100644 src/lib/krad/attrset.c
|
|
create mode 100644 src/lib/krad/client.c
|
|
create mode 100644 src/lib/krad/code.c
|
|
create mode 100644 src/lib/krad/deps
|
|
create mode 100644 src/lib/krad/internal.h
|
|
create mode 100644 src/lib/krad/libkrad.exports
|
|
create mode 100644 src/lib/krad/packet.c
|
|
create mode 100644 src/lib/krad/remote.c
|
|
create mode 100644 src/lib/krad/t_attr.c
|
|
create mode 100644 src/lib/krad/t_attrset.c
|
|
create mode 100644 src/lib/krad/t_client.c
|
|
create mode 100644 src/lib/krad/t_code.c
|
|
create mode 100644 src/lib/krad/t_daemon.h
|
|
create mode 100644 src/lib/krad/t_daemon.py
|
|
create mode 100644 src/lib/krad/t_packet.c
|
|
create mode 100644 src/lib/krad/t_remote.c
|
|
create mode 100644 src/lib/krad/t_test.c
|
|
create mode 100644 src/lib/krad/t_test.h
|
|
|
|
diff --git a/src/configure.in b/src/configure.in
|
|
index faf93a1..d8676e5 100644
|
|
--- a/src/configure.in
|
|
+++ b/src/configure.in
|
|
@@ -1318,7 +1318,7 @@ dnl lib/krb5/ccache/ccapi
|
|
lib/rpc lib/rpc/unit-test
|
|
|
|
lib/kadm5 lib/kadm5/clnt lib/kadm5/srv lib/kadm5/unit-test
|
|
-
|
|
+ lib/krad
|
|
lib/apputils
|
|
|
|
dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
|
|
diff --git a/src/include/Makefile.in b/src/include/Makefile.in
|
|
index c69b809..869b04b 100644
|
|
--- a/src/include/Makefile.in
|
|
+++ b/src/include/Makefile.in
|
|
@@ -145,5 +145,6 @@ install-headers-unix install:: krb5/krb5.h profile.h
|
|
$(INSTALL_DATA) $(srcdir)/krb5/kadm5_hook_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)kadm5_hook_plugin.h
|
|
$(INSTALL_DATA) profile.h $(DESTDIR)$(KRB5_INCDIR)$(S)profile.h
|
|
$(INSTALL_DATA) $(srcdir)/gssapi.h $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi.h
|
|
+ $(INSTALL_DATA) $(srcdir)/krad.h $(DESTDIR)$(KRB5_INCDIR)/krad.h
|
|
|
|
depend:: krb5/krb5.h $(BUILT_HEADERS)
|
|
diff --git a/src/include/krad.h b/src/include/krad.h
|
|
new file mode 100644
|
|
index 0000000..e6d5766
|
|
--- /dev/null
|
|
+++ b/src/include/krad.h
|
|
@@ -0,0 +1,267 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * This API is not considered as stable as the main krb5 API.
|
|
+ *
|
|
+ * - We may make arbitrary incompatible changes between feature releases
|
|
+ * (e.g. from 1.12 to 1.13).
|
|
+ * - We will make some effort to avoid making incompatible changes for
|
|
+ * bugfix releases, but will make them if necessary.
|
|
+ */
|
|
+
|
|
+#ifndef KRAD_H_
|
|
+#define KRAD_H_
|
|
+
|
|
+#include <krb5.h>
|
|
+#include <verto.h>
|
|
+#include <stddef.h>
|
|
+#include <stdio.h>
|
|
+
|
|
+#define KRAD_PACKET_SIZE_MAX 4096
|
|
+
|
|
+#define KRAD_SERVICE_TYPE_LOGIN 1
|
|
+#define KRAD_SERVICE_TYPE_FRAMED 2
|
|
+#define KRAD_SERVICE_TYPE_CALLBACK_LOGIN 3
|
|
+#define KRAD_SERVICE_TYPE_CALLBACK_FRAMED 4
|
|
+#define KRAD_SERVICE_TYPE_OUTBOUND 5
|
|
+#define KRAD_SERVICE_TYPE_ADMINISTRATIVE 6
|
|
+#define KRAD_SERVICE_TYPE_NAS_PROMPT 7
|
|
+#define KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY 8
|
|
+#define KRAD_SERVICE_TYPE_CALLBACK_NAS_PROMPT 9
|
|
+#define KRAD_SERVICE_TYPE_CALL_CHECK 10
|
|
+#define KRAD_SERVICE_TYPE_CALLBACK_ADMINISTRATIVE 11
|
|
+
|
|
+typedef struct krad_attrset_st krad_attrset;
|
|
+typedef struct krad_packet_st krad_packet;
|
|
+typedef struct krad_client_st krad_client;
|
|
+typedef unsigned char krad_code;
|
|
+typedef unsigned char krad_attr;
|
|
+
|
|
+/* Called when a response is received or the request times out. */
|
|
+typedef void
|
|
+(*krad_cb)(krb5_error_code retval, const krad_packet *request,
|
|
+ const krad_packet *response, void *data);
|
|
+
|
|
+/*
|
|
+ * Called to iterate over a set of requests. Either the callback will be
|
|
+ * called until it returns NULL, or it will be called with cancel = TRUE to
|
|
+ * terminate in the middle of an iteration.
|
|
+ */
|
|
+typedef const krad_packet *
|
|
+(*krad_packet_iter_cb)(void *data, krb5_boolean cancel);
|
|
+
|
|
+/*
|
|
+ * Code
|
|
+ */
|
|
+
|
|
+/* Convert a code name to its number. Only works for codes defined
|
|
+ * by RFC 2875 or 2882. Returns 0 if the name was not found. */
|
|
+krad_code
|
|
+krad_code_name2num(const char *name);
|
|
+
|
|
+/* Convert a code number to its name. Only works for attributes defined
|
|
+ * by RFC 2865 or 2882. Returns NULL if the name was not found. */
|
|
+const char *
|
|
+krad_code_num2name(krad_code code);
|
|
+
|
|
+/*
|
|
+ * Attribute
|
|
+ */
|
|
+
|
|
+/* Convert an attribute name to its number. Only works for attributes defined
|
|
+ * by RFC 2865. Returns 0 if the name was not found. */
|
|
+krad_attr
|
|
+krad_attr_name2num(const char *name);
|
|
+
|
|
+/* Convert an attribute number to its name. Only works for attributes defined
|
|
+ * by RFC 2865. Returns NULL if the name was not found. */
|
|
+const char *
|
|
+krad_attr_num2name(krad_attr type);
|
|
+
|
|
+/*
|
|
+ * Attribute set
|
|
+ */
|
|
+
|
|
+/* Create a new attribute set. */
|
|
+krb5_error_code
|
|
+krad_attrset_new(krb5_context ctx, krad_attrset **set);
|
|
+
|
|
+/* Create a deep copy of an attribute set. */
|
|
+krb5_error_code
|
|
+krad_attrset_copy(const krad_attrset *set, krad_attrset **copy);
|
|
+
|
|
+/* Free an attribute set. */
|
|
+void
|
|
+krad_attrset_free(krad_attrset *set);
|
|
+
|
|
+/* Add an attribute to a set. */
|
|
+krb5_error_code
|
|
+krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data);
|
|
+
|
|
+/* Add a four-octet unsigned number attribute to the given set. */
|
|
+krb5_error_code
|
|
+krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num);
|
|
+
|
|
+/* Delete the specified attribute. */
|
|
+void
|
|
+krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx);
|
|
+
|
|
+/* Get the specified attribute. */
|
|
+const krb5_data *
|
|
+krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx);
|
|
+
|
|
+/*
|
|
+ * Packet
|
|
+ */
|
|
+
|
|
+/* Determine the bytes needed from the socket to get the whole packet. Don't
|
|
+ * cache the return value as it can change! Returns -1 on EBADMSG. */
|
|
+ssize_t
|
|
+krad_packet_bytes_needed(const krb5_data *buffer);
|
|
+
|
|
+/* Free a packet. */
|
|
+void
|
|
+krad_packet_free(krad_packet *pkt);
|
|
+
|
|
+/*
|
|
+ * Create a new request packet.
|
|
+ *
|
|
+ * This function takes the attributes specified in set and converts them into a
|
|
+ * radius packet. The packet will have a randomized id. If cb is not NULL, it
|
|
+ * will be called passing data as the argument to iterate over a set of
|
|
+ * outstanding requests. In this case, the id will be both random and unique
|
|
+ * across the set of requests.
|
|
+ */
|
|
+krb5_error_code
|
|
+krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
|
|
+ const krad_attrset *set, krad_packet_iter_cb cb,
|
|
+ void *data, krad_packet **request);
|
|
+
|
|
+/*
|
|
+ * Create a new response packet.
|
|
+ *
|
|
+ * This function is similar to krad_packet_new_requst() except that it crafts a
|
|
+ * packet in response to a request packet. This new packet will borrow values
|
|
+ * from the request such as the id and the authenticator.
|
|
+ */
|
|
+krb5_error_code
|
|
+krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
|
|
+ const krad_attrset *set, const krad_packet *request,
|
|
+ krad_packet **response);
|
|
+
|
|
+/*
|
|
+ * Decode a request radius packet from krb5_data.
|
|
+ *
|
|
+ * The resulting decoded packet will be a request packet stored in *reqpkt.
|
|
+ *
|
|
+ * If cb is NULL, *duppkt will always be NULL.
|
|
+ *
|
|
+ * If cb is not NULL, it will be called (with the data argument) to iterate
|
|
+ * over a set of requests currently being processed. In this case, if the
|
|
+ * packet is a duplicate of an already received request, the original request
|
|
+ * will be set in *duppkt.
|
|
+ */
|
|
+krb5_error_code
|
|
+krad_packet_decode_request(krb5_context ctx, const char *secret,
|
|
+ const krb5_data *buffer, krad_packet_iter_cb cb,
|
|
+ void *data, const krad_packet **duppkt,
|
|
+ krad_packet **reqpkt);
|
|
+
|
|
+/*
|
|
+ * Decode a response radius packet from krb5_data.
|
|
+ *
|
|
+ * The resulting decoded packet will be a response packet stored in *rsppkt.
|
|
+ *
|
|
+ * If cb is NULL, *reqpkt will always be NULL.
|
|
+ *
|
|
+ * If cb is not NULL, it will be called (with the data argument) to iterate
|
|
+ * over a set of requests awaiting responses. In this case, if the response
|
|
+ * packet matches one of these requests, the original request will be set in
|
|
+ * *reqpkt.
|
|
+ */
|
|
+krb5_error_code
|
|
+krad_packet_decode_response(krb5_context ctx, const char *secret,
|
|
+ const krb5_data *buffer, krad_packet_iter_cb cb,
|
|
+ void *data, const krad_packet **reqpkt,
|
|
+ krad_packet **rsppkt);
|
|
+
|
|
+/* Encode packet. */
|
|
+const krb5_data *
|
|
+krad_packet_encode(const krad_packet *pkt);
|
|
+
|
|
+/* Get the code for the given packet. */
|
|
+krad_code
|
|
+krad_packet_get_code(const krad_packet *pkt);
|
|
+
|
|
+/* Get the specified attribute. */
|
|
+const krb5_data *
|
|
+krad_packet_get_attr(const krad_packet *pkt, krad_attr type, size_t indx);
|
|
+
|
|
+/*
|
|
+ * Client
|
|
+ */
|
|
+
|
|
+/* Create a new client. */
|
|
+krb5_error_code
|
|
+krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **client);
|
|
+
|
|
+/* Free the client. */
|
|
+void
|
|
+krad_client_free(krad_client *client);
|
|
+
|
|
+/*
|
|
+ * Send a request to a radius server.
|
|
+ *
|
|
+ * The remote host may be specified by one of the following formats:
|
|
+ * - /path/to/unix.socket
|
|
+ * - IPv4
|
|
+ * - IPv4:port
|
|
+ * - IPv4:service
|
|
+ * - [IPv6]
|
|
+ * - [IPv6]:port
|
|
+ * - [IPv6]:service
|
|
+ * - hostname
|
|
+ * - hostname:port
|
|
+ * - hostname:service
|
|
+ *
|
|
+ * The timeout parameter (milliseconds) is the total timeout across all remote
|
|
+ * hosts (when DNS returns multiple entries) and all retries.
|
|
+ *
|
|
+ * The cb function will be called with the data argument when either a response
|
|
+ * is received or the request times out on all possible remote hosts.
|
|
+ *
|
|
+ * If the remote host is a unix domain socket, retries is ignored due to
|
|
+ * guaranteed delivery.
|
|
+ */
|
|
+krb5_error_code
|
|
+krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
|
|
+ const char *remote, const char *secret, int timeout,
|
|
+ size_t retries, krad_cb cb, void *data);
|
|
+
|
|
+#endif /* KRAD_H_ */
|
|
diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in
|
|
index 485db40..4dde514 100644
|
|
--- a/src/lib/Makefile.in
|
|
+++ b/src/lib/Makefile.in
|
|
@@ -1,5 +1,5 @@
|
|
mydir=lib
|
|
-SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils
|
|
+SUBDIRS=crypto krb5 gssapi rpc kdb kadm5 apputils krad
|
|
WINSUBDIRS=crypto krb5 gssapi
|
|
BUILDTOP=$(REL)..
|
|
|
|
diff --git a/src/lib/krad/Makefile.in b/src/lib/krad/Makefile.in
|
|
new file mode 100644
|
|
index 0000000..75431a0
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/Makefile.in
|
|
@@ -0,0 +1,74 @@
|
|
+mydir=lib$(S)krad
|
|
+BUILDTOP=$(REL)..$(S)..
|
|
+RELDIR=krad
|
|
+
|
|
+RUN_SETUP=@KRB5_RUN_ENV@
|
|
+PROG_LIBPATH=-L$(TOPLIBD)
|
|
+PROG_RPATH=$(KRB5_LIBDIR)
|
|
+
|
|
+SHLIB_EXPLIBS=$(KRB5_BASE_LIBS) $(VERTO_LIBS)
|
|
+SHLIB_EXPDEPLIBS=$(KRB5_BASE_DEPLIBS) $(VERTO_DEPLIB)
|
|
+SHLIB_DIRS=-L$(TOPLIBD)
|
|
+SHLIB_RDIRS=$(KRB5_LIBDIR)
|
|
+
|
|
+LIBBASE=krad
|
|
+LIBMAJOR=0
|
|
+LIBMINOR=0
|
|
+
|
|
+STLIBOBJS=attr.o attrset.o client.o code.o packet.o remote.o
|
|
+LIBOBJS=$(OUTPRE)attr.$(OBJEXT) \
|
|
+ $(OUTPRE)attrset.$(OBJEXT) \
|
|
+ $(OUTPRE)client.$(OBJEXT) \
|
|
+ $(OUTPRE)code.$(OBJEXT) \
|
|
+ $(OUTPRE)packet.$(OBJEXT) \
|
|
+ $(OUTPRE)remote.$(OBJEXT)
|
|
+SRCS=attr.c attrset.c client.c code.c packet.c remote.c \
|
|
+ t_attr.c t_attrset.c t_client.c t_code.c t_packet.c t_remote.c t_test.c
|
|
+
|
|
+STOBJLISTS=OBJS.ST
|
|
+
|
|
+all-unix:: all-liblinks
|
|
+install-unix:: install-libs
|
|
+
|
|
+clean-unix:: clean-liblinks clean-libs clean-libobjs
|
|
+
|
|
+check-unix:: t_attr t_attrset t_code t_packet t_remote t_client
|
|
+ $(RUN_SETUP) $(VALGRIND) ./t_attr
|
|
+ $(RUN_SETUP) $(VALGRIND) ./t_attrset
|
|
+ $(RUN_SETUP) $(VALGRIND) ./t_code
|
|
+ $(RUN_SETUP) $(VALGRIND) ./t_packet $(PYTHON) $(srcdir)/t_daemon.py
|
|
+ $(RUN_SETUP) $(VALGRIND) ./t_remote $(PYTHON) $(srcdir)/t_daemon.py
|
|
+ $(RUN_SETUP) $(VALGRIND) ./t_client $(PYTHON) $(srcdir)/t_daemon.py
|
|
+
|
|
+TESTDEPS=t_test.o $(KRB5_BASE_DEPLIBS)
|
|
+TESTLIBS=t_test.o $(KRB5_BASE_LIBS)
|
|
+
|
|
+T_ATTR_OBJS=attr.o t_attr.o
|
|
+t_attr: $(T_ATTR_OBJS) $(TESTDEPS)
|
|
+ $(CC_LINK) -o $@ $(T_ATTR_OBJS) $(TESTLIBS)
|
|
+
|
|
+T_ATTRSET_OBJS=attr.o attrset.o t_attrset.o
|
|
+t_attrset: $(T_ATTRSET_OBJS) $(TESTDEPS)
|
|
+ $(CC_LINK) -o $@ $(T_ATTRSET_OBJS) $(TESTLIBS)
|
|
+
|
|
+T_CODE_OBJS=code.o t_code.o
|
|
+t_code: $(T_CODE_OBJS) $(TESTDEPS)
|
|
+ $(CC_LINK) -o $@ $(T_CODE_OBJS) $(TESTLIBS)
|
|
+
|
|
+T_PACKET_OBJS=attr.o attrset.o code.o packet.o t_packet.o
|
|
+t_packet: $(T_PACKET_OBJS) $(TESTDEPS)
|
|
+ $(CC_LINK) -o $@ $(T_PACKET_OBJS) $(TESTLIBS)
|
|
+
|
|
+T_REMOTE_OBJS=attr.o attrset.o code.o packet.o remote.o t_remote.o
|
|
+t_remote: $(T_REMOTE_OBJS) $(TESTDEPS) $(VERTO_DEPLIB)
|
|
+ $(CC_LINK) -o $@ $(T_REMOTE_OBJS) $(TESTLIBS) $(VERTO_LIBS)
|
|
+
|
|
+T_CLIENT_OBJS=attr.o attrset.o code.o packet.o remote.o client.o t_client.o
|
|
+t_client: $(T_CLIENT_OBJS) $(TESTDEPS) $(VERTO_DEPLIB)
|
|
+ $(CC_LINK) -o $@ $(T_CLIENT_OBJS) $(TESTLIBS) $(VERTO_LIBS)
|
|
+
|
|
+clean-unix:: clean-libobjs
|
|
+ $(RM) *.o t_attr t_attrset t_code t_packet t_remote t_client
|
|
+
|
|
+@lib_frag@
|
|
+@libobj_frag@
|
|
diff --git a/src/lib/krad/attr.c b/src/lib/krad/attr.c
|
|
new file mode 100644
|
|
index 0000000..9c13d9d
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/attr.c
|
|
@@ -0,0 +1,317 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/attr.c - RADIUS attribute functions for libkrad */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include <k5-int.h>
|
|
+#include "internal.h"
|
|
+
|
|
+#include <string.h>
|
|
+
|
|
+/* RFC 2865 */
|
|
+#define BLOCKSIZE 16
|
|
+
|
|
+typedef krb5_error_code
|
|
+(*attribute_transform_fn)(krb5_context ctx, const char *secret,
|
|
+ const unsigned char *auth, const krb5_data *in,
|
|
+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
|
|
+
|
|
+typedef struct {
|
|
+ const char *name;
|
|
+ unsigned char minval;
|
|
+ unsigned char maxval;
|
|
+ attribute_transform_fn encode;
|
|
+ attribute_transform_fn decode;
|
|
+} attribute_record;
|
|
+
|
|
+static krb5_error_code
|
|
+user_password_encode(krb5_context ctx, const char *secret,
|
|
+ const unsigned char *auth, const krb5_data *in,
|
|
+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
|
|
+
|
|
+static krb5_error_code
|
|
+user_password_decode(krb5_context ctx, const char *secret,
|
|
+ const unsigned char *auth, const krb5_data *in,
|
|
+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
|
|
+
|
|
+static const attribute_record attributes[UCHAR_MAX] = {
|
|
+ {"User-Name", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"User-Password", 1, 128, user_password_encode, user_password_decode},
|
|
+ {"CHAP-Password", 17, 17, NULL, NULL},
|
|
+ {"NAS-IP-Address", 4, 4, NULL, NULL},
|
|
+ {"NAS-Port", 4, 4, NULL, NULL},
|
|
+ {"Service-Type", 4, 4, NULL, NULL},
|
|
+ {"Framed-Protocol", 4, 4, NULL, NULL},
|
|
+ {"Framed-IP-Address", 4, 4, NULL, NULL},
|
|
+ {"Framed-IP-Netmask", 4, 4, NULL, NULL},
|
|
+ {"Framed-Routing", 4, 4, NULL, NULL},
|
|
+ {"Filter-Id", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Framed-MTU", 4, 4, NULL, NULL},
|
|
+ {"Framed-Compression", 4, 4, NULL, NULL},
|
|
+ {"Login-IP-Host", 4, 4, NULL, NULL},
|
|
+ {"Login-Service", 4, 4, NULL, NULL},
|
|
+ {"Login-TCP-Port", 4, 4, NULL, NULL},
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Unassigned */
|
|
+ {"Reply-Message", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Callback-Number", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Callback-Id", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Unassigned */
|
|
+ {"Framed-Route", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Framed-IPX-Network", 4, 4, NULL, NULL},
|
|
+ {"State", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Class", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Vendor-Specific", 5, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Session-Timeout", 4, 4, NULL, NULL},
|
|
+ {"Idle-Timeout", 4, 4, NULL, NULL},
|
|
+ {"Termination-Action", 4, 4, NULL, NULL},
|
|
+ {"Called-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Calling-Station-Id", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"NAS-Identifier", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Proxy-State", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Login-LAT-Service", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Login-LAT-Node", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"Login-LAT-Group", 32, 32, NULL, NULL},
|
|
+ {"Framed-AppleTalk-Link", 4, 4, NULL, NULL},
|
|
+ {"Framed-AppleTalk-Network", 4, 4, NULL, NULL},
|
|
+ {"Framed-AppleTalk-Zone", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {NULL, 0, 0, NULL, NULL}, /* Reserved for accounting */
|
|
+ {"CHAP-Challenge", 5, MAX_ATTRSIZE, NULL, NULL},
|
|
+ {"NAS-Port-Type", 4, 4, NULL, NULL},
|
|
+ {"Port-Limit", 4, 4, NULL, NULL},
|
|
+ {"Login-LAT-Port", 1, MAX_ATTRSIZE, NULL, NULL},
|
|
+};
|
|
+
|
|
+/* Encode User-Password attribute. */
|
|
+static krb5_error_code
|
|
+user_password_encode(krb5_context ctx, const char *secret,
|
|
+ const unsigned char *auth, const krb5_data *in,
|
|
+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
|
|
+{
|
|
+ const unsigned char *indx;
|
|
+ krb5_error_code retval;
|
|
+ unsigned int seclen;
|
|
+ krb5_checksum sum;
|
|
+ size_t blck, len, i;
|
|
+ krb5_data tmp;
|
|
+
|
|
+ /* Copy the input buffer to the (zero-padded) output buffer. */
|
|
+ len = (in->length + BLOCKSIZE - 1) / BLOCKSIZE * BLOCKSIZE;
|
|
+ if (len > MAX_ATTRSIZE)
|
|
+ return ENOBUFS;
|
|
+ memset(outbuf, 0, len);
|
|
+ memcpy(outbuf, in->data, in->length);
|
|
+
|
|
+ /* Create our temporary space for processing each block. */
|
|
+ seclen = strlen(secret);
|
|
+ retval = alloc_data(&tmp, seclen + BLOCKSIZE);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ memcpy(tmp.data, secret, seclen);
|
|
+ for (blck = 0, indx = auth; blck * BLOCKSIZE < len; blck++) {
|
|
+ memcpy(tmp.data + seclen, indx, BLOCKSIZE);
|
|
+
|
|
+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &tmp,
|
|
+ &sum);
|
|
+ if (retval != 0) {
|
|
+ zap(tmp.data, tmp.length);
|
|
+ zap(outbuf, len);
|
|
+ krb5_free_data_contents(ctx, &tmp);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < BLOCKSIZE; i++)
|
|
+ outbuf[blck * BLOCKSIZE + i] ^= sum.contents[i];
|
|
+ krb5_free_checksum_contents(ctx, &sum);
|
|
+
|
|
+ indx = &outbuf[blck * BLOCKSIZE];
|
|
+ }
|
|
+
|
|
+ zap(tmp.data, tmp.length);
|
|
+ krb5_free_data_contents(ctx, &tmp);
|
|
+ *outlen = len;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Decode User-Password attribute. */
|
|
+static krb5_error_code
|
|
+user_password_decode(krb5_context ctx, const char *secret,
|
|
+ const unsigned char *auth, const krb5_data *in,
|
|
+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
|
|
+{
|
|
+ const unsigned char *indx;
|
|
+ krb5_error_code retval;
|
|
+ unsigned int seclen;
|
|
+ krb5_checksum sum;
|
|
+ ssize_t blck, i;
|
|
+ krb5_data tmp;
|
|
+
|
|
+ if (in->length % BLOCKSIZE != 0)
|
|
+ return EINVAL;
|
|
+ if (in->length > MAX_ATTRSIZE)
|
|
+ return ENOBUFS;
|
|
+
|
|
+ /* Create our temporary space for processing each block. */
|
|
+ seclen = strlen(secret);
|
|
+ retval = alloc_data(&tmp, seclen + BLOCKSIZE);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ memcpy(tmp.data, secret, seclen);
|
|
+ for (blck = 0, indx = auth; blck * BLOCKSIZE < in->length; blck++) {
|
|
+ memcpy(tmp.data + seclen, indx, BLOCKSIZE);
|
|
+
|
|
+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0,
|
|
+ &tmp, &sum);
|
|
+ if (retval != 0) {
|
|
+ zap(tmp.data, tmp.length);
|
|
+ zap(outbuf, in->length);
|
|
+ krb5_free_data_contents(ctx, &tmp);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < BLOCKSIZE; i++) {
|
|
+ outbuf[blck * BLOCKSIZE + i] = in->data[blck * BLOCKSIZE + i] ^
|
|
+ sum.contents[i];
|
|
+ }
|
|
+ krb5_free_checksum_contents(ctx, &sum);
|
|
+
|
|
+ indx = (const unsigned char *)&in->data[blck * BLOCKSIZE];
|
|
+ }
|
|
+
|
|
+ /* Strip off trailing NULL bytes. */
|
|
+ *outlen = in->length;
|
|
+ while (*outlen > 0 && outbuf[*outlen - 1] == '\0')
|
|
+ (*outlen)--;
|
|
+
|
|
+ krb5_free_data_contents(ctx, &tmp);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+kr_attr_valid(krad_attr type, const krb5_data *data)
|
|
+{
|
|
+ const attribute_record *ar;
|
|
+
|
|
+ if (type == 0)
|
|
+ return EINVAL;
|
|
+
|
|
+ ar = &attributes[type - 1];
|
|
+ return (data->length >= ar->minval && data->length <= ar->maxval) ? 0 :
|
|
+ EMSGSIZE;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+kr_attr_encode(krb5_context ctx, const char *secret,
|
|
+ const unsigned char *auth, krad_attr type,
|
|
+ const krb5_data *in, unsigned char outbuf[MAX_ATTRSIZE],
|
|
+ size_t *outlen)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+
|
|
+ retval = kr_attr_valid(type, in);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ if (attributes[type - 1].encode == NULL) {
|
|
+ if (in->length > MAX_ATTRSIZE)
|
|
+ return ENOBUFS;
|
|
+
|
|
+ *outlen = in->length;
|
|
+ memcpy(outbuf, in->data, in->length);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return attributes[type - 1].encode(ctx, secret, auth, in, outbuf, outlen);
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
|
|
+ krad_attr type, const krb5_data *in,
|
|
+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+
|
|
+ retval = kr_attr_valid(type, in);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ if (attributes[type - 1].encode == NULL) {
|
|
+ if (in->length > MAX_ATTRSIZE)
|
|
+ return ENOBUFS;
|
|
+
|
|
+ *outlen = in->length;
|
|
+ memcpy(outbuf, in->data, in->length);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return attributes[type - 1].decode(ctx, secret, auth, in, outbuf, outlen);
|
|
+}
|
|
+
|
|
+krad_attr
|
|
+krad_attr_name2num(const char *name)
|
|
+{
|
|
+ unsigned char i;
|
|
+
|
|
+ for (i = 0; i < UCHAR_MAX; i++) {
|
|
+ if (attributes[i].name == NULL)
|
|
+ continue;
|
|
+
|
|
+ if (strcmp(attributes[i].name, name) == 0)
|
|
+ return i + 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+const char *
|
|
+krad_attr_num2name(krad_attr type)
|
|
+{
|
|
+ if (type == 0)
|
|
+ return NULL;
|
|
+
|
|
+ return attributes[type - 1].name;
|
|
+}
|
|
diff --git a/src/lib/krad/attrset.c b/src/lib/krad/attrset.c
|
|
new file mode 100644
|
|
index 0000000..fbd0621
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/attrset.c
|
|
@@ -0,0 +1,244 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/attrset.c - RADIUS attribute set functions for libkrad */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include <k5-int.h>
|
|
+#include <k5-queue.h>
|
|
+#include "internal.h"
|
|
+
|
|
+#include <string.h>
|
|
+
|
|
+TAILQ_HEAD(attr_head, attr_st);
|
|
+
|
|
+typedef struct attr_st attr;
|
|
+struct attr_st {
|
|
+ TAILQ_ENTRY(attr_st) list;
|
|
+ krad_attr type;
|
|
+ krb5_data attr;
|
|
+ char buffer[MAX_ATTRSIZE];
|
|
+};
|
|
+
|
|
+struct krad_attrset_st {
|
|
+ krb5_context ctx;
|
|
+ struct attr_head list;
|
|
+};
|
|
+
|
|
+krb5_error_code
|
|
+krad_attrset_new(krb5_context ctx, krad_attrset **set)
|
|
+{
|
|
+ krad_attrset *tmp;
|
|
+
|
|
+ tmp = calloc(1, sizeof(krad_attrset));
|
|
+ if (tmp == NULL)
|
|
+ return ENOMEM;
|
|
+ tmp->ctx = ctx;
|
|
+ TAILQ_INIT(&tmp->list);
|
|
+
|
|
+ *set = tmp;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+krad_attrset_free(krad_attrset *set)
|
|
+{
|
|
+ attr *a;
|
|
+
|
|
+ if (set == NULL)
|
|
+ return;
|
|
+
|
|
+ while (!TAILQ_EMPTY(&set->list)) {
|
|
+ a = TAILQ_FIRST(&set->list);
|
|
+ TAILQ_REMOVE(&set->list, a, list);
|
|
+ zap(a->buffer, sizeof(a->buffer));
|
|
+ free(a);
|
|
+ }
|
|
+
|
|
+ free(set);
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+krad_attrset_add(krad_attrset *set, krad_attr type, const krb5_data *data)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ attr *tmp;
|
|
+
|
|
+ retval = kr_attr_valid(type, data);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ tmp = calloc(1, sizeof(attr));
|
|
+ if (tmp == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ tmp->type = type;
|
|
+ tmp->attr = make_data(tmp->buffer, data->length);
|
|
+ memcpy(tmp->attr.data, data->data, data->length);
|
|
+
|
|
+ TAILQ_INSERT_TAIL(&set->list, tmp, list);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+krad_attrset_add_number(krad_attrset *set, krad_attr type, krb5_ui_4 num)
|
|
+{
|
|
+ krb5_data data;
|
|
+
|
|
+ num = htonl(num);
|
|
+ data = make_data(&num, sizeof(num));
|
|
+ return krad_attrset_add(set, type, &data);
|
|
+}
|
|
+
|
|
+void
|
|
+krad_attrset_del(krad_attrset *set, krad_attr type, size_t indx)
|
|
+{
|
|
+ attr *a;
|
|
+
|
|
+ TAILQ_FOREACH(a, &set->list, list) {
|
|
+ if (a->type == type && indx-- == 0) {
|
|
+ TAILQ_REMOVE(&set->list, a, list);
|
|
+ zap(a->buffer, sizeof(a->buffer));
|
|
+ free(a);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+const krb5_data *
|
|
+krad_attrset_get(const krad_attrset *set, krad_attr type, size_t indx)
|
|
+{
|
|
+ attr *a;
|
|
+
|
|
+ TAILQ_FOREACH(a, &set->list, list) {
|
|
+ if (a->type == type && indx-- == 0)
|
|
+ return &a->attr;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+krad_attrset_copy(const krad_attrset *set, krad_attrset **copy)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ krad_attrset *tmp;
|
|
+ attr *a;
|
|
+
|
|
+ retval = krad_attrset_new(set->ctx, &tmp);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ TAILQ_FOREACH(a, &set->list, list) {
|
|
+ retval = krad_attrset_add(tmp, a->type, &a->attr);
|
|
+ if (retval != 0) {
|
|
+ krad_attrset_free(tmp);
|
|
+ return retval;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *copy = tmp;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+kr_attrset_encode(const krad_attrset *set, const char *secret,
|
|
+ const unsigned char *auth,
|
|
+ unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen)
|
|
+{
|
|
+ unsigned char buffer[MAX_ATTRSIZE];
|
|
+ krb5_error_code retval;
|
|
+ size_t i = 0, attrlen;
|
|
+ attr *a;
|
|
+
|
|
+ if (set == NULL) {
|
|
+ *outlen = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ TAILQ_FOREACH(a, &set->list, list) {
|
|
+ retval = kr_attr_encode(set->ctx, secret, auth, a->type, &a->attr,
|
|
+ buffer, &attrlen);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ if (i + attrlen + 2 > MAX_ATTRSETSIZE)
|
|
+ return EMSGSIZE;
|
|
+
|
|
+ outbuf[i++] = a->type;
|
|
+ outbuf[i++] = attrlen + 2;
|
|
+ memcpy(&outbuf[i], buffer, attrlen);
|
|
+ i += attrlen;
|
|
+ }
|
|
+
|
|
+ *outlen = i;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
|
|
+ const unsigned char *auth, krad_attrset **set_out)
|
|
+{
|
|
+ unsigned char buffer[MAX_ATTRSIZE];
|
|
+ krb5_data tmp;
|
|
+ krb5_error_code retval;
|
|
+ krad_attr type;
|
|
+ krad_attrset *set;
|
|
+ size_t i, len;
|
|
+
|
|
+ *set_out = NULL;
|
|
+
|
|
+ retval = krad_attrset_new(ctx, &set);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ for (i = 0; i + 2 < in->length; ) {
|
|
+ type = in->data[i++];
|
|
+ tmp = make_data(&in->data[i + 1], in->data[i] - 2);
|
|
+ i += tmp.length + 1;
|
|
+
|
|
+ retval = (in->length < i) ? EBADMSG : 0;
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ retval = kr_attr_decode(ctx, secret, auth, type, &tmp, buffer, &len);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ tmp = make_data(buffer, len);
|
|
+ retval = krad_attrset_add(set, type, &tmp);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ *set_out = set;
|
|
+ set = NULL;
|
|
+
|
|
+cleanup:
|
|
+ zap(buffer, sizeof(buffer));
|
|
+ krad_attrset_free(set);
|
|
+ return retval;
|
|
+}
|
|
diff --git a/src/lib/krad/client.c b/src/lib/krad/client.c
|
|
new file mode 100644
|
|
index 0000000..0c37680
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/client.c
|
|
@@ -0,0 +1,335 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/client.c - Client request code for libkrad */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include <k5-queue.h>
|
|
+#include "internal.h"
|
|
+
|
|
+#include <string.h>
|
|
+#include <sys/un.h>
|
|
+#include <unistd.h>
|
|
+#include <stdio.h>
|
|
+#include <limits.h>
|
|
+
|
|
+LIST_HEAD(server_head, server_st);
|
|
+
|
|
+typedef struct remote_state_st remote_state;
|
|
+typedef struct request_st request;
|
|
+typedef struct server_st server;
|
|
+
|
|
+struct remote_state_st {
|
|
+ const krad_packet *packet;
|
|
+ krad_remote *remote;
|
|
+};
|
|
+
|
|
+struct request_st {
|
|
+ krad_client *rc;
|
|
+
|
|
+ krad_code code;
|
|
+ krad_attrset *attrs;
|
|
+ int timeout;
|
|
+ size_t retries;
|
|
+ krad_cb cb;
|
|
+ void *data;
|
|
+
|
|
+ remote_state *remotes;
|
|
+ ssize_t current;
|
|
+ ssize_t count;
|
|
+};
|
|
+
|
|
+struct server_st {
|
|
+ krad_remote *serv;
|
|
+ time_t last;
|
|
+ LIST_ENTRY(server_st) list;
|
|
+};
|
|
+
|
|
+struct krad_client_st {
|
|
+ krb5_context kctx;
|
|
+ verto_ctx *vctx;
|
|
+ struct server_head servers;
|
|
+};
|
|
+
|
|
+/* Return either a pre-existing server that matches the address info and the
|
|
+ * secret, or create a new one. */
|
|
+static krb5_error_code
|
|
+get_server(krad_client *rc, const struct addrinfo *ai, const char *secret,
|
|
+ krad_remote **out)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ time_t currtime;
|
|
+ server *srv;
|
|
+
|
|
+ if (time(&currtime) == (time_t)-1)
|
|
+ return errno;
|
|
+
|
|
+ LIST_FOREACH(srv, &rc->servers, list) {
|
|
+ if (kr_remote_equals(srv->serv, ai, secret)) {
|
|
+ srv->last = currtime;
|
|
+ *out = srv->serv;
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ srv = calloc(1, sizeof(server));
|
|
+ if (srv == NULL)
|
|
+ return ENOMEM;
|
|
+ srv->last = currtime;
|
|
+
|
|
+ retval = kr_remote_new(rc->kctx, rc->vctx, ai, secret, &srv->serv);
|
|
+ if (retval != 0) {
|
|
+ free(srv);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ LIST_INSERT_HEAD(&rc->servers, srv, list);
|
|
+ *out = srv->serv;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Free a request. */
|
|
+static void
|
|
+request_free(request *req)
|
|
+{
|
|
+ krad_attrset_free(req->attrs);
|
|
+ free(req->remotes);
|
|
+ free(req);
|
|
+}
|
|
+
|
|
+/* Create a request. */
|
|
+static krb5_error_code
|
|
+request_new(krad_client *rc, krad_code code, const krad_attrset *attrs,
|
|
+ const struct addrinfo *ai, const char *secret, int timeout,
|
|
+ size_t retries, krad_cb cb, void *data, request **req)
|
|
+{
|
|
+ const struct addrinfo *tmp;
|
|
+ krb5_error_code retval;
|
|
+ request *rqst;
|
|
+ size_t i;
|
|
+
|
|
+ if (ai == NULL)
|
|
+ return EINVAL;
|
|
+
|
|
+ rqst = calloc(1, sizeof(request));
|
|
+ if (rqst == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ for (tmp = ai; tmp != NULL; tmp = tmp->ai_next)
|
|
+ rqst->count++;
|
|
+
|
|
+ rqst->rc = rc;
|
|
+ rqst->code = code;
|
|
+ rqst->cb = cb;
|
|
+ rqst->data = data;
|
|
+ rqst->timeout = timeout / rqst->count;
|
|
+ rqst->retries = retries;
|
|
+
|
|
+ retval = krad_attrset_copy(attrs, &rqst->attrs);
|
|
+ if (retval != 0) {
|
|
+ request_free(rqst);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ rqst->remotes = calloc(rqst->count + 1, sizeof(remote_state));
|
|
+ if (rqst->remotes == NULL) {
|
|
+ request_free(rqst);
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ i = 0;
|
|
+ for (tmp = ai; tmp != NULL; tmp = tmp->ai_next) {
|
|
+ retval = get_server(rc, tmp, secret, &rqst->remotes[i++].remote);
|
|
+ if (retval != 0) {
|
|
+ request_free(rqst);
|
|
+ return retval;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *req = rqst;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Close remotes that haven't been used in a while. */
|
|
+static void
|
|
+age(struct server_head *head, time_t currtime)
|
|
+{
|
|
+ server *srv, *tmp;
|
|
+
|
|
+ LIST_FOREACH_SAFE(srv, head, list, tmp) {
|
|
+ if (currtime == (time_t)-1 || currtime - srv->last > 60 * 60) {
|
|
+ LIST_REMOVE(srv, list);
|
|
+ kr_remote_free(srv->serv);
|
|
+ free(srv);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Handle a response from a server (or related errors). */
|
|
+static void
|
|
+on_response(krb5_error_code retval, const krad_packet *reqp,
|
|
+ const krad_packet *rspp, void *data)
|
|
+{
|
|
+ request *req = data;
|
|
+ time_t currtime;
|
|
+ size_t i;
|
|
+
|
|
+ /* Do nothing if we are already completed. */
|
|
+ if (req->count < 0)
|
|
+ return;
|
|
+
|
|
+ /* If we have timed out and have more remotes to try, do so. */
|
|
+ if (retval == ETIMEDOUT && req->remotes[++req->current].remote != NULL) {
|
|
+ retval = kr_remote_send(req->remotes[req->current].remote, req->code,
|
|
+ req->attrs, on_response, req, req->timeout,
|
|
+ req->retries,
|
|
+ &req->remotes[req->current].packet);
|
|
+ if (retval == 0)
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Mark the request as complete. */
|
|
+ req->count = -1;
|
|
+
|
|
+ /* Inform the callback. */
|
|
+ req->cb(retval, reqp, rspp, req->data);
|
|
+
|
|
+ /* Cancel the outstanding packets. */
|
|
+ for (i = 0; req->remotes[i].remote != NULL; i++)
|
|
+ kr_remote_cancel(req->remotes[i].remote, req->remotes[i].packet);
|
|
+
|
|
+ /* Age out servers that haven't been used in a while. */
|
|
+ if (time(&currtime) != (time_t)-1)
|
|
+ age(&req->rc->servers, currtime);
|
|
+
|
|
+ request_free(req);
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+krad_client_new(krb5_context kctx, verto_ctx *vctx, krad_client **out)
|
|
+{
|
|
+ krad_client *tmp;
|
|
+
|
|
+ tmp = calloc(1, sizeof(krad_client));
|
|
+ if (tmp == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ tmp->kctx = kctx;
|
|
+ tmp->vctx = vctx;
|
|
+
|
|
+ *out = tmp;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+krad_client_free(krad_client *rc)
|
|
+{
|
|
+ if (rc == NULL)
|
|
+ return;
|
|
+
|
|
+ age(&rc->servers, -1);
|
|
+ free(rc);
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+resolve_remote(const char *remote, struct addrinfo **ai)
|
|
+{
|
|
+ const char *svc = "radius";
|
|
+ krb5_error_code retval;
|
|
+ struct addrinfo hints;
|
|
+ char *sep, *srv;
|
|
+
|
|
+ /* Isolate the port number if it exists. */
|
|
+ srv = strdup(remote);
|
|
+ if (srv == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ if (srv[0] == '[') {
|
|
+ /* IPv6 */
|
|
+ sep = strrchr(srv, ']');
|
|
+ if (sep != NULL && sep[1] == ':') {
|
|
+ sep[1] = '\0';
|
|
+ svc = &sep[2];
|
|
+ }
|
|
+ } else {
|
|
+ /* IPv4 or DNS */
|
|
+ sep = strrchr(srv, ':');
|
|
+ if (sep != NULL && sep[1] != '\0') {
|
|
+ sep[0] = '\0';
|
|
+ svc = &sep[1];
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Perform the lookup. */
|
|
+ memset(&hints, 0, sizeof(hints));
|
|
+ hints.ai_socktype = SOCK_DGRAM;
|
|
+ retval = gai_error_code(getaddrinfo(srv, svc, &hints, ai));
|
|
+ free(srv);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+krad_client_send(krad_client *rc, krad_code code, const krad_attrset *attrs,
|
|
+ const char *remote, const char *secret, int timeout,
|
|
+ size_t retries, krad_cb cb, void *data)
|
|
+{
|
|
+ struct addrinfo usock, *ai = NULL;
|
|
+ krb5_error_code retval;
|
|
+ struct sockaddr_un ua;
|
|
+ request *req;
|
|
+
|
|
+ if (remote[0] == '/') {
|
|
+ ua.sun_family = AF_UNIX;
|
|
+ snprintf(ua.sun_path, sizeof(ua.sun_path), "%s", remote);
|
|
+ memset(&usock, 0, sizeof(usock));
|
|
+ usock.ai_family = AF_UNIX;
|
|
+ usock.ai_socktype = SOCK_STREAM;
|
|
+ usock.ai_addr = (struct sockaddr *)&ua;
|
|
+ usock.ai_addrlen = sizeof(ua);
|
|
+
|
|
+ retval = request_new(rc, code, attrs, &usock, secret, timeout, retries,
|
|
+ cb, data, &req);
|
|
+ } else {
|
|
+ retval = resolve_remote(remote, &ai);
|
|
+ if (retval == 0) {
|
|
+ retval = request_new(rc, code, attrs, ai, secret, timeout, retries,
|
|
+ cb, data, &req);
|
|
+ freeaddrinfo(ai);
|
|
+ }
|
|
+ }
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ retval = kr_remote_send(req->remotes[req->current].remote, req->code,
|
|
+ req->attrs, on_response, req, req->timeout,
|
|
+ req->retries, &req->remotes[req->current].packet);
|
|
+ if (retval != 0) {
|
|
+ request_free(req);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/lib/krad/code.c b/src/lib/krad/code.c
|
|
new file mode 100644
|
|
index 0000000..16871bb
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/code.c
|
|
@@ -0,0 +1,111 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/code.c - RADIUS code name table for libkrad */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "internal.h"
|
|
+
|
|
+#include <string.h>
|
|
+
|
|
+static const char *codes[UCHAR_MAX] = {
|
|
+ "Access-Request",
|
|
+ "Access-Accept",
|
|
+ "Access-Reject",
|
|
+ "Accounting-Request",
|
|
+ "Accounting-Response",
|
|
+ "Accounting-Status",
|
|
+ "Password-Request",
|
|
+ "Password-Ack",
|
|
+ "Password-Reject",
|
|
+ "Accounting-Message",
|
|
+ "Access-Challenge",
|
|
+ "Status-Server",
|
|
+ "Status-Client",
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ "Resource-Free-Request",
|
|
+ "Resource-Free-Response",
|
|
+ "Resource-Query-Request",
|
|
+ "Resource-Query-Response",
|
|
+ "Alternate-Resource-Reclaim-Request",
|
|
+ "NAS-Reboot-Request",
|
|
+ "NAS-Reboot-Response",
|
|
+ NULL,
|
|
+ "Next-Passcode",
|
|
+ "New-Pin",
|
|
+ "Terminate-Session",
|
|
+ "Password-Expired",
|
|
+ "Event-Request",
|
|
+ "Event-Response",
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ "Disconnect-Request",
|
|
+ "Disconnect-Ack",
|
|
+ "Disconnect-Nak",
|
|
+ "Change-Filters-Request",
|
|
+ "Change-Filters-Ack",
|
|
+ "Change-Filters-Nak",
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ "IP-Address-Allocate",
|
|
+ "IP-Address-Release",
|
|
+};
|
|
+
|
|
+krad_code
|
|
+krad_code_name2num(const char *name)
|
|
+{
|
|
+ unsigned char i;
|
|
+
|
|
+ for (i = 0; i < UCHAR_MAX; i++) {
|
|
+ if (codes[i] == NULL)
|
|
+ continue;
|
|
+
|
|
+ if (strcmp(codes[i], name) == 0)
|
|
+ return ++i;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+const char *
|
|
+krad_code_num2name(krad_code code)
|
|
+{
|
|
+ if (code == 0)
|
|
+ return NULL;
|
|
+
|
|
+ return codes[code - 1];
|
|
+}
|
|
diff --git a/src/lib/krad/deps b/src/lib/krad/deps
|
|
new file mode 100644
|
|
index 0000000..8171f94
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/deps
|
|
@@ -0,0 +1,156 @@
|
|
+#
|
|
+# Generated makefile dependencies follow.
|
|
+#
|
|
+attr.so attr.po $(OUTPRE)attr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
|
|
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
|
|
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h attr.c internal.h
|
|
+attrset.so attrset.po $(OUTPRE)attrset.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
|
|
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
|
|
+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
|
|
+ attrset.c internal.h
|
|
+client.so client.po $(OUTPRE)client.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
|
|
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
|
|
+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
|
|
+ client.c internal.h
|
|
+code.so code.po $(OUTPRE)code.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
|
|
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
|
|
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h code.c internal.h
|
|
+packet.so packet.po $(OUTPRE)packet.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
|
|
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
|
|
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h internal.h packet.c
|
|
+remote.so remote.po $(OUTPRE)remote.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-queue.h \
|
|
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
|
|
+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
|
|
+ internal.h remote.c
|
|
+t_attr.so t_attr.po $(OUTPRE)t_attr.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
|
|
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
|
|
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h internal.h t_attr.c \
|
|
+ t_test.h
|
|
+t_attrset.so t_attrset.po $(OUTPRE)t_attrset.$(OBJEXT): \
|
|
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
|
|
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
|
|
+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
|
|
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
|
|
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
|
|
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
|
|
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
|
|
+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
|
|
+ internal.h t_attrset.c t_test.h
|
|
+t_client.so t_client.po $(OUTPRE)t_client.$(OBJEXT): \
|
|
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
|
|
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
|
|
+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
|
|
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
|
|
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
|
|
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
|
|
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
|
|
+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
|
|
+ internal.h t_client.c t_daemon.h t_test.h
|
|
+t_code.so t_code.po $(OUTPRE)t_code.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
|
|
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
|
|
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h internal.h t_code.c \
|
|
+ t_test.h
|
|
+t_packet.so t_packet.po $(OUTPRE)t_packet.$(OBJEXT): \
|
|
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
|
|
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
|
|
+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
|
|
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
|
|
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
|
|
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
|
|
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
|
|
+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
|
|
+ internal.h t_daemon.h t_packet.c t_test.h
|
|
+t_remote.so t_remote.po $(OUTPRE)t_remote.$(OBJEXT): \
|
|
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
|
|
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
|
|
+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/k5-buf.h \
|
|
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
|
|
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
|
|
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
|
|
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
|
|
+ $(top_srcdir)/include/krad.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
|
|
+ internal.h t_daemon.h t_remote.c t_test.h
|
|
+t_test.so t_test.po $(OUTPRE)t_test.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
|
|
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krad.h \
|
|
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
|
|
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h internal.h t_test.c \
|
|
+ t_test.h
|
|
diff --git a/src/lib/krad/internal.h b/src/lib/krad/internal.h
|
|
new file mode 100644
|
|
index 0000000..996a893
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/internal.h
|
|
@@ -0,0 +1,155 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/internal.h - Internal declarations for libkrad */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef INTERNAL_H_
|
|
+#define INTERNAL_H_
|
|
+
|
|
+#include <k5-int.h>
|
|
+#include "krad.h"
|
|
+
|
|
+#include <errno.h>
|
|
+
|
|
+#include <sys/types.h>
|
|
+#include <sys/socket.h>
|
|
+#include <netdb.h>
|
|
+
|
|
+#ifndef UCHAR_MAX
|
|
+#define UCHAR_MAX 255
|
|
+#endif
|
|
+
|
|
+/* RFC 2865 */
|
|
+#define MAX_ATTRSIZE (UCHAR_MAX - 2)
|
|
+#define MAX_ATTRSETSIZE (KRAD_PACKET_SIZE_MAX - 20)
|
|
+
|
|
+typedef struct krad_remote_st krad_remote;
|
|
+
|
|
+/* Validate constraints of an attribute. */
|
|
+krb5_error_code
|
|
+kr_attr_valid(krad_attr type, const krb5_data *data);
|
|
+
|
|
+/* Encode an attribute. */
|
|
+krb5_error_code
|
|
+kr_attr_encode(krb5_context ctx, const char *secret, const unsigned char *auth,
|
|
+ krad_attr type, const krb5_data *in,
|
|
+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
|
|
+
|
|
+/* Decode an attribute. */
|
|
+krb5_error_code
|
|
+kr_attr_decode(krb5_context ctx, const char *secret, const unsigned char *auth,
|
|
+ krad_attr type, const krb5_data *in,
|
|
+ unsigned char outbuf[MAX_ATTRSIZE], size_t *outlen);
|
|
+
|
|
+/* Encode the attributes into the buffer. */
|
|
+krb5_error_code
|
|
+kr_attrset_encode(const krad_attrset *set, const char *secret,
|
|
+ const unsigned char *auth,
|
|
+ unsigned char outbuf[MAX_ATTRSETSIZE], size_t *outlen);
|
|
+
|
|
+/* Decode attributes from a buffer. */
|
|
+krb5_error_code
|
|
+kr_attrset_decode(krb5_context ctx, const krb5_data *in, const char *secret,
|
|
+ const unsigned char *auth, krad_attrset **set);
|
|
+
|
|
+/* Create a new remote object which manages a socket and the state of
|
|
+ * outstanding requests. */
|
|
+krb5_error_code
|
|
+kr_remote_new(krb5_context kctx, verto_ctx *vctx, const struct addrinfo *info,
|
|
+ const char *secret, krad_remote **rr);
|
|
+
|
|
+/* Free a remote object. */
|
|
+void
|
|
+kr_remote_free(krad_remote *rr);
|
|
+
|
|
+/*
|
|
+ * Send the packet to the remote. The cb will be called when a response is
|
|
+ * received, the request times out, the request is canceled or an error occurs.
|
|
+ *
|
|
+ * The timeout parameter is the total timeout across all retries in
|
|
+ * milliseconds.
|
|
+ *
|
|
+ * If the cb is called with a retval of ETIMEDOUT it indicates that the alloted
|
|
+ * time has elapsed. However, in the case of a timeout, we continue to listen
|
|
+ * for the packet until krad_remote_cancel() is called or a response is
|
|
+ * received. This means that cb will always be called twice in the event of a
|
|
+ * timeout. This permits you to pursue other remotes while still listening for
|
|
+ * a response from the first one.
|
|
+ */
|
|
+krb5_error_code
|
|
+kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
|
|
+ krad_cb cb, void *data, int timeout, size_t retries,
|
|
+ const krad_packet **pkt);
|
|
+
|
|
+/* Remove packet from the queue of requests awaiting responses. */
|
|
+void
|
|
+kr_remote_cancel(krad_remote *rr, const krad_packet *pkt);
|
|
+
|
|
+/* Determine if this remote object refers to the remote resource identified
|
|
+ * by the addrinfo struct and the secret. */
|
|
+krb5_boolean
|
|
+kr_remote_equals(const krad_remote *rr, const struct addrinfo *info,
|
|
+ const char *secret);
|
|
+
|
|
+/* Adapted from lib/krb5/os/sendto_kdc.c. */
|
|
+static inline krb5_error_code
|
|
+gai_error_code(int err)
|
|
+{
|
|
+ switch (err) {
|
|
+ case 0:
|
|
+ return 0;
|
|
+ case EAI_BADFLAGS:
|
|
+ case EAI_FAMILY:
|
|
+ case EAI_SOCKTYPE:
|
|
+ case EAI_SERVICE:
|
|
+#ifdef EAI_ADDRFAMILY
|
|
+ case EAI_ADDRFAMILY:
|
|
+#endif
|
|
+ return EINVAL;
|
|
+ case EAI_AGAIN:
|
|
+ return EAGAIN;
|
|
+ case EAI_MEMORY:
|
|
+ return ENOMEM;
|
|
+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
|
|
+ case EAI_NODATA:
|
|
+#endif
|
|
+ case EAI_NONAME:
|
|
+ return EADDRNOTAVAIL;
|
|
+#ifdef EAI_OVERFLOW
|
|
+ case EAI_OVERFLOW:
|
|
+ return EOVERFLOW;
|
|
+#endif
|
|
+#ifdef EAI_SYSTEM
|
|
+ case EAI_SYSTEM:
|
|
+ return errno;
|
|
+#endif
|
|
+ default:
|
|
+ return EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif /* INTERNAL_H_ */
|
|
diff --git a/src/lib/krad/libkrad.exports b/src/lib/krad/libkrad.exports
|
|
new file mode 100644
|
|
index 0000000..fe3f159
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/libkrad.exports
|
|
@@ -0,0 +1,23 @@
|
|
+krad_code_name2num
|
|
+krad_code_num2name
|
|
+krad_attr_name2num
|
|
+krad_attr_num2name
|
|
+krad_attrset_new
|
|
+krad_attrset_copy
|
|
+krad_attrset_free
|
|
+krad_attrset_add
|
|
+krad_attrset_add_number
|
|
+krad_attrset_del
|
|
+krad_attrset_get
|
|
+krad_packet_bytes_needed
|
|
+krad_packet_free
|
|
+krad_packet_new_request
|
|
+krad_packet_new_response
|
|
+krad_packet_decode_request
|
|
+krad_packet_decode_response
|
|
+krad_packet_encode
|
|
+krad_packet_get_code
|
|
+krad_packet_get_attr
|
|
+krad_client_new
|
|
+krad_client_free
|
|
+krad_client_send
|
|
diff --git a/src/lib/krad/packet.c b/src/lib/krad/packet.c
|
|
new file mode 100644
|
|
index 0000000..6e6b27e
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/packet.c
|
|
@@ -0,0 +1,470 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/packet.c - Packet functions for libkrad */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "internal.h"
|
|
+
|
|
+#include <string.h>
|
|
+
|
|
+#include <arpa/inet.h>
|
|
+
|
|
+typedef unsigned char uchar;
|
|
+
|
|
+/* RFC 2865 */
|
|
+#define OFFSET_CODE 0
|
|
+#define OFFSET_ID 1
|
|
+#define OFFSET_LENGTH 2
|
|
+#define OFFSET_AUTH 4
|
|
+#define OFFSET_ATTR 20
|
|
+#define AUTH_FIELD_SIZE (OFFSET_ATTR - OFFSET_AUTH)
|
|
+
|
|
+#define offset(d, o) (&(d)->data[o])
|
|
+#define pkt_code_get(p) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE))
|
|
+#define pkt_code_set(p, v) (*(krad_code *)offset(&(p)->pkt, OFFSET_CODE)) = v
|
|
+#define pkt_id_get(p) (*(uchar *)offset(&(p)->pkt, OFFSET_ID))
|
|
+#define pkt_id_set(p, v) (*(uchar *)offset(&(p)->pkt, OFFSET_ID)) = v
|
|
+#define pkt_len_get(p) load_16_be(offset(&(p)->pkt, OFFSET_LENGTH))
|
|
+#define pkt_len_set(p, v) store_16_be(v, offset(&(p)->pkt, OFFSET_LENGTH))
|
|
+#define pkt_auth(p) ((uchar *)offset(&(p)->pkt, OFFSET_AUTH))
|
|
+#define pkt_attr(p) ((unsigned char *)offset(&(p)->pkt, OFFSET_ATTR))
|
|
+
|
|
+struct krad_packet_st {
|
|
+ char buffer[KRAD_PACKET_SIZE_MAX];
|
|
+ krad_attrset *attrset;
|
|
+ krb5_data pkt;
|
|
+};
|
|
+
|
|
+typedef struct {
|
|
+ uchar x[(UCHAR_MAX + 1) / 8];
|
|
+} idmap;
|
|
+
|
|
+/* Ensure the map is empty. */
|
|
+static inline void
|
|
+idmap_init(idmap *map)
|
|
+{
|
|
+ memset(map, 0, sizeof(*map));
|
|
+}
|
|
+
|
|
+/* Set an id as already allocated. */
|
|
+static inline void
|
|
+idmap_set(idmap *map, uchar id)
|
|
+{
|
|
+ map->x[id / 8] |= 1 << (id % 8);
|
|
+}
|
|
+
|
|
+/* Determine whether or not an id is used. */
|
|
+static inline krb5_boolean
|
|
+idmap_isset(const idmap *map, uchar id)
|
|
+{
|
|
+ return (map->x[id / 8] & (1 << (id % 8))) != 0;
|
|
+}
|
|
+
|
|
+/* Find an unused id starting the search at the value specified in id.
|
|
+ * NOTE: For optimal security, the initial value of id should be random. */
|
|
+static inline krb5_error_code
|
|
+idmap_find(const idmap *map, uchar *id)
|
|
+{
|
|
+ krb5_int16 i;
|
|
+
|
|
+ for (i = *id; i >= 0 && i <= UCHAR_MAX; *id % 2 == 0 ? i++ : i--) {
|
|
+ if (!idmap_isset(map, i))
|
|
+ goto success;
|
|
+ }
|
|
+
|
|
+ for (i = *id; i >= 0 && i <= UCHAR_MAX; *id % 2 == 1 ? i++ : i--) {
|
|
+ if (!idmap_isset(map, i))
|
|
+ goto success;
|
|
+ }
|
|
+
|
|
+ return ERANGE;
|
|
+
|
|
+success:
|
|
+ *id = i;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Generate size bytes of random data into the buffer. */
|
|
+static inline krb5_error_code
|
|
+randomize(krb5_context ctx, void *buffer, unsigned int size)
|
|
+{
|
|
+ krb5_data rdata = make_data(buffer, size);
|
|
+ return krb5_c_random_make_octets(ctx, &rdata);
|
|
+}
|
|
+
|
|
+/* Generate a radius packet id. */
|
|
+static krb5_error_code
|
|
+id_generate(krb5_context ctx, krad_packet_iter_cb cb, void *data, uchar *id)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ const krad_packet *tmp;
|
|
+ idmap used;
|
|
+ uchar i;
|
|
+
|
|
+ retval = randomize(ctx, &i, sizeof(i));
|
|
+ if (retval != 0) {
|
|
+ if (cb != NULL)
|
|
+ (*cb)(data, TRUE);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ if (cb != NULL) {
|
|
+ idmap_init(&used);
|
|
+ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE))
|
|
+ idmap_set(&used, tmp->pkt.data[1]);
|
|
+
|
|
+ retval = idmap_find(&used, &i);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ *id = i;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Generate a random authenticator field. */
|
|
+static krb5_error_code
|
|
+auth_generate_random(krb5_context ctx, uchar *rauth)
|
|
+{
|
|
+ krb5_ui_4 trunctime;
|
|
+ time_t currtime;
|
|
+
|
|
+ /* Get the least-significant four bytes of the current time. */
|
|
+ currtime = time(NULL);
|
|
+ if (currtime == (time_t)-1)
|
|
+ return errno;
|
|
+ trunctime = (krb5_ui_4)currtime;
|
|
+ memcpy(rauth, &trunctime, sizeof(trunctime));
|
|
+
|
|
+ /* Randomize the rest of the buffer. */
|
|
+ return randomize(ctx, rauth + sizeof(trunctime),
|
|
+ AUTH_FIELD_SIZE - sizeof(trunctime));
|
|
+}
|
|
+
|
|
+/* Generate a response authenticator field. */
|
|
+static krb5_error_code
|
|
+auth_generate_response(krb5_context ctx, const char *secret,
|
|
+ const krad_packet *response, const uchar *auth,
|
|
+ uchar *rauth)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ krb5_checksum hash;
|
|
+ krb5_data data;
|
|
+
|
|
+ /* Allocate the temporary buffer. */
|
|
+ retval = alloc_data(&data, response->pkt.length + strlen(secret));
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ /* Encoded RADIUS packet with the request's
|
|
+ * authenticator and the secret at the end. */
|
|
+ memcpy(data.data, response->pkt.data, response->pkt.length);
|
|
+ memcpy(data.data + OFFSET_AUTH, auth, AUTH_FIELD_SIZE);
|
|
+ memcpy(data.data + response->pkt.length, secret, strlen(secret));
|
|
+
|
|
+ /* Hash it. */
|
|
+ retval = krb5_c_make_checksum(ctx, CKSUMTYPE_RSA_MD5, NULL, 0, &data,
|
|
+ &hash);
|
|
+ free(data.data);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ memcpy(rauth, hash.contents, AUTH_FIELD_SIZE);
|
|
+ krb5_free_checksum_contents(ctx, &hash);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Create a new packet. */
|
|
+static krad_packet *
|
|
+packet_new()
|
|
+{
|
|
+ krad_packet *pkt;
|
|
+
|
|
+ pkt = calloc(1, sizeof(krad_packet));
|
|
+ if (pkt == NULL)
|
|
+ return NULL;
|
|
+ pkt->pkt = make_data(pkt->buffer, sizeof(pkt->buffer));
|
|
+
|
|
+ return pkt;
|
|
+}
|
|
+
|
|
+/* Set the attrset object by decoding the packet. */
|
|
+static krb5_error_code
|
|
+packet_set_attrset(krb5_context ctx, const char *secret, krad_packet *pkt)
|
|
+{
|
|
+ krb5_data tmp;
|
|
+
|
|
+ tmp = make_data(pkt_attr(pkt), pkt->pkt.length - OFFSET_ATTR);
|
|
+ return kr_attrset_decode(ctx, &tmp, secret, pkt_auth(pkt), &pkt->attrset);
|
|
+}
|
|
+
|
|
+ssize_t
|
|
+krad_packet_bytes_needed(const krb5_data *buffer)
|
|
+{
|
|
+ size_t len;
|
|
+
|
|
+ if (buffer->length < OFFSET_AUTH)
|
|
+ return OFFSET_AUTH - buffer->length;
|
|
+
|
|
+ len = load_16_be(offset(buffer, OFFSET_LENGTH));
|
|
+ if (len > KRAD_PACKET_SIZE_MAX)
|
|
+ return -1;
|
|
+
|
|
+ return buffer->length > len ? 0 : len - buffer->length;
|
|
+}
|
|
+
|
|
+void
|
|
+krad_packet_free(krad_packet *pkt)
|
|
+{
|
|
+ if (pkt)
|
|
+ krad_attrset_free(pkt->attrset);
|
|
+ free(pkt);
|
|
+}
|
|
+
|
|
+/* Create a new request packet. */
|
|
+krb5_error_code
|
|
+krad_packet_new_request(krb5_context ctx, const char *secret, krad_code code,
|
|
+ const krad_attrset *set, krad_packet_iter_cb cb,
|
|
+ void *data, krad_packet **request)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ krad_packet *pkt;
|
|
+ uchar id;
|
|
+ size_t attrset_len;
|
|
+
|
|
+ pkt = packet_new();
|
|
+ if (pkt == NULL) {
|
|
+ if (cb != NULL)
|
|
+ (*cb)(data, TRUE);
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ /* Generate the ID. */
|
|
+ retval = id_generate(ctx, cb, data, &id);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ pkt_id_set(pkt, id);
|
|
+
|
|
+ /* Generate the authenticator. */
|
|
+ retval = auth_generate_random(ctx, pkt_auth(pkt));
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ /* Encode the attributes. */
|
|
+ retval = kr_attrset_encode(set, secret, pkt_auth(pkt), pkt_attr(pkt),
|
|
+ &attrset_len);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ /* Set the code, ID and length. */
|
|
+ pkt->pkt.length = attrset_len + OFFSET_ATTR;
|
|
+ pkt_code_set(pkt, code);
|
|
+ pkt_len_set(pkt, pkt->pkt.length);
|
|
+
|
|
+ /* Copy the attrset for future use. */
|
|
+ retval = packet_set_attrset(ctx, secret, pkt);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ *request = pkt;
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ free(pkt);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/* Create a new request packet. */
|
|
+krb5_error_code
|
|
+krad_packet_new_response(krb5_context ctx, const char *secret, krad_code code,
|
|
+ const krad_attrset *set, const krad_packet *request,
|
|
+ krad_packet **response)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ krad_packet *pkt;
|
|
+ size_t attrset_len;
|
|
+
|
|
+ pkt = packet_new();
|
|
+ if (pkt == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ /* Encode the attributes. */
|
|
+ retval = kr_attrset_encode(set, secret, pkt_auth(request), pkt_attr(pkt),
|
|
+ &attrset_len);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ /* Set the code, ID and length. */
|
|
+ pkt->pkt.length = attrset_len + OFFSET_ATTR;
|
|
+ pkt_code_set(pkt, code);
|
|
+ pkt_id_set(pkt, pkt_id_get(request));
|
|
+ pkt_len_set(pkt, pkt->pkt.length);
|
|
+
|
|
+ /* Generate the authenticator. */
|
|
+ retval = auth_generate_response(ctx, secret, pkt, pkt_auth(request),
|
|
+ pkt_auth(pkt));
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ /* Copy the attrset for future use. */
|
|
+ retval = packet_set_attrset(ctx, secret, pkt);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ *response = pkt;
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ free(pkt);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/* Decode a packet. */
|
|
+static krb5_error_code
|
|
+decode_packet(krb5_context ctx, const char *secret, const krb5_data *buffer,
|
|
+ krad_packet **pkt)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ krad_packet *tmp;
|
|
+ krb5_ui_2 len;
|
|
+
|
|
+ tmp = packet_new();
|
|
+ if (tmp == NULL) {
|
|
+ retval = ENOMEM;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Ensure a proper message length. */
|
|
+ retval = buffer->length < OFFSET_ATTR ? EMSGSIZE : 0;
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ len = load_16_be(offset(buffer, OFFSET_LENGTH));
|
|
+ retval = len < OFFSET_ATTR ? EBADMSG : 0;
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ retval = (len > buffer->length || len > tmp->pkt.length) ? EBADMSG : 0;
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ /* Copy over the buffer. */
|
|
+ tmp->pkt.length = len;
|
|
+ memcpy(tmp->pkt.data, buffer->data, len);
|
|
+
|
|
+ /* Parse the packet to ensure it is well-formed. */
|
|
+ retval = packet_set_attrset(ctx, secret, tmp);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ *pkt = tmp;
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ krad_packet_free(tmp);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+krad_packet_decode_request(krb5_context ctx, const char *secret,
|
|
+ const krb5_data *buffer, krad_packet_iter_cb cb,
|
|
+ void *data, const krad_packet **duppkt,
|
|
+ krad_packet **reqpkt)
|
|
+{
|
|
+ const krad_packet *tmp = NULL;
|
|
+ krb5_error_code retval;
|
|
+
|
|
+ retval = decode_packet(ctx, secret, buffer, reqpkt);
|
|
+ if (cb != NULL && retval == 0) {
|
|
+ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
|
|
+ if (pkt_id_get(*reqpkt) == pkt_id_get(tmp))
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (cb != NULL && (retval != 0 || tmp != NULL))
|
|
+ (*cb)(data, TRUE);
|
|
+
|
|
+ *duppkt = tmp;
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+krad_packet_decode_response(krb5_context ctx, const char *secret,
|
|
+ const krb5_data *buffer, krad_packet_iter_cb cb,
|
|
+ void *data, const krad_packet **reqpkt,
|
|
+ krad_packet **rsppkt)
|
|
+{
|
|
+ uchar auth[AUTH_FIELD_SIZE];
|
|
+ const krad_packet *tmp = NULL;
|
|
+ krb5_error_code retval;
|
|
+
|
|
+ retval = decode_packet(ctx, secret, buffer, rsppkt);
|
|
+ if (cb != NULL && retval == 0) {
|
|
+ for (tmp = (*cb)(data, FALSE); tmp != NULL; tmp = (*cb)(data, FALSE)) {
|
|
+ if (pkt_id_get(*rsppkt) != pkt_id_get(tmp))
|
|
+ continue;
|
|
+
|
|
+ /* Response */
|
|
+ retval = auth_generate_response(ctx, secret, *rsppkt,
|
|
+ pkt_auth(tmp), auth);
|
|
+ if (retval != 0) {
|
|
+ krad_packet_free(*rsppkt);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* If the authenticator matches, then the response is valid. */
|
|
+ if (memcmp(pkt_auth(*rsppkt), auth, sizeof(auth)) == 0)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (cb != NULL && (retval != 0 || tmp != NULL))
|
|
+ (*cb)(data, TRUE);
|
|
+
|
|
+ *reqpkt = tmp;
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+const krb5_data *
|
|
+krad_packet_encode(const krad_packet *pkt)
|
|
+{
|
|
+ return &pkt->pkt;
|
|
+}
|
|
+
|
|
+krad_code
|
|
+krad_packet_get_code(const krad_packet *pkt)
|
|
+{
|
|
+ if (pkt == NULL)
|
|
+ return 0;
|
|
+
|
|
+ return pkt_code_get(pkt);
|
|
+}
|
|
+
|
|
+const krb5_data *
|
|
+krad_packet_get_attr(const krad_packet *pkt, krad_attr type, size_t indx)
|
|
+{
|
|
+ return krad_attrset_get(pkt->attrset, type, indx);
|
|
+}
|
|
diff --git a/src/lib/krad/remote.c b/src/lib/krad/remote.c
|
|
new file mode 100644
|
|
index 0000000..74d9865
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/remote.c
|
|
@@ -0,0 +1,519 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/remote.c - Protocol code for libkrad */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include <k5-int.h>
|
|
+#include <k5-queue.h>
|
|
+#include "internal.h"
|
|
+
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#include <sys/un.h>
|
|
+
|
|
+#define FLAGS_READ (VERTO_EV_FLAG_PERSIST | VERTO_EV_FLAG_IO_CLOSE_FD | \
|
|
+ VERTO_EV_FLAG_IO_ERROR | VERTO_EV_FLAG_IO_READ)
|
|
+#define FLAGS_WRITE (FLAGS_READ | VERTO_EV_FLAG_IO_WRITE)
|
|
+
|
|
+TAILQ_HEAD(request_head, request_st);
|
|
+
|
|
+typedef struct request_st request;
|
|
+struct request_st {
|
|
+ TAILQ_ENTRY(request_st) list;
|
|
+ krad_remote *rr;
|
|
+ krad_packet *request;
|
|
+ krad_cb cb;
|
|
+ void *data;
|
|
+ verto_ev *timer;
|
|
+ int timeout;
|
|
+ size_t retries;
|
|
+ size_t sent;
|
|
+};
|
|
+
|
|
+struct krad_remote_st {
|
|
+ krb5_context kctx;
|
|
+ verto_ctx *vctx;
|
|
+ verto_ev *io;
|
|
+ char *secret;
|
|
+ struct addrinfo *info;
|
|
+ struct request_head list;
|
|
+ char buffer_[KRAD_PACKET_SIZE_MAX];
|
|
+ krb5_data buffer;
|
|
+};
|
|
+
|
|
+static void
|
|
+on_io(verto_ctx *ctx, verto_ev *ev);
|
|
+
|
|
+/* Iterate over the set of outstanding packets. */
|
|
+static const krad_packet *
|
|
+iterator(request **out)
|
|
+{
|
|
+ request *tmp = *out;
|
|
+
|
|
+ if (tmp == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ *out = TAILQ_NEXT(tmp, list);
|
|
+ return tmp->request;
|
|
+}
|
|
+
|
|
+/* Create a new request. */
|
|
+static krb5_error_code
|
|
+request_new(krad_remote *rr, krad_packet *rqst, int timeout, size_t retries,
|
|
+ krad_cb cb, void *data, request **out)
|
|
+{
|
|
+ request *tmp;
|
|
+
|
|
+ tmp = calloc(1, sizeof(request));
|
|
+ if (tmp == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ tmp->rr = rr;
|
|
+ tmp->request = rqst;
|
|
+ tmp->cb = cb;
|
|
+ tmp->data = data;
|
|
+ tmp->timeout = timeout;
|
|
+ tmp->retries = retries;
|
|
+
|
|
+ *out = tmp;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Finish a request, calling the callback and freeing it. */
|
|
+static inline void
|
|
+request_finish(request *req, krb5_error_code retval,
|
|
+ const krad_packet *response)
|
|
+{
|
|
+ if (retval != ETIMEDOUT)
|
|
+ TAILQ_REMOVE(&req->rr->list, req, list);
|
|
+
|
|
+ req->cb(retval, req->request, response, req->data);
|
|
+
|
|
+ if (retval != ETIMEDOUT) {
|
|
+ krad_packet_free(req->request);
|
|
+ verto_del(req->timer);
|
|
+ free(req);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Handle when packets receive no response within their alloted time. */
|
|
+static void
|
|
+on_timeout(verto_ctx *ctx, verto_ev *ev)
|
|
+{
|
|
+ request *req = verto_get_private(ev);
|
|
+
|
|
+ req->timer = NULL; /* Void the timer event. */
|
|
+
|
|
+ /* If we have more retries to perform, resend the packet. */
|
|
+ if (req->retries-- > 1) {
|
|
+ req->sent = 0;
|
|
+ verto_set_flags(req->rr->io, FLAGS_WRITE);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ request_finish(req, ETIMEDOUT, NULL);
|
|
+}
|
|
+
|
|
+/* Connect to the remote host. */
|
|
+static krb5_error_code
|
|
+remote_connect(krad_remote *rr)
|
|
+{
|
|
+ int i, sock = -1;
|
|
+ verto_ev *ev;
|
|
+
|
|
+ sock = socket(rr->info->ai_family, rr->info->ai_socktype,
|
|
+ rr->info->ai_protocol);
|
|
+ if (sock < 0)
|
|
+ return errno;
|
|
+
|
|
+ i = connect(sock, rr->info->ai_addr, rr->info->ai_addrlen);
|
|
+ if (i < 0) {
|
|
+ i = errno;
|
|
+ close(sock);
|
|
+ return i;
|
|
+ }
|
|
+
|
|
+ ev = verto_add_io(rr->vctx, FLAGS_READ, on_io, sock);
|
|
+ if (ev == NULL) {
|
|
+ close(sock);
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ rr->io = ev;
|
|
+ verto_set_private(rr->io, rr, NULL);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Disconnect and reconnect to the remote host. */
|
|
+static krb5_error_code
|
|
+remote_reconnect(krad_remote *rr, int errnum)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ const krb5_data *tmp;
|
|
+ request *r;
|
|
+
|
|
+ verto_del(rr->io);
|
|
+ rr->io = NULL;
|
|
+ retval = remote_connect(rr);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ TAILQ_FOREACH(r, &rr->list, list) {
|
|
+ tmp = krad_packet_encode(r->request);
|
|
+
|
|
+ if (r->sent == tmp->length) {
|
|
+ /* Error out sent requests. */
|
|
+ request_finish(r, errnum, NULL);
|
|
+ } else {
|
|
+ /* Reset partially sent requests. */
|
|
+ r->sent = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Close the connection and call the callbacks of all oustanding requests. */
|
|
+static void
|
|
+remote_shutdown(krad_remote *rr, int errnum)
|
|
+{
|
|
+ verto_del(rr->io);
|
|
+ rr->io = NULL;
|
|
+ while (!TAILQ_EMPTY(&rr->list))
|
|
+ request_finish(TAILQ_FIRST(&rr->list), errnum, NULL);
|
|
+}
|
|
+
|
|
+/* Write data to the socket. */
|
|
+static void
|
|
+on_io_write(krad_remote *rr)
|
|
+{
|
|
+ const krb5_data *tmp;
|
|
+ request *r;
|
|
+ int i;
|
|
+
|
|
+ TAILQ_FOREACH(r, &rr->list, list) {
|
|
+ tmp = krad_packet_encode(r->request);
|
|
+
|
|
+ /* If the packet has already been sent, do nothing. */
|
|
+ if (r->sent == tmp->length)
|
|
+ continue;
|
|
+
|
|
+ /* Send the packet. */
|
|
+ i = sendto(verto_get_fd(rr->io), tmp->data + r->sent,
|
|
+ tmp->length - r->sent, 0, NULL, 0);
|
|
+ if (i < 0) {
|
|
+ /* Should we try again? */
|
|
+ if (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS ||
|
|
+ errno == EINTR)
|
|
+ return;
|
|
+
|
|
+ /* In this case, we need to re-connect. */
|
|
+ i = remote_reconnect(rr, errno);
|
|
+ if (i == 0)
|
|
+ return;
|
|
+
|
|
+ /* Do a full reset. */
|
|
+ remote_shutdown(rr, i);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* SOCK_STREAM permits partial writes. */
|
|
+ if (rr->info->ai_socktype == SOCK_STREAM)
|
|
+ r->sent += i;
|
|
+ else if (i == (int)tmp->length)
|
|
+ r->sent = i;
|
|
+
|
|
+ /* If the packet was completely sent, set a timeout. */
|
|
+ if (r->sent == tmp->length) {
|
|
+ verto_del(r->timer);
|
|
+ r->timer = verto_add_timeout(rr->vctx, VERTO_EV_FLAG_NONE,
|
|
+ on_timeout, r->timeout);
|
|
+ if (r->timer == NULL)
|
|
+ request_finish(r, ENOMEM, NULL);
|
|
+ else
|
|
+ verto_set_private(r->timer, r, NULL);
|
|
+ }
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ verto_set_flags(rr->io, FLAGS_READ);
|
|
+}
|
|
+
|
|
+/* Read data from the socket. */
|
|
+static void
|
|
+on_io_read(krad_remote *rr)
|
|
+{
|
|
+ const krad_packet *req = NULL;
|
|
+ krad_packet *rsp = NULL;
|
|
+ krb5_error_code retval;
|
|
+ ssize_t pktlen;
|
|
+ request *tmp, *r;
|
|
+ int i;
|
|
+
|
|
+ pktlen = sizeof(rr->buffer_);
|
|
+ if (rr->info->ai_socktype == SOCK_STREAM) {
|
|
+ pktlen = krad_packet_bytes_needed(&rr->buffer);
|
|
+ if (pktlen < 0) {
|
|
+ retval = remote_reconnect(rr, EBADMSG);
|
|
+ if (retval != 0)
|
|
+ remote_shutdown(rr, retval);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Read the packet. */
|
|
+ i = recv(verto_get_fd(rr->io), rr->buffer.data + rr->buffer.length,
|
|
+ pktlen - rr->buffer.length, 0);
|
|
+ if (i < 0) {
|
|
+ /* Should we try again? */
|
|
+ if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)
|
|
+ return;
|
|
+
|
|
+ if (errno == ECONNREFUSED || errno == ECONNRESET ||
|
|
+ errno == ENOTCONN) {
|
|
+ /*
|
|
+ * When doing UDP against a local socket, the kernel will notify
|
|
+ * when the daemon closes. But not against remote sockets. We want
|
|
+ * to treat them both the same. Returning here will cause an
|
|
+ * eventual timeout.
|
|
+ */
|
|
+ if (rr->info->ai_socktype != SOCK_STREAM)
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* In this case, we need to re-connect. */
|
|
+ i = remote_reconnect(rr, errno);
|
|
+ if (i == 0)
|
|
+ return;
|
|
+
|
|
+ /* Do a full reset. */
|
|
+ remote_shutdown(rr, i);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* If we have a partial read or just the header, try again. */
|
|
+ rr->buffer.length += i;
|
|
+ pktlen = krad_packet_bytes_needed(&rr->buffer);
|
|
+ if (rr->info->ai_socktype == SOCK_STREAM && pktlen > 0)
|
|
+ return;
|
|
+
|
|
+ /* Decode the packet. */
|
|
+ tmp = TAILQ_FIRST(&rr->list);
|
|
+ retval = krad_packet_decode_response(rr->kctx, rr->secret, &rr->buffer,
|
|
+ (krad_packet_iter_cb)iterator, &tmp,
|
|
+ &req, &rsp);
|
|
+ rr->buffer.length = 0;
|
|
+ if (retval != 0)
|
|
+ return;
|
|
+
|
|
+ /* Match the response with an outstanding request. */
|
|
+ if (req != NULL) {
|
|
+ TAILQ_FOREACH(r, &rr->list, list) {
|
|
+ if (r->request == req &&
|
|
+ r->sent == krad_packet_encode(req)->length) {
|
|
+ request_finish(r, 0, rsp);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ krad_packet_free(rsp);
|
|
+}
|
|
+
|
|
+/* Handle when IO is ready on the socket. */
|
|
+static void
|
|
+on_io(verto_ctx *ctx, verto_ev *ev)
|
|
+{
|
|
+ krad_remote *rr;
|
|
+
|
|
+ rr = verto_get_private(ev);
|
|
+
|
|
+ if (verto_get_fd_state(ev) & VERTO_EV_FLAG_IO_WRITE)
|
|
+ on_io_write(rr);
|
|
+ else
|
|
+ on_io_read(rr);
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+kr_remote_new(krb5_context kctx, verto_ctx *vctx, const struct addrinfo *info,
|
|
+ const char *secret, krad_remote **rr)
|
|
+{
|
|
+ krb5_error_code retval = ENOMEM;
|
|
+ krad_remote *tmp = NULL;
|
|
+
|
|
+ tmp = calloc(1, sizeof(krad_remote));
|
|
+ if (tmp == NULL)
|
|
+ goto error;
|
|
+ tmp->kctx = kctx;
|
|
+ tmp->vctx = vctx;
|
|
+ tmp->buffer = make_data(tmp->buffer_, 0);
|
|
+ TAILQ_INIT(&tmp->list);
|
|
+
|
|
+ tmp->secret = strdup(secret);
|
|
+ if (tmp->secret == NULL)
|
|
+ goto error;
|
|
+
|
|
+ tmp->info = k5memdup(info, sizeof(*info), &retval);
|
|
+ if (tmp->info == NULL)
|
|
+ goto error;
|
|
+
|
|
+ tmp->info->ai_addr = k5memdup(info->ai_addr, info->ai_addrlen, &retval);
|
|
+ if (tmp->info == NULL)
|
|
+ goto error;
|
|
+ tmp->info->ai_next = NULL;
|
|
+ tmp->info->ai_canonname = NULL;
|
|
+
|
|
+ retval = remote_connect(tmp);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ *rr = tmp;
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ kr_remote_free(tmp);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+void
|
|
+kr_remote_free(krad_remote *rr)
|
|
+{
|
|
+ if (rr == NULL)
|
|
+ return;
|
|
+
|
|
+ while (!TAILQ_EMPTY(&rr->list))
|
|
+ request_finish(TAILQ_FIRST(&rr->list), ECANCELED, NULL);
|
|
+
|
|
+ free(rr->secret);
|
|
+ if (rr->info != NULL)
|
|
+ free(rr->info->ai_addr);
|
|
+ free(rr->info);
|
|
+ verto_del(rr->io);
|
|
+ free(rr);
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+kr_remote_send(krad_remote *rr, krad_code code, krad_attrset *attrs,
|
|
+ krad_cb cb, void *data, int timeout, size_t retries,
|
|
+ const krad_packet **pkt)
|
|
+{
|
|
+ krad_packet *tmp = NULL;
|
|
+ krb5_error_code retval;
|
|
+ request *r;
|
|
+
|
|
+ r = TAILQ_FIRST(&rr->list);
|
|
+ retval = krad_packet_new_request(rr->kctx, rr->secret, code, attrs,
|
|
+ (krad_packet_iter_cb)iterator, &r, &tmp);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ TAILQ_FOREACH(r, &rr->list, list) {
|
|
+ if (r->request == tmp) {
|
|
+ retval = EALREADY;
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (rr->io == NULL) {
|
|
+ retval = remote_connect(rr);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ if (rr->info->ai_socktype == SOCK_STREAM)
|
|
+ retries = 0;
|
|
+ timeout = timeout / (retries + 1);
|
|
+ retval = request_new(rr, tmp, timeout, retries, cb, data, &r);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ if ((verto_get_flags(rr->io) & VERTO_EV_FLAG_IO_WRITE) == 0)
|
|
+ verto_set_flags(rr->io, FLAGS_WRITE);
|
|
+
|
|
+ TAILQ_INSERT_TAIL(&rr->list, r, list);
|
|
+ if (pkt != NULL)
|
|
+ *pkt = tmp;
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ krad_packet_free(tmp);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+void
|
|
+kr_remote_cancel(krad_remote *rr, const krad_packet *pkt)
|
|
+{
|
|
+ request *r;
|
|
+
|
|
+ TAILQ_FOREACH(r, &rr->list, list) {
|
|
+ if (r->request == pkt) {
|
|
+ request_finish(r, ECANCELED, NULL);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+krb5_boolean
|
|
+kr_remote_equals(const krad_remote *rr, const struct addrinfo *info,
|
|
+ const char *secret)
|
|
+{
|
|
+ struct sockaddr_un *a, *b;
|
|
+
|
|
+ if (strcmp(rr->secret, secret) != 0)
|
|
+ return FALSE;
|
|
+
|
|
+ if (info->ai_addrlen != rr->info->ai_addrlen)
|
|
+ return FALSE;
|
|
+
|
|
+ if (info->ai_family != rr->info->ai_family)
|
|
+ return FALSE;
|
|
+
|
|
+ if (info->ai_socktype != rr->info->ai_socktype)
|
|
+ return FALSE;
|
|
+
|
|
+ if (info->ai_protocol != rr->info->ai_protocol)
|
|
+ return FALSE;
|
|
+
|
|
+ if (info->ai_flags != rr->info->ai_flags)
|
|
+ return FALSE;
|
|
+
|
|
+ if (memcmp(rr->info->ai_addr, info->ai_addr, info->ai_addrlen) != 0) {
|
|
+ /* AF_UNIX fails the memcmp() test due to uninitialized bytes after the
|
|
+ * socket name. */
|
|
+ if (info->ai_family != AF_UNIX)
|
|
+ return FALSE;
|
|
+
|
|
+ a = (struct sockaddr_un *)info->ai_addr;
|
|
+ b = (struct sockaddr_un *)rr->info->ai_addr;
|
|
+ if (strncmp(a->sun_path, b->sun_path, sizeof(a->sun_path)) != 0)
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ return TRUE;
|
|
+}
|
|
diff --git a/src/lib/krad/t_attr.c b/src/lib/krad/t_attr.c
|
|
new file mode 100644
|
|
index 0000000..e80d77b
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_attr.c
|
|
@@ -0,0 +1,89 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_attr.c - Attribute test program */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "t_test.h"
|
|
+
|
|
+const static char encoded[] = {
|
|
+ 0xba, 0xfc, 0xed, 0x50, 0xe1, 0xeb, 0xa6, 0xc3,
|
|
+ 0xc1, 0x75, 0x20, 0xe9, 0x10, 0xce, 0xc2, 0xcb
|
|
+};
|
|
+
|
|
+const static unsigned char auth[] = {
|
|
+ 0xac, 0x9d, 0xc1, 0x62, 0x08, 0xc4, 0xc7, 0x8b,
|
|
+ 0xa1, 0x2f, 0x25, 0x0a, 0xc4, 0x1d, 0x36, 0x41
|
|
+};
|
|
+
|
|
+int
|
|
+main()
|
|
+{
|
|
+ unsigned char outbuf[MAX_ATTRSETSIZE];
|
|
+ const char *decoded = "accept";
|
|
+ const char *secret = "foo";
|
|
+ krb5_error_code retval;
|
|
+ krb5_context ctx;
|
|
+ const char *tmp;
|
|
+ krb5_data in;
|
|
+ size_t len;
|
|
+
|
|
+ noerror(krb5_init_context(&ctx));
|
|
+
|
|
+ /* Make sure User-Name is 1. */
|
|
+ insist(krad_attr_name2num("User-Name") == 1);
|
|
+
|
|
+ /* Make sure 2 is User-Password. */
|
|
+ tmp = krad_attr_num2name(2);
|
|
+ insist(tmp != NULL);
|
|
+ insist(strcmp(tmp, "User-Password") == 0);
|
|
+
|
|
+ /* Test decoding. */
|
|
+ in = make_data((void *)encoded, sizeof(encoded));
|
|
+ noerror(kr_attr_decode(ctx, secret, auth,
|
|
+ krad_attr_name2num("User-Password"),
|
|
+ &in, outbuf, &len));
|
|
+ insist(len == strlen(decoded));
|
|
+ insist(memcmp(outbuf, decoded, len) == 0);
|
|
+
|
|
+ /* Test encoding. */
|
|
+ in = string2data((char *)decoded);
|
|
+ retval = kr_attr_encode(ctx, secret, auth,
|
|
+ krad_attr_name2num("User-Password"),
|
|
+ &in, outbuf, &len);
|
|
+ insist(retval == 0);
|
|
+ insist(len == sizeof(encoded));
|
|
+ insist(memcmp(outbuf, encoded, len) == 0);
|
|
+
|
|
+ /* Test constraint. */
|
|
+ in.length = 100;
|
|
+ insist(kr_attr_valid(krad_attr_name2num("User-Password"), &in) == 0);
|
|
+ in.length = 200;
|
|
+ insist(kr_attr_valid(krad_attr_name2num("User-Password"), &in) != 0);
|
|
+
|
|
+ krb5_free_context(ctx);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/lib/krad/t_attrset.c b/src/lib/krad/t_attrset.c
|
|
new file mode 100644
|
|
index 0000000..afae5e4
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_attrset.c
|
|
@@ -0,0 +1,98 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_attrset.c - Attribute set test program */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "t_test.h"
|
|
+
|
|
+const static unsigned char auth[] = {
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
+};
|
|
+
|
|
+const static char encpass[] = {
|
|
+ 0x58, 0x8d, 0xff, 0xda, 0x37, 0xf9, 0xe4, 0xca,
|
|
+ 0x19, 0xae, 0x49, 0xb7, 0x16, 0x6d, 0x58, 0x27
|
|
+};
|
|
+
|
|
+int
|
|
+main()
|
|
+{
|
|
+ unsigned char buffer[KRAD_PACKET_SIZE_MAX], encoded[MAX_ATTRSETSIZE];
|
|
+ const char *username = "testUser", *password = "accept";
|
|
+ const krb5_data *tmpp;
|
|
+ krad_attrset *set;
|
|
+ krb5_context ctx;
|
|
+ size_t len = 0, encode_len;
|
|
+ krb5_data tmp;
|
|
+
|
|
+ noerror(krb5_init_context(&ctx));
|
|
+ noerror(krad_attrset_new(ctx, &set));
|
|
+
|
|
+ /* Add username. */
|
|
+ tmp = string2data((char *)username);
|
|
+ noerror(krad_attrset_add(set, krad_attr_name2num("User-Name"), &tmp));
|
|
+
|
|
+ /* Add password. */
|
|
+ tmp = string2data((char *)password);
|
|
+ noerror(krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp));
|
|
+
|
|
+ /* Encode attrset. */
|
|
+ noerror(kr_attrset_encode(set, "foo", auth, buffer, &encode_len));
|
|
+ krad_attrset_free(set);
|
|
+
|
|
+ /* Manually encode User-Name. */
|
|
+ encoded[len + 0] = krad_attr_name2num("User-Name");
|
|
+ encoded[len + 1] = strlen(username) + 2;
|
|
+ memcpy(encoded + len + 2, username, strlen(username));
|
|
+ len += encoded[len + 1];
|
|
+
|
|
+ /* Manually encode User-Password. */
|
|
+ encoded[len + 0] = krad_attr_name2num("User-Password");
|
|
+ encoded[len + 1] = sizeof(encpass) + 2;
|
|
+ memcpy(encoded + len + 2, encpass, sizeof(encpass));
|
|
+ len += encoded[len + 1];
|
|
+
|
|
+ /* Compare output. */
|
|
+ insist(len == encode_len);
|
|
+ insist(memcmp(encoded, buffer, len) == 0);
|
|
+
|
|
+ /* Decode output. */
|
|
+ tmp = make_data(buffer, len);
|
|
+ noerror(kr_attrset_decode(ctx, &tmp, "foo", auth, &set));
|
|
+
|
|
+ /* Test getting an attribute. */
|
|
+ tmp = string2data((char *)username);
|
|
+ tmpp = krad_attrset_get(set, krad_attr_name2num("User-Name"), 0);
|
|
+ insist(tmpp != NULL);
|
|
+ insist(tmpp->length == tmp.length);
|
|
+ insist(strncmp(tmpp->data, tmp.data, tmp.length) == 0);
|
|
+
|
|
+ krad_attrset_free(set);
|
|
+ krb5_free_context(ctx);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/lib/krad/t_client.c b/src/lib/krad/t_client.c
|
|
new file mode 100644
|
|
index 0000000..3d0fda9
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_client.c
|
|
@@ -0,0 +1,126 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_client.c - Client request test program */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "t_daemon.h"
|
|
+
|
|
+#define EVENT_COUNT 4
|
|
+
|
|
+static struct
|
|
+{
|
|
+ int count;
|
|
+ struct event events[EVENT_COUNT];
|
|
+} record;
|
|
+
|
|
+static verto_ctx *vctx;
|
|
+
|
|
+static void
|
|
+callback(krb5_error_code retval, const krad_packet *request,
|
|
+ const krad_packet *response, void *data)
|
|
+{
|
|
+ struct event *evt;
|
|
+
|
|
+ evt = &record.events[record.count++];
|
|
+ evt->error = retval != 0;
|
|
+ if (evt->error)
|
|
+ evt->result.retval = retval;
|
|
+ else
|
|
+ evt->result.code = krad_packet_get_code(response);
|
|
+ verto_break(vctx);
|
|
+}
|
|
+
|
|
+int
|
|
+main(int argc, const char **argv)
|
|
+{
|
|
+ krad_attrset *attrs;
|
|
+ krad_client *rc;
|
|
+ krb5_context kctx;
|
|
+ krb5_data tmp;
|
|
+
|
|
+ if (!daemon_start(argc, argv)) {
|
|
+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ noerror(krb5_init_context(&kctx));
|
|
+ vctx = verto_new(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_TIMEOUT);
|
|
+ insist(vctx != NULL);
|
|
+ noerror(krad_client_new(kctx, vctx, &rc));
|
|
+
|
|
+ tmp = string2data("testUser");
|
|
+ noerror(krad_attrset_new(kctx, &attrs));
|
|
+ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Name"), &tmp));
|
|
+
|
|
+ /* Test accept. */
|
|
+ tmp = string2data("accept");
|
|
+ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Password"),
|
|
+ &tmp));
|
|
+ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
|
|
+ "localhost", "foo", 1000, 3, callback, NULL));
|
|
+ verto_run(vctx);
|
|
+
|
|
+ /* Test reject. */
|
|
+ tmp = string2data("reject");
|
|
+ krad_attrset_del(attrs, krad_attr_name2num("User-Password"), 0);
|
|
+ noerror(krad_attrset_add(attrs, krad_attr_name2num("User-Password"),
|
|
+ &tmp));
|
|
+ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
|
|
+ "localhost", "foo", 1000, 3, callback, NULL));
|
|
+ verto_run(vctx);
|
|
+
|
|
+ /* Test timeout. */
|
|
+ daemon_stop();
|
|
+ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
|
|
+ "localhost", "foo", 1000, 3, callback, NULL));
|
|
+ verto_run(vctx);
|
|
+
|
|
+ /* Test outstanding packet freeing. */
|
|
+ noerror(krad_client_send(rc, krad_code_name2num("Access-Request"), attrs,
|
|
+ "localhost", "foo", 1000, 3, callback, NULL));
|
|
+ krad_client_free(rc);
|
|
+ rc = NULL;
|
|
+
|
|
+ /* Verify the results. */
|
|
+ insist(record.count == EVENT_COUNT);
|
|
+ insist(record.events[0].error == FALSE);
|
|
+ insist(record.events[0].result.code ==
|
|
+ krad_code_name2num("Access-Accept"));
|
|
+ insist(record.events[1].error == FALSE);
|
|
+ insist(record.events[1].result.code ==
|
|
+ krad_code_name2num("Access-Reject"));
|
|
+ insist(record.events[2].error == TRUE);
|
|
+ insist(record.events[2].result.retval == ETIMEDOUT);
|
|
+ insist(record.events[3].error == TRUE);
|
|
+ insist(record.events[3].result.retval == ECANCELED);
|
|
+
|
|
+ krad_attrset_free(attrs);
|
|
+ krad_client_free(rc);
|
|
+ verto_free(vctx);
|
|
+ krb5_free_context(kctx);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/lib/krad/t_code.c b/src/lib/krad/t_code.c
|
|
new file mode 100644
|
|
index 0000000..b245a7e
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_code.c
|
|
@@ -0,0 +1,54 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_code.c - RADIUS code table test program */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "t_test.h"
|
|
+
|
|
+int
|
|
+main()
|
|
+{
|
|
+ const char *tmp;
|
|
+
|
|
+ insist(krad_code_name2num("Access-Request") == 1);
|
|
+ insist(krad_code_name2num("Access-Accept") == 2);
|
|
+ insist(krad_code_name2num("Access-Reject") == 3);
|
|
+
|
|
+ tmp = krad_code_num2name(1);
|
|
+ insist(tmp != NULL);
|
|
+ insist(strcmp(tmp, "Access-Request") == 0);
|
|
+
|
|
+ tmp = krad_code_num2name(2);
|
|
+ insist(tmp != NULL);
|
|
+ insist(strcmp(tmp, "Access-Accept") == 0);
|
|
+
|
|
+ tmp = krad_code_num2name(3);
|
|
+ insist(tmp != NULL);
|
|
+ insist(strcmp(tmp, "Access-Reject") == 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/lib/krad/t_daemon.h b/src/lib/krad/t_daemon.h
|
|
new file mode 100644
|
|
index 0000000..7c345a6
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_daemon.h
|
|
@@ -0,0 +1,92 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_daemon.h - Daemonization helper for RADIUS test programs */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef T_DAEMON_H_
|
|
+#define T_DAEMON_H_
|
|
+
|
|
+#include "t_test.h"
|
|
+#include <libgen.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+
|
|
+static pid_t daemon_pid;
|
|
+
|
|
+static void
|
|
+daemon_stop(void)
|
|
+{
|
|
+ if (daemon_pid == 0)
|
|
+ return;
|
|
+ kill(daemon_pid, SIGTERM);
|
|
+ waitpid(daemon_pid, NULL, 0);
|
|
+ daemon_pid = 0;
|
|
+}
|
|
+
|
|
+static krb5_boolean
|
|
+daemon_start(int argc, const char **argv)
|
|
+{
|
|
+ sigset_t set;
|
|
+ int sig;
|
|
+
|
|
+ if (argc != 3 || argv == NULL)
|
|
+ return FALSE;
|
|
+
|
|
+ if (daemon_pid != 0)
|
|
+ return TRUE;
|
|
+
|
|
+ if (sigemptyset(&set) != 0)
|
|
+ return FALSE;
|
|
+
|
|
+ if (sigaddset(&set, SIGUSR1) != 0)
|
|
+ return FALSE;
|
|
+
|
|
+ if (sigaddset(&set, SIGCHLD) != 0)
|
|
+ return FALSE;
|
|
+
|
|
+ if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
|
|
+ return FALSE;
|
|
+
|
|
+ daemon_pid = fork();
|
|
+ if (daemon_pid == 0) {
|
|
+ close(STDOUT_FILENO);
|
|
+ open("/dev/null", O_WRONLY);
|
|
+ exit(execlp(argv[1], argv[1], argv[2], NULL));
|
|
+ }
|
|
+
|
|
+ if (sigwait(&set, &sig) != 0 || sig == SIGCHLD) {
|
|
+ daemon_stop();
|
|
+ daemon_pid = 0;
|
|
+ return FALSE;
|
|
+ }
|
|
+
|
|
+ atexit(daemon_stop);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+#endif /* T_DAEMON_H_ */
|
|
diff --git a/src/lib/krad/t_daemon.py b/src/lib/krad/t_daemon.py
|
|
new file mode 100644
|
|
index 0000000..d62bc29
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_daemon.py
|
|
@@ -0,0 +1,76 @@
|
|
+#!/usr/bin/python
|
|
+#
|
|
+# Copyright 2013 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:
|
|
+#
|
|
+# 1. Redistributions of source code must retain the above copyright
|
|
+# notice, this list of conditions and the following disclaimer.
|
|
+#
|
|
+# 2. 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.
|
|
+#
|
|
+# 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.
|
|
+
|
|
+import StringIO
|
|
+import os
|
|
+import sys
|
|
+import signal
|
|
+
|
|
+try:
|
|
+ from pyrad import dictionary, packet, server
|
|
+except ImportError:
|
|
+ sys.stdout.write("pyrad not found!\n")
|
|
+ sys.exit(0)
|
|
+
|
|
+# We could use a dictionary file, but since we need
|
|
+# such few attributes, we'll just include them here
|
|
+DICTIONARY = """
|
|
+ATTRIBUTE\tUser-Name\t1\tstring
|
|
+ATTRIBUTE\tUser-Password\t2\toctets
|
|
+ATTRIBUTE\tNAS-Identifier\t32\tstring
|
|
+"""
|
|
+
|
|
+class TestServer(server.Server):
|
|
+ def _HandleAuthPacket(self, pkt):
|
|
+ server.Server._HandleAuthPacket(self, pkt)
|
|
+
|
|
+ passwd = []
|
|
+
|
|
+ print "Request: "
|
|
+ for key in pkt.keys():
|
|
+ if key == "User-Password":
|
|
+ passwd = map(pkt.PwDecrypt, pkt[key])
|
|
+ print "\t%s\t%s" % (key, passwd)
|
|
+ else:
|
|
+ print "\t%s\t%s" % (key, pkt[key])
|
|
+
|
|
+ reply = self.CreateReplyPacket(pkt)
|
|
+ if passwd == ['accept']:
|
|
+ reply.code = packet.AccessAccept
|
|
+ print "Response: %s" % "Access-Accept"
|
|
+ else:
|
|
+ reply.code = packet.AccessReject
|
|
+ print "Response: %s" % "Access-Reject"
|
|
+ print
|
|
+ self.SendReplyPacket(pkt.fd, reply)
|
|
+
|
|
+srv = TestServer(addresses=["localhost"],
|
|
+ hosts={"127.0.0.1":
|
|
+ server.RemoteHost("127.0.0.1", "foo", "localhost")},
|
|
+ dict=dictionary.Dictionary(StringIO.StringIO(DICTIONARY)))
|
|
+os.kill(os.getppid(), signal.SIGUSR1)
|
|
+srv.Run()
|
|
diff --git a/src/lib/krad/t_packet.c b/src/lib/krad/t_packet.c
|
|
new file mode 100644
|
|
index 0000000..0a92e9c
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_packet.c
|
|
@@ -0,0 +1,194 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_packet.c - RADIUS packet test program */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "t_daemon.h"
|
|
+
|
|
+#define ACCEPT_PACKET 0
|
|
+#define REJECT_PACKET 1
|
|
+
|
|
+static krad_packet *packets[3];
|
|
+
|
|
+static const krad_packet *
|
|
+iterator(void *data, krb5_boolean cancel)
|
|
+{
|
|
+ krad_packet *tmp;
|
|
+ int *i = data;
|
|
+
|
|
+ if (cancel || packets[*i] == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ tmp = packets[*i];
|
|
+ *i += 1;
|
|
+ return tmp;
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+make_packet(krb5_context ctx, const krb5_data *username,
|
|
+ const krb5_data *password, krad_packet **pkt)
|
|
+{
|
|
+ krad_attrset *set = NULL;
|
|
+ krad_packet *tmp = NULL;
|
|
+ krb5_error_code retval;
|
|
+ const krb5_data *data;
|
|
+ int i = 0;
|
|
+
|
|
+ retval = krad_attrset_new(ctx, &set);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+
|
|
+ retval = krad_attrset_add(set, krad_attr_name2num("User-Name"), username);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+
|
|
+ retval = krad_attrset_add(set, krad_attr_name2num("User-Password"),
|
|
+ password);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+
|
|
+ retval = krad_packet_new_request(ctx, "foo",
|
|
+ krad_code_name2num("Access-Request"),
|
|
+ set, iterator, &i, &tmp);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+
|
|
+ data = krad_packet_get_attr(tmp, krad_attr_name2num("User-Name"), 0);
|
|
+ if (data == NULL) {
|
|
+ retval = ENOENT;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (data->length != username->length ||
|
|
+ memcmp(data->data, username->data, data->length) != 0) {
|
|
+ retval = EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ *pkt = tmp;
|
|
+ tmp = NULL;
|
|
+
|
|
+out:
|
|
+ krad_attrset_free(set);
|
|
+ krad_packet_free(tmp);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+do_auth(krb5_context ctx, struct addrinfo *ai, const char *secret,
|
|
+ const krad_packet *rqst, krb5_boolean *auth)
|
|
+{
|
|
+ const krad_packet *req = NULL;
|
|
+ char tmp[KRAD_PACKET_SIZE_MAX];
|
|
+ const krb5_data *request;
|
|
+ krad_packet *rsp = NULL;
|
|
+ krb5_error_code retval;
|
|
+ krb5_data response;
|
|
+ int sock = -1, i;
|
|
+
|
|
+ response = make_data(tmp, sizeof(tmp));
|
|
+
|
|
+ sock = socket(ai->ai_family, ai->ai_socktype, 0);
|
|
+ if (sock < 0) {
|
|
+ retval = errno;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ request = krad_packet_encode(rqst);
|
|
+ if (sendto(sock, request->data, request->length, 0, ai->ai_addr,
|
|
+ ai->ai_addrlen) < 0) {
|
|
+ retval = errno;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ i = recv(sock, response.data, sizeof(tmp), 0);
|
|
+ if (i < 0) {
|
|
+ retval = errno;
|
|
+ goto out;
|
|
+ }
|
|
+ response.length = i;
|
|
+
|
|
+ i = 0;
|
|
+ retval = krad_packet_decode_response(ctx, secret, &response, iterator, &i,
|
|
+ &req, &rsp);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+
|
|
+ if (req != rqst) {
|
|
+ retval = EBADMSG;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ *auth = krad_packet_get_code(rsp) == krad_code_name2num("Access-Accept");
|
|
+
|
|
+out:
|
|
+ krad_packet_free(rsp);
|
|
+ if (sock >= 0)
|
|
+ close(sock);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+int
|
|
+main(int argc, const char **argv)
|
|
+{
|
|
+ struct addrinfo *ai = NULL, hints;
|
|
+ krb5_data username, password;
|
|
+ krb5_boolean auth = FALSE;
|
|
+ krb5_context ctx;
|
|
+
|
|
+ username = string2data("testUser");
|
|
+
|
|
+ if (!daemon_start(argc, argv)) {
|
|
+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ noerror(krb5_init_context(&ctx));
|
|
+
|
|
+ password = string2data("accept");
|
|
+ noerror(make_packet(ctx, &username, &password, &packets[ACCEPT_PACKET]));
|
|
+
|
|
+ password = string2data("reject");
|
|
+ noerror(make_packet(ctx, &username, &password, &packets[REJECT_PACKET]));
|
|
+
|
|
+ memset(&hints, 0, sizeof(hints));
|
|
+ hints.ai_family = AF_INET;
|
|
+ hints.ai_socktype = SOCK_DGRAM;
|
|
+ noerror(gai_error_code(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
|
|
+
|
|
+ noerror(do_auth(ctx, ai, "foo", packets[ACCEPT_PACKET], &auth));
|
|
+ insist(auth == TRUE);
|
|
+
|
|
+ noerror(do_auth(ctx, ai, "foo", packets[REJECT_PACKET], &auth));
|
|
+ insist(auth == FALSE);
|
|
+
|
|
+ krad_packet_free(packets[ACCEPT_PACKET]);
|
|
+ krad_packet_free(packets[REJECT_PACKET]);
|
|
+ krb5_free_context(ctx);
|
|
+ freeaddrinfo(ai);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/lib/krad/t_remote.c b/src/lib/krad/t_remote.c
|
|
new file mode 100644
|
|
index 0000000..a521ecb
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_remote.c
|
|
@@ -0,0 +1,170 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_remote.c - Protocol test program */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "t_daemon.h"
|
|
+
|
|
+#define EVENT_COUNT 6
|
|
+
|
|
+static struct
|
|
+{
|
|
+ int count;
|
|
+ struct event events[EVENT_COUNT];
|
|
+} record;
|
|
+
|
|
+static krad_attrset *set;
|
|
+static krad_remote *rr;
|
|
+static verto_ctx *vctx;
|
|
+
|
|
+static void
|
|
+callback(krb5_error_code retval, const krad_packet *request,
|
|
+ const krad_packet *response, void *data)
|
|
+{
|
|
+ struct event *evt;
|
|
+
|
|
+ evt = &record.events[record.count++];
|
|
+ evt->error = retval != 0;
|
|
+ if (evt->error)
|
|
+ evt->result.retval = retval;
|
|
+ else
|
|
+ evt->result.code = krad_packet_get_code(response);
|
|
+ verto_break(vctx);
|
|
+}
|
|
+
|
|
+static void
|
|
+remote_new(krb5_context kctx, krad_remote **remote)
|
|
+{
|
|
+ struct addrinfo *ai = NULL, hints;
|
|
+
|
|
+ memset(&hints, 0, sizeof(hints));
|
|
+ hints.ai_family = AF_INET;
|
|
+ hints.ai_socktype = SOCK_DGRAM;
|
|
+ noerror(gai_error_code(getaddrinfo("127.0.0.1", "radius", &hints, &ai)));
|
|
+
|
|
+ noerror(kr_remote_new(kctx, vctx, ai, "foo", remote));
|
|
+ insist(kr_remote_equals(*remote, ai, "foo"));
|
|
+ freeaddrinfo(ai);
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+do_auth(const char *password, const krad_packet **pkt)
|
|
+{
|
|
+ const krad_packet *tmppkt;
|
|
+ krb5_error_code retval;
|
|
+ krb5_data tmp = string2data((char *)password);
|
|
+
|
|
+ retval = krad_attrset_add(set, krad_attr_name2num("User-Password"), &tmp);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ retval = kr_remote_send(rr, krad_code_name2num("Access-Request"), set,
|
|
+ callback, NULL, 1000, 3, &tmppkt);
|
|
+ krad_attrset_del(set, krad_attr_name2num("User-Password"), 0);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ if (pkt != NULL)
|
|
+ *pkt = tmppkt;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+test_timeout(verto_ctx *ctx, verto_ev *ev)
|
|
+{
|
|
+ static const krad_packet *pkt;
|
|
+
|
|
+ noerror(do_auth("accept", &pkt));
|
|
+ kr_remote_cancel(rr, pkt);
|
|
+}
|
|
+
|
|
+int
|
|
+main(int argc, const char **argv)
|
|
+{
|
|
+ krb5_context kctx = NULL;
|
|
+ krb5_data tmp;
|
|
+
|
|
+ if (!daemon_start(argc, argv)) {
|
|
+ fprintf(stderr, "Unable to start pyrad daemon, skipping test...\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Initialize. */
|
|
+ noerror(krb5_init_context(&kctx));
|
|
+ vctx = verto_new(NULL, VERTO_EV_TYPE_IO | VERTO_EV_TYPE_TIMEOUT);
|
|
+ insist(vctx != NULL);
|
|
+ remote_new(kctx, &rr);
|
|
+
|
|
+ /* Create attribute set. */
|
|
+ noerror(krad_attrset_new(kctx, &set));
|
|
+ tmp = string2data("testUser");
|
|
+ noerror(krad_attrset_add(set, krad_attr_name2num("User-Name"), &tmp));
|
|
+
|
|
+ /* Send accept packet. */
|
|
+ noerror(do_auth("accept", NULL));
|
|
+ verto_run(vctx);
|
|
+
|
|
+ /* Send reject packet. */
|
|
+ noerror(do_auth("reject", NULL));
|
|
+ verto_run(vctx);
|
|
+
|
|
+ /* Send canceled packet. */
|
|
+ insist(verto_add_timeout(vctx, VERTO_EV_FLAG_NONE, test_timeout, 0) !=
|
|
+ NULL);
|
|
+ verto_run(vctx);
|
|
+
|
|
+ /* Test timeout. */
|
|
+ daemon_stop();
|
|
+ noerror(do_auth("accept", NULL));
|
|
+ verto_run(vctx);
|
|
+
|
|
+ /* Test outstanding packet freeing. */
|
|
+ noerror(do_auth("accept", NULL));
|
|
+ kr_remote_free(rr);
|
|
+ krad_attrset_free(set);
|
|
+
|
|
+ /* Verify the results. */
|
|
+ insist(record.count == EVENT_COUNT);
|
|
+ insist(record.events[0].error == FALSE);
|
|
+ insist(record.events[0].result.code ==
|
|
+ krad_code_name2num("Access-Accept"));
|
|
+ insist(record.events[1].error == FALSE);
|
|
+ insist(record.events[1].result.code ==
|
|
+ krad_code_name2num("Access-Reject"));
|
|
+ insist(record.events[2].error == TRUE);
|
|
+ insist(record.events[2].result.retval == ECANCELED);
|
|
+ insist(record.events[3].error == TRUE);
|
|
+ insist(record.events[3].result.retval == ETIMEDOUT);
|
|
+ insist(record.events[4].error == TRUE);
|
|
+ insist(record.events[4].result.retval == ECANCELED);
|
|
+ insist(record.events[5].error == TRUE);
|
|
+ insist(record.events[5].result.retval == ECANCELED);
|
|
+
|
|
+ verto_free(vctx);
|
|
+ krb5_free_context(kctx);
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/lib/krad/t_test.c b/src/lib/krad/t_test.c
|
|
new file mode 100644
|
|
index 0000000..152bc77
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_test.c
|
|
@@ -0,0 +1,50 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_test.c - Utility functions for libkrad tests */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "t_test.h"
|
|
+
|
|
+void
|
|
+noerror_impl(const char *file, int line, const char *cmd, int retval)
|
|
+{
|
|
+ if (retval == 0)
|
|
+ return;
|
|
+
|
|
+ fprintf(stderr, "%s:%d: %s:\n\t%s\n", file, line, strerror(retval), cmd);
|
|
+ exit(1);
|
|
+}
|
|
+
|
|
+void
|
|
+insist_impl(const char *file, int line, const char *cmd, krb5_boolean result)
|
|
+{
|
|
+ if (result)
|
|
+ return;
|
|
+
|
|
+ fprintf(stderr, "%s:%d: insist failed:\n\t%s\n", file, line, cmd);
|
|
+ exit(1);
|
|
+}
|
|
diff --git a/src/lib/krad/t_test.h b/src/lib/krad/t_test.h
|
|
new file mode 100644
|
|
index 0000000..f44742f
|
|
--- /dev/null
|
|
+++ b/src/lib/krad/t_test.h
|
|
@@ -0,0 +1,60 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* lib/krad/t_test.h - Shared declarations for libkrad test programs */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef T_TEST_H_
|
|
+#define T_TEST_H_
|
|
+
|
|
+#include "internal.h"
|
|
+
|
|
+#include <sys/wait.h>
|
|
+#include <signal.h>
|
|
+#include <unistd.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+
|
|
+#define insist(x) insist_impl(__FILE__, __LINE__, #x, x)
|
|
+#define noerror(x) noerror_impl(__FILE__, __LINE__, #x, x)
|
|
+
|
|
+struct event {
|
|
+ krb5_boolean error;
|
|
+ union
|
|
+ {
|
|
+ krb5_error_code retval;
|
|
+ krad_code code;
|
|
+ } result;
|
|
+};
|
|
+
|
|
+void
|
|
+noerror_impl(const char *file, int line, const char *cmd, int retval);
|
|
+
|
|
+void
|
|
+insist_impl(const char *file, int line, const char *cmd, krb5_boolean result);
|
|
+
|
|
+#endif /* T_TEST_H_ */
|
|
--
|
|
1.8.2.1
|
|
|
|
|
|
From 01c7bae6c9a962ffd38f9d5f70fdde7b9e6eb929 Mon Sep 17 00:00:00 2001
|
|
From: Nathaniel McCallum <npmccallum@redhat.com>
|
|
Date: Tue, 9 Apr 2013 12:24:47 -0400
|
|
Subject: [PATCH 4/4] add otp plugin
|
|
|
|
---
|
|
src/Makefile.in | 1 +
|
|
src/configure.in | 1 +
|
|
src/kdc/kdc_preauth.c | 2 +
|
|
src/plugins/preauth/otp/Makefile.in | 39 +++
|
|
src/plugins/preauth/otp/deps | 26 ++
|
|
src/plugins/preauth/otp/main.c | 379 ++++++++++++++++++++++++
|
|
src/plugins/preauth/otp/otp.exports | 1 +
|
|
src/plugins/preauth/otp/otp_state.c | 560 ++++++++++++++++++++++++++++++++++++
|
|
src/plugins/preauth/otp/otp_state.h | 59 ++++
|
|
9 files changed, 1068 insertions(+)
|
|
create mode 100644 src/plugins/preauth/otp/Makefile.in
|
|
create mode 100644 src/plugins/preauth/otp/deps
|
|
create mode 100644 src/plugins/preauth/otp/main.c
|
|
create mode 100644 src/plugins/preauth/otp/otp.exports
|
|
create mode 100644 src/plugins/preauth/otp/otp_state.c
|
|
create mode 100644 src/plugins/preauth/otp/otp_state.h
|
|
|
|
diff --git a/src/Makefile.in b/src/Makefile.in
|
|
index 2c65831..0b9d355 100644
|
|
--- a/src/Makefile.in
|
|
+++ b/src/Makefile.in
|
|
@@ -12,6 +12,7 @@ SUBDIRS=util include lib \
|
|
plugins/kadm5_hook/test \
|
|
plugins/kdb/db2 \
|
|
@ldap_plugin_dir@ \
|
|
+ plugins/preauth/otp \
|
|
plugins/preauth/pkinit \
|
|
kdc kadmin slave clients appl tests \
|
|
config-files man doc @po@
|
|
diff --git a/src/configure.in b/src/configure.in
|
|
index d8676e5..520e0c8 100644
|
|
--- a/src/configure.in
|
|
+++ b/src/configure.in
|
|
@@ -1337,6 +1337,7 @@ dnl ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
|
|
plugins/kdb/db2/libdb2/test
|
|
plugins/kdb/hdb
|
|
plugins/preauth/cksum_body
|
|
+ plugins/preauth/otp
|
|
plugins/preauth/securid_sam2
|
|
plugins/preauth/wpse
|
|
plugins/authdata/greet
|
|
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
|
|
index 42a37a8..afbf1f6 100644
|
|
--- a/src/kdc/kdc_preauth.c
|
|
+++ b/src/kdc/kdc_preauth.c
|
|
@@ -238,6 +238,8 @@ get_plugin_vtables(krb5_context context,
|
|
/* Auto-register encrypted challenge and (if possible) pkinit. */
|
|
k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "pkinit",
|
|
"preauth");
|
|
+ k5_plugin_register_dyn(context, PLUGIN_INTERFACE_KDCPREAUTH, "otp",
|
|
+ "preauth");
|
|
k5_plugin_register(context, PLUGIN_INTERFACE_KDCPREAUTH,
|
|
"encrypted_challenge",
|
|
kdcpreauth_encrypted_challenge_initvt);
|
|
diff --git a/src/plugins/preauth/otp/Makefile.in b/src/plugins/preauth/otp/Makefile.in
|
|
new file mode 100644
|
|
index 0000000..43bd2ba
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/Makefile.in
|
|
@@ -0,0 +1,39 @@
|
|
+mydir=plugins$(S)preauth$(S)otp
|
|
+BUILDTOP=$(REL)..$(S)..$(S)..
|
|
+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(top_srcdir)/config-files/krb5.conf ; export KRB5_CONFIG ;
|
|
+PROG_LIBPATH=-L$(TOPLIBD)
|
|
+PROG_RPATH=$(KRB5_LIBDIR)
|
|
+MODULE_INSTALL_DIR = $(KRB5_PA_MODULE_DIR)
|
|
+DEFS=@DEFS@
|
|
+
|
|
+LIBBASE=otp
|
|
+LIBMAJOR=0
|
|
+LIBMINOR=0
|
|
+SO_EXT=.so
|
|
+RELDIR=../plugins/preauth/otp
|
|
+
|
|
+SHLIB_EXPDEPS = $(VERTO_DEPLIBS) $(KRB5_BASE_DEPLIBS) \
|
|
+ $(TOPLIBD)/libkrad$(SHLIBEXT)
|
|
+
|
|
+SHLIB_EXPLIBS= -lkrad $(VERTO_LIBS) $(KRB5_BASE_LIBS)
|
|
+
|
|
+SHLIB_DIRS=-L$(TOPLIBD)
|
|
+SHLIB_RDIRS=$(KRB5_LIBDIR)
|
|
+STOBJLISTS=OBJS.ST
|
|
+STLIBOBJS = \
|
|
+ otp_state.o \
|
|
+ main.o
|
|
+
|
|
+SRCS = \
|
|
+ $(srcdir)/otp_state.c \
|
|
+ $(srcdir)/main.c
|
|
+
|
|
+all-unix:: all-liblinks
|
|
+install-unix:: install-libs
|
|
+clean-unix:: clean-liblinks clean-libs clean-libobjs
|
|
+
|
|
+clean::
|
|
+ $(RM) lib$(LIBBASE)$(SO_EXT)
|
|
+
|
|
+@libnover_frag@
|
|
+@libobj_frag@
|
|
diff --git a/src/plugins/preauth/otp/deps b/src/plugins/preauth/otp/deps
|
|
new file mode 100644
|
|
index 0000000..3352126
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/deps
|
|
@@ -0,0 +1,26 @@
|
|
+#
|
|
+# Generated makefile dependencies follow.
|
|
+#
|
|
+otp_state.so otp_state.po $(OUTPRE)otp_state.$(OBJEXT): \
|
|
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
|
|
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
|
|
+ $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
|
|
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
|
|
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \
|
|
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
|
|
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
|
|
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
|
|
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/krb5/preauth_plugin.h \
|
|
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
|
|
+ otp_state.c otp_state.h
|
|
+main.so main.po $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
|
|
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
|
|
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
|
|
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
|
|
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
|
|
+ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
|
|
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
|
|
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
|
|
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
|
|
+ $(top_srcdir)/include/krb5/preauth_plugin.h $(top_srcdir)/include/port-sockets.h \
|
|
+ $(top_srcdir)/include/socket-utils.h main.c otp_state.h
|
|
diff --git a/src/plugins/preauth/otp/main.c b/src/plugins/preauth/otp/main.c
|
|
new file mode 100644
|
|
index 0000000..2f7470e
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/main.c
|
|
@@ -0,0 +1,379 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* plugins/preauth/otp/main.c - OTP kdcpreauth module definition */
|
|
+/*
|
|
+ * Copyright 2011 NORDUnet A/S. All rights reserved.
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "k5-int.h"
|
|
+#include "k5-json.h"
|
|
+#include <krb5/preauth_plugin.h>
|
|
+#include "otp_state.h"
|
|
+
|
|
+#include <errno.h>
|
|
+#include <ctype.h>
|
|
+
|
|
+static krb5_preauthtype otp_pa_type_list[] =
|
|
+ { KRB5_PADATA_OTP_REQUEST, 0 };
|
|
+
|
|
+struct request_state {
|
|
+ krb5_kdcpreauth_verify_respond_fn respond;
|
|
+ void *arg;
|
|
+};
|
|
+
|
|
+static krb5_error_code
|
|
+decrypt_encdata(krb5_context context, krb5_keyblock *armor_key,
|
|
+ krb5_pa_otp_req *req, krb5_data *out)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ krb5_data plaintext;
|
|
+
|
|
+ if (req == NULL)
|
|
+ return EINVAL;
|
|
+
|
|
+ retval = alloc_data(&plaintext, req->enc_data.ciphertext.length);
|
|
+ if (retval)
|
|
+ return retval;
|
|
+
|
|
+ retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST,
|
|
+ NULL, &req->enc_data, &plaintext);
|
|
+ if (retval != 0) {
|
|
+ com_err("otp", retval, "Unable to decrypt encData in PA-OTP-REQUEST");
|
|
+ free(plaintext.data);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ *out = plaintext;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+nonce_verify(krb5_context ctx, krb5_keyblock *armor_key,
|
|
+ const krb5_data *nonce)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ krb5_timestamp ts;
|
|
+ krb5_data *er = NULL;
|
|
+
|
|
+ if (armor_key == NULL || nonce->data == NULL) {
|
|
+ retval = EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Decode the PA-OTP-ENC-REQUEST structure. */
|
|
+ retval = decode_krb5_pa_otp_enc_req(nonce, &er);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+
|
|
+ /* Make sure the nonce is exactly the same size as the one generated. */
|
|
+ if (er->length != armor_key->length + sizeof(krb5_timestamp))
|
|
+ goto out;
|
|
+
|
|
+ /* Check to make sure the timestamp at the beginning is still valid. */
|
|
+ ts = load_32_be(er->data);
|
|
+ retval = krb5_check_clockskew(ctx, ts);
|
|
+
|
|
+out:
|
|
+ krb5_free_data(ctx, er);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+timestamp_verify(krb5_context ctx, const krb5_data *nonce)
|
|
+{
|
|
+ krb5_error_code retval = EINVAL;
|
|
+ krb5_pa_enc_ts *et = NULL;
|
|
+
|
|
+ if (nonce->data == NULL)
|
|
+ goto out;
|
|
+
|
|
+ /* Decode the PA-ENC-TS-ENC structure. */
|
|
+ retval = decode_krb5_pa_enc_ts(nonce, &et);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+
|
|
+ /* Check the clockskew. */
|
|
+ retval = krb5_check_clockskew(ctx, et->patimestamp);
|
|
+
|
|
+out:
|
|
+ krb5_free_pa_enc_ts(ctx, et);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+nonce_generate(krb5_context ctx, unsigned int length, krb5_data *nonce_out)
|
|
+{
|
|
+ krb5_data nonce;
|
|
+ krb5_error_code retval;
|
|
+ krb5_timestamp now;
|
|
+
|
|
+ retval = krb5_timeofday(ctx, &now);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ retval = alloc_data(&nonce, sizeof(now) + length);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ retval = krb5_c_random_make_octets(ctx, &nonce);
|
|
+ if (retval != 0) {
|
|
+ free(nonce.data);
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ store_32_be(now, nonce.data);
|
|
+ *nonce_out = nonce;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+on_response(void *data, krb5_error_code retval, otp_response response)
|
|
+{
|
|
+ struct request_state rs = *(struct request_state *)data;
|
|
+
|
|
+ free(data);
|
|
+
|
|
+ if (retval == 0 && response != otp_response_success)
|
|
+ retval = KRB5_PREAUTH_FAILED;
|
|
+
|
|
+ rs.respond(rs.arg, retval, NULL, NULL, NULL);
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+otp_init(krb5_context context, krb5_kdcpreauth_moddata *moddata_out,
|
|
+ const char **realmnames)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ otp_state *state;
|
|
+
|
|
+ retval = otp_state_new(context, &state);
|
|
+ if (retval)
|
|
+ return retval;
|
|
+ *moddata_out = (krb5_kdcpreauth_moddata)state;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+otp_fini(krb5_context context, krb5_kdcpreauth_moddata moddata)
|
|
+{
|
|
+ otp_state_free((otp_state *)moddata);
|
|
+}
|
|
+
|
|
+static int
|
|
+otp_flags(krb5_context context, krb5_preauthtype pa_type)
|
|
+{
|
|
+ return PA_REPLACES_KEY;
|
|
+}
|
|
+
|
|
+static void
|
|
+otp_edata(krb5_context context, krb5_kdc_req *request,
|
|
+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
|
|
+ krb5_kdcpreauth_moddata moddata, krb5_preauthtype pa_type,
|
|
+ krb5_kdcpreauth_edata_respond_fn respond, void *arg)
|
|
+{
|
|
+ krb5_otp_tokeninfo ti, *tis[2] = { &ti, NULL };
|
|
+ krb5_keyblock *armor_key = NULL;
|
|
+ krb5_pa_otp_challenge chl;
|
|
+ krb5_pa_data *pa = NULL;
|
|
+ krb5_error_code retval;
|
|
+ krb5_data *encoding;
|
|
+ char *config;
|
|
+
|
|
+ /* Determine if otp is enabled for the user. */
|
|
+ retval = cb->get_string(context, rock, "otp", &config);
|
|
+ if (retval != 0 || config == NULL)
|
|
+ goto out;
|
|
+ cb->free_string(context, rock, config);
|
|
+
|
|
+ /* Get the armor key. This indicates the length of random data to use in
|
|
+ * the nonce. */
|
|
+ armor_key = cb->fast_armor(context, rock);
|
|
+ if (armor_key == NULL) {
|
|
+ retval = ENOENT;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Build the (mostly empty) challenge. */
|
|
+ memset(&ti, 0, sizeof(ti));
|
|
+ memset(&chl, 0, sizeof(chl));
|
|
+ chl.tokeninfo = tis;
|
|
+ ti.format = -1;
|
|
+ ti.length = -1;
|
|
+ ti.iteration_count = -1;
|
|
+
|
|
+ /* Generate the nonce. */
|
|
+ retval = nonce_generate(context, armor_key->length, &chl.nonce);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+
|
|
+ /* Build the output pa-data. */
|
|
+ retval = encode_krb5_pa_otp_challenge(&chl, &encoding);
|
|
+ if (retval != 0)
|
|
+ goto out;
|
|
+ pa = k5alloc(sizeof(krb5_pa_data), &retval);
|
|
+ if (pa == NULL) {
|
|
+ krb5_free_data(context, encoding);
|
|
+ goto out;
|
|
+ }
|
|
+ pa->pa_type = KRB5_PADATA_OTP_CHALLENGE;
|
|
+ pa->contents = (krb5_octet *)encoding->data;
|
|
+ pa->length = encoding->length;
|
|
+ free(encoding);
|
|
+
|
|
+out:
|
|
+ (*respond)(arg, retval, pa);
|
|
+}
|
|
+
|
|
+static void
|
|
+otp_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
|
|
+ krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa,
|
|
+ krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
|
|
+ krb5_kdcpreauth_moddata moddata,
|
|
+ krb5_kdcpreauth_verify_respond_fn respond, void *arg)
|
|
+{
|
|
+ krb5_keyblock *armor_key = NULL;
|
|
+ krb5_pa_otp_req *req = NULL;
|
|
+ struct request_state *rs;
|
|
+ krb5_error_code retval;
|
|
+ krb5_data d, plaintext;
|
|
+ char *config;
|
|
+
|
|
+ enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
|
|
+
|
|
+ /* Get the FAST armor key. */
|
|
+ armor_key = cb->fast_armor(context, rock);
|
|
+ if (armor_key == NULL) {
|
|
+ retval = KRB5KDC_ERR_PREAUTH_FAILED;
|
|
+ com_err("otp", retval, "No armor key found when verifying padata");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Decode the request. */
|
|
+ d = make_data(pa->contents, pa->length);
|
|
+ retval = decode_krb5_pa_otp_req(&d, &req);
|
|
+ if (retval != 0) {
|
|
+ com_err("otp", retval, "Unable to decode OTP request");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Decrypt the nonce from the request. */
|
|
+ retval = decrypt_encdata(context, armor_key, req, &plaintext);
|
|
+ if (retval != 0) {
|
|
+ com_err("otp", retval, "Unable to decrypt nonce");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Verify the nonce or timestamp. */
|
|
+ retval = nonce_verify(context, armor_key, &plaintext);
|
|
+ if (retval != 0)
|
|
+ retval = timestamp_verify(context, &plaintext);
|
|
+ krb5_free_data_contents(context, &plaintext);
|
|
+ if (retval != 0) {
|
|
+ com_err("otp", retval, "Unable to verify nonce or timestamp");
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Create the request state. */
|
|
+ rs = k5alloc(sizeof(struct request_state), &retval);
|
|
+ if (rs == NULL)
|
|
+ goto error;
|
|
+ rs->arg = arg;
|
|
+ rs->respond = respond;
|
|
+
|
|
+ /* Get the principal's OTP configuration string. */
|
|
+ retval = cb->get_string(context, rock, "otp", &config);
|
|
+ if (config == NULL)
|
|
+ retval = KRB5_PREAUTH_FAILED;
|
|
+ if (retval != 0) {
|
|
+ free(rs);
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ /* Send the request. */
|
|
+ otp_state_verify((otp_state *)moddata, cb->event_context(context, rock),
|
|
+ request->client, config, req, on_response, rs);
|
|
+ cb->free_string(context, rock, config);
|
|
+
|
|
+ k5_free_pa_otp_req(context, req);
|
|
+ return;
|
|
+
|
|
+error:
|
|
+ k5_free_pa_otp_req(context, req);
|
|
+ (*respond)(arg, retval, NULL, NULL, NULL);
|
|
+}
|
|
+
|
|
+static krb5_error_code
|
|
+otp_return_padata(krb5_context context, krb5_pa_data *padata,
|
|
+ krb5_data *req_pkt, krb5_kdc_req *request,
|
|
+ krb5_kdc_rep *reply, krb5_keyblock *encrypting_key,
|
|
+ krb5_pa_data **send_pa_out, krb5_kdcpreauth_callbacks cb,
|
|
+ krb5_kdcpreauth_rock rock, krb5_kdcpreauth_moddata moddata,
|
|
+ krb5_kdcpreauth_modreq modreq)
|
|
+{
|
|
+ krb5_keyblock *armor_key = NULL;
|
|
+
|
|
+ if (padata->length == 0)
|
|
+ return 0;
|
|
+
|
|
+ /* Get the armor key. */
|
|
+ armor_key = cb->fast_armor(context, rock);
|
|
+ if (!armor_key) {
|
|
+ com_err("otp", ENOENT, "No armor key found when returning padata");
|
|
+ return ENOENT;
|
|
+ }
|
|
+
|
|
+ /* Replace the reply key with the FAST armor key. */
|
|
+ krb5_free_keyblock_contents(context, encrypting_key);
|
|
+ return krb5_copy_keyblock_contents(context, armor_key, encrypting_key);
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
|
|
+ krb5_plugin_vtable vtable);
|
|
+
|
|
+krb5_error_code
|
|
+kdcpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
|
|
+ krb5_plugin_vtable vtable)
|
|
+{
|
|
+ krb5_kdcpreauth_vtable vt;
|
|
+
|
|
+ if (maj_ver != 1)
|
|
+ return KRB5_PLUGIN_VER_NOTSUPP;
|
|
+
|
|
+ vt = (krb5_kdcpreauth_vtable)vtable;
|
|
+ vt->name = "otp";
|
|
+ vt->pa_type_list = otp_pa_type_list;
|
|
+ vt->init = otp_init;
|
|
+ vt->fini = otp_fini;
|
|
+ vt->flags = otp_flags;
|
|
+ vt->edata = otp_edata;
|
|
+ vt->verify = otp_verify;
|
|
+ vt->return_padata = otp_return_padata;
|
|
+
|
|
+ com_err("otp", 0, "Loaded");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/src/plugins/preauth/otp/otp.exports b/src/plugins/preauth/otp/otp.exports
|
|
new file mode 100644
|
|
index 0000000..26aa19d
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/otp.exports
|
|
@@ -0,0 +1 @@
|
|
+kdcpreauth_otp_initvt
|
|
diff --git a/src/plugins/preauth/otp/otp_state.c b/src/plugins/preauth/otp/otp_state.c
|
|
new file mode 100644
|
|
index 0000000..95f88e0
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/otp_state.c
|
|
@@ -0,0 +1,560 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* plugins/preauth/otp/otp_state.c - Verify OTP token values using RADIUS */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include "otp_state.h"
|
|
+
|
|
+#include <krad.h>
|
|
+#include <k5-json.h>
|
|
+
|
|
+#include <ctype.h>
|
|
+
|
|
+#ifndef HOST_NAME_MAX
|
|
+/* SUSv2 */
|
|
+#define HOST_NAME_MAX 255
|
|
+#endif
|
|
+
|
|
+#define DEFAULT_TYPE_NAME "DEFAULT"
|
|
+#define DEFAULT_SOCKET_FMT KDC_DIR "/%s.socket"
|
|
+#define DEFAULT_TIMEOUT 5
|
|
+#define DEFAULT_RETRIES 3
|
|
+
|
|
+typedef struct token_type_st {
|
|
+ char *name;
|
|
+ char *server;
|
|
+ char *secret;
|
|
+ int timeout;
|
|
+ size_t retries;
|
|
+ krb5_boolean strip_realm;
|
|
+} token_type;
|
|
+
|
|
+typedef struct token_st {
|
|
+ const token_type *type;
|
|
+ krb5_data username;
|
|
+} token;
|
|
+
|
|
+typedef struct request_st {
|
|
+ otp_state *state;
|
|
+ token *tokens;
|
|
+ ssize_t index;
|
|
+ otp_cb cb;
|
|
+ void *data;
|
|
+ krad_attrset *attrs;
|
|
+} request;
|
|
+
|
|
+struct otp_state_st {
|
|
+ krb5_context ctx;
|
|
+ token_type *types;
|
|
+ krad_client *radius;
|
|
+ krad_attrset *attrs;
|
|
+};
|
|
+
|
|
+static void request_send(request *req);
|
|
+
|
|
+/* Free the contents of a single token type. */
|
|
+static void
|
|
+token_type_free(token_type *type)
|
|
+{
|
|
+ if (type == NULL)
|
|
+ return;
|
|
+
|
|
+ free(type->name);
|
|
+ free(type->server);
|
|
+ free(type->secret);
|
|
+}
|
|
+
|
|
+/* Construct the internal default token type. */
|
|
+static krb5_error_code
|
|
+token_type_default(token_type *out)
|
|
+{
|
|
+ char *name = NULL, *server = NULL, *secret = NULL;
|
|
+
|
|
+ memset(out, 0, sizeof(*out));
|
|
+
|
|
+ name = strdup(DEFAULT_TYPE_NAME);
|
|
+ if (name == NULL)
|
|
+ goto oom;
|
|
+ if (asprintf(&server, DEFAULT_SOCKET_FMT, name) < 0)
|
|
+ goto oom;
|
|
+ secret = strdup("");
|
|
+ if (secret == NULL)
|
|
+ goto oom;
|
|
+
|
|
+ out->name = name;
|
|
+ out->server = server;
|
|
+ out->secret = secret;
|
|
+ out->timeout = DEFAULT_TIMEOUT * 1000;
|
|
+ out->retries = DEFAULT_RETRIES;
|
|
+ out->strip_realm = FALSE;
|
|
+ return 0;
|
|
+
|
|
+oom:
|
|
+ free(name);
|
|
+ free(server);
|
|
+ free(secret);
|
|
+ return ENOMEM;
|
|
+}
|
|
+
|
|
+/* Decode a single token type from the profile. */
|
|
+static krb5_error_code
|
|
+token_type_decode(profile_t profile, const char *name, token_type *out)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ char *server = NULL, *name_copy = NULL, *secret = NULL;
|
|
+ const char *default_secret;
|
|
+ int strip_realm, timeout, retries;
|
|
+
|
|
+ memset(out, 0, sizeof(*out));
|
|
+
|
|
+ /* Set the name. */
|
|
+ name_copy = strdup(name);
|
|
+ if (name_copy == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ /* Set strip_realm. */
|
|
+ retval = profile_get_boolean(profile, "otp", name, "strip_realm", TRUE,
|
|
+ &strip_realm);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ /* Set the server. */
|
|
+ retval = profile_get_string(profile, "otp", name, "server", NULL, &server);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+ if (server == NULL && asprintf(&server, DEFAULT_SOCKET_FMT, name) < 0) {
|
|
+ retval = ENOMEM;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Get the secret (optional for Unix-domain sockets). */
|
|
+ default_secret = (*server == '/') ? "" : NULL;
|
|
+ retval = profile_get_string(profile, "otp", name, "secret", default_secret,
|
|
+ &secret);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+ if (secret == NULL) {
|
|
+ com_err("otp", EINVAL, "Secret not specified in token type '%s'",
|
|
+ name);
|
|
+ retval = EINVAL;
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Get the timeout (profile value in seconds, result in milliseconds). */
|
|
+ retval = profile_get_integer(profile, "otp", name, "timeout",
|
|
+ DEFAULT_TIMEOUT, &timeout);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+ timeout *= 1000;
|
|
+
|
|
+ /* Get the number of retries. */
|
|
+ retval = profile_get_integer(profile, "otp", name, "retries",
|
|
+ DEFAULT_RETRIES, &retries);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+
|
|
+ out->name = name_copy;
|
|
+ out->server = server;
|
|
+ out->secret = secret;
|
|
+ out->timeout = timeout;
|
|
+ out->retries = retries;
|
|
+ out->strip_realm = strip_realm;
|
|
+ name_copy = server = secret = NULL;
|
|
+
|
|
+cleanup:
|
|
+ free(name_copy);
|
|
+ free(server);
|
|
+ free(secret);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/* Free an array of token types. */
|
|
+static void
|
|
+token_types_free(token_type *types)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (types == NULL)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; types[i].server != NULL; i++)
|
|
+ token_type_free(&types[i]);
|
|
+
|
|
+ free(types);
|
|
+}
|
|
+
|
|
+/* Decode an array of token types from the profile. */
|
|
+static krb5_error_code
|
|
+token_types_decode(profile_t profile, token_type **out)
|
|
+{
|
|
+ const char *hier[2] = { "otp", NULL };
|
|
+ token_type *types = NULL;
|
|
+ char **names = NULL;
|
|
+ krb5_error_code retval;
|
|
+ size_t i, pos;
|
|
+ krb5_boolean have_default = FALSE;
|
|
+
|
|
+ retval = profile_get_subsection_names(profile, hier, &names);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ /* Check if any of the profile subsections overrides the default. */
|
|
+ for (i = 0; names[i] != NULL; i++) {
|
|
+ if (strcmp(names[i], DEFAULT_TYPE_NAME) == 0)
|
|
+ have_default = TRUE;
|
|
+ }
|
|
+
|
|
+ /* Leave space for the default (possibly) and the terminator. */
|
|
+ types = k5alloc((i + 2) * sizeof(token_type), &retval);
|
|
+ if (types == NULL)
|
|
+ goto cleanup;
|
|
+
|
|
+ /* If no default has been specified, use our internal default. */
|
|
+ pos = 0;
|
|
+ if (!have_default) {
|
|
+ retval = token_type_default(&types[pos++]);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ /* Decode each profile section into a token type element. */
|
|
+ for (i = 0; names[i] != NULL; i++) {
|
|
+ retval = token_type_decode(profile, names[i], &types[pos++]);
|
|
+ if (retval != 0)
|
|
+ goto cleanup;
|
|
+ }
|
|
+
|
|
+ *out = types;
|
|
+ types = NULL;
|
|
+
|
|
+cleanup:
|
|
+ profile_free_list(names);
|
|
+ token_types_free(types);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+/* Free the contents of a single token. */
|
|
+static void
|
|
+token_free_contents(token *t)
|
|
+{
|
|
+ if (t != NULL)
|
|
+ free(t->username.data);
|
|
+}
|
|
+
|
|
+/* Decode a single token from a JSON token object. */
|
|
+static krb5_error_code
|
|
+token_decode(krb5_context ctx, krb5_const_principal princ,
|
|
+ const token_type *types, k5_json_object obj, token *out)
|
|
+{
|
|
+ const char *typename = DEFAULT_TYPE_NAME;
|
|
+ const token_type *type = NULL;
|
|
+ char *username = NULL;
|
|
+ krb5_error_code retval;
|
|
+ k5_json_value val;
|
|
+ size_t i;
|
|
+ int flags;
|
|
+
|
|
+ memset(out, 0, sizeof(*out));
|
|
+
|
|
+ /* Find the token type. */
|
|
+ val = k5_json_object_get(obj, "type");
|
|
+ if (val != NULL && k5_json_get_tid(val) == K5_JSON_TID_STRING)
|
|
+ typename = k5_json_string_utf8(val);
|
|
+ for (i = 0; types[i].server != NULL; i++) {
|
|
+ if (strcmp(typename, types[i].name) == 0)
|
|
+ type = &types[i];
|
|
+ }
|
|
+ if (type == NULL)
|
|
+ return EINVAL;
|
|
+
|
|
+ /* Get the username, either from obj or from unparsing the principal. */
|
|
+ val = k5_json_object_get(obj, "username");
|
|
+ if (val != NULL && k5_json_get_tid(val) == K5_JSON_TID_STRING) {
|
|
+ username = strdup(k5_json_string_utf8(val));
|
|
+ if (username == NULL)
|
|
+ return ENOMEM;
|
|
+ } else {
|
|
+ flags = type->strip_realm ? KRB5_PRINCIPAL_UNPARSE_NO_REALM : 0;
|
|
+ retval = krb5_unparse_name_flags(ctx, princ, flags, &username);
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ out->type = type;
|
|
+ out->username = string2data(username);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Free an array of tokens. */
|
|
+static void
|
|
+tokens_free(token *tokens)
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ if (tokens == NULL)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; tokens[i].type != NULL; i++)
|
|
+ token_free_contents(&tokens[i]);
|
|
+
|
|
+ free(tokens);
|
|
+}
|
|
+
|
|
+/* Decode an array of tokens from the configuration string. */
|
|
+static krb5_error_code
|
|
+tokens_decode(krb5_context ctx, krb5_const_principal princ,
|
|
+ const token_type *types, const char *config, token **out)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ k5_json_value arr, obj;
|
|
+ token *tokens;
|
|
+ ssize_t len, i, j;
|
|
+
|
|
+ if (config == NULL)
|
|
+ config = "[{}]";
|
|
+
|
|
+ arr = k5_json_decode(config);
|
|
+ if (arr == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY ||
|
|
+ (len = k5_json_array_length(arr)) == 0) {
|
|
+ k5_json_release(arr);
|
|
+
|
|
+ arr = k5_json_decode("[{}]");
|
|
+ if (arr == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ if (k5_json_get_tid(arr) != K5_JSON_TID_ARRAY) {
|
|
+ k5_json_release(arr);
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ len = k5_json_array_length(arr);
|
|
+ }
|
|
+
|
|
+ tokens = calloc(len + 1, sizeof(token));
|
|
+ if (tokens == NULL) {
|
|
+ k5_json_release(arr);
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ for (i = 0, j = 0; i < len; i++) {
|
|
+ obj = k5_json_array_get(arr, i);
|
|
+ if (k5_json_get_tid(obj) != K5_JSON_TID_OBJECT)
|
|
+ continue;
|
|
+
|
|
+ retval = token_decode(ctx, princ, types, obj, &tokens[j++]);
|
|
+ if (retval != 0) {
|
|
+ k5_json_release(arr);
|
|
+ while (--j > 0)
|
|
+ token_free_contents(&tokens[j]);
|
|
+ free(tokens);
|
|
+ return retval;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ k5_json_release(arr);
|
|
+ *out = tokens;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+request_free(request *req)
|
|
+{
|
|
+ if (req == NULL)
|
|
+ return;
|
|
+
|
|
+ krad_attrset_free(req->attrs);
|
|
+ tokens_free(req->tokens);
|
|
+ free(req);
|
|
+}
|
|
+
|
|
+krb5_error_code
|
|
+otp_state_new(krb5_context ctx, otp_state **out)
|
|
+{
|
|
+ char hostname[HOST_NAME_MAX + 1];
|
|
+ krb5_error_code retval;
|
|
+ profile_t profile;
|
|
+ krb5_data hndata;
|
|
+ otp_state *self;
|
|
+
|
|
+ retval = gethostname(hostname, sizeof(hostname));
|
|
+ if (retval != 0)
|
|
+ return retval;
|
|
+
|
|
+ self = calloc(1, sizeof(otp_state));
|
|
+ if (self == NULL)
|
|
+ return ENOMEM;
|
|
+
|
|
+ retval = krb5_get_profile(ctx, &profile);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = token_types_decode(profile, &self->types);
|
|
+ profile_abandon(profile);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = krad_attrset_new(ctx, &self->attrs);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ hndata = make_data(hostname, strlen(hostname));
|
|
+ retval = krad_attrset_add(self->attrs,
|
|
+ krad_attr_name2num("NAS-Identifier"), &hndata);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = krad_attrset_add_number(self->attrs,
|
|
+ krad_attr_name2num("Service-Type"),
|
|
+ KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ self->ctx = ctx;
|
|
+ *out = self;
|
|
+ return 0;
|
|
+
|
|
+error:
|
|
+ otp_state_free(self);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+void
|
|
+otp_state_free(otp_state *self)
|
|
+{
|
|
+ if (self == NULL)
|
|
+ return;
|
|
+
|
|
+ krad_attrset_free(self->attrs);
|
|
+ token_types_free(self->types);
|
|
+ free(self);
|
|
+}
|
|
+
|
|
+static void
|
|
+callback(krb5_error_code retval, const krad_packet *rqst,
|
|
+ const krad_packet *resp, void *data)
|
|
+{
|
|
+ request *req = data;
|
|
+
|
|
+ req->index++;
|
|
+
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ /* If we received an accept packet, success! */
|
|
+ if (krad_packet_get_code(resp) ==
|
|
+ krad_code_name2num("Access-Accept")) {
|
|
+ req->cb(req->data, retval, otp_response_success);
|
|
+ request_free(req);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* If we have no more tokens to try, failure! */
|
|
+ if (req->tokens[req->index].type == NULL)
|
|
+ goto error;
|
|
+
|
|
+ /* Try the next token. */
|
|
+ request_send(req);
|
|
+
|
|
+error:
|
|
+ req->cb(req->data, retval, otp_response_fail);
|
|
+ request_free(req);
|
|
+}
|
|
+
|
|
+static void
|
|
+request_send(request *req)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ token *tok = &req->tokens[req->index];
|
|
+ const token_type *t = tok->type;
|
|
+
|
|
+ retval = krad_attrset_add(req->attrs, krad_attr_name2num("User-Name"),
|
|
+ &tok->username);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = krad_client_send(req->state->radius,
|
|
+ krad_code_name2num("Access-Request"), req->attrs,
|
|
+ t->server, t->secret, t->timeout, t->retries,
|
|
+ callback, req);
|
|
+ krad_attrset_del(req->attrs, krad_attr_name2num("User-Name"), 0);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ return;
|
|
+
|
|
+error:
|
|
+ req->cb(req->data, retval, otp_response_fail);
|
|
+ request_free(req);
|
|
+}
|
|
+
|
|
+void
|
|
+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ,
|
|
+ const char *config, const krb5_pa_otp_req *req,
|
|
+ otp_cb cb, void *data)
|
|
+{
|
|
+ krb5_error_code retval;
|
|
+ request *rqst = NULL;
|
|
+
|
|
+ if (state->radius == NULL) {
|
|
+ retval = krad_client_new(state->ctx, ctx, &state->radius);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ rqst = calloc(1, sizeof(request));
|
|
+ if (rqst == NULL) {
|
|
+ (*cb)(data, ENOMEM, otp_response_fail);
|
|
+ return;
|
|
+ }
|
|
+ rqst->state = state;
|
|
+ rqst->data = data;
|
|
+ rqst->cb = cb;
|
|
+
|
|
+ retval = krad_attrset_copy(state->attrs, &rqst->attrs);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = krad_attrset_add(rqst->attrs, krad_attr_name2num("User-Password"),
|
|
+ &req->otp_value);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ retval = tokens_decode(state->ctx, princ, state->types, config,
|
|
+ &rqst->tokens);
|
|
+ if (retval != 0)
|
|
+ goto error;
|
|
+
|
|
+ request_send(rqst);
|
|
+ return;
|
|
+
|
|
+error:
|
|
+ (*cb)(data, retval, otp_response_fail);
|
|
+ request_free(rqst);
|
|
+}
|
|
diff --git a/src/plugins/preauth/otp/otp_state.h b/src/plugins/preauth/otp/otp_state.h
|
|
new file mode 100644
|
|
index 0000000..4247d0b
|
|
--- /dev/null
|
|
+++ b/src/plugins/preauth/otp/otp_state.h
|
|
@@ -0,0 +1,59 @@
|
|
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
+/* plugins/preauth/otp/otp_state.h - Internal declarations for OTP module */
|
|
+/*
|
|
+ * Copyright 2013 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:
|
|
+ *
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
+ *
|
|
+ * 2. 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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#ifndef OTP_H_
|
|
+#define OTP_H_
|
|
+
|
|
+#include <k5-int.h>
|
|
+#include <verto.h>
|
|
+
|
|
+#include <com_err.h>
|
|
+
|
|
+typedef enum otp_response {
|
|
+ otp_response_fail = 0,
|
|
+ otp_response_success
|
|
+ /* Other values reserved for responses like next token or new pin. */
|
|
+} otp_response;
|
|
+
|
|
+typedef struct otp_state_st otp_state;
|
|
+typedef void
|
|
+(*otp_cb)(void *data, krb5_error_code retval, otp_response response);
|
|
+
|
|
+krb5_error_code
|
|
+otp_state_new(krb5_context ctx, otp_state **self);
|
|
+
|
|
+void
|
|
+otp_state_free(otp_state *self);
|
|
+
|
|
+void
|
|
+otp_state_verify(otp_state *state, verto_ctx *ctx, krb5_const_principal princ,
|
|
+ const char *config, const krb5_pa_otp_req *request,
|
|
+ otp_cb cb, void *data);
|
|
+
|
|
+#endif /* OTP_H_ */
|
|
--
|
|
1.8.2.1
|
|
|