summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2012-01-29 12:01:30 -0500
committerSimo Sorce <simo@redhat.com>2012-01-29 22:36:19 -0500
commit266b81ef65dc4d41a328341799b1493508ecb24b (patch)
tree1b6372493c2ab504834de062369660a279968f70
parent9014d7581a9e1dc431499b91286758de0dcf769b (diff)
downloadgss-proxy-266b81ef65dc4d41a328341799b1493508ecb24b.tar.gz
gss-proxy-266b81ef65dc4d41a328341799b1493508ecb24b.tar.xz
gss-proxy-266b81ef65dc4d41a328341799b1493508ecb24b.zip
Add infrastructure for client side mechglue library
-rw-r--r--proxy/Makefile.am2
-rw-r--r--proxy/src/mechglue/README7
-rw-r--r--proxy/src/mechglue/gpm_common.c527
-rw-r--r--proxy/src/mechglue/gssapi_gpm.h47
4 files changed, 583 insertions, 0 deletions
diff --git a/proxy/Makefile.am b/proxy/Makefile.am
index c76e9b5..eb77bd3 100644
--- a/proxy/Makefile.am
+++ b/proxy/Makefile.am
@@ -81,6 +81,7 @@ if BUILD_SELINUX
endif
GP_RPCGEN_OBJ = rpcgen/gp_rpc_xdr.c rpcgen/gss_proxy_xdr.c
+GP_MECHGLUE_OBJ = src/mechglue/gpm_common.c
dist_noinst_HEADERS =
@@ -105,6 +106,7 @@ accept_context_SOURCES = \
src/gp_config.c \
src/gp_conv.c \
$(GP_RPCGEN_OBJ) \
+ $(GP_MECHGLUE_OBJ) \
tests/accept_context.c
gssproxy_LDADD = \
diff --git a/proxy/src/mechglue/README b/proxy/src/mechglue/README
new file mode 100644
index 0000000..a97d511
--- /dev/null
+++ b/proxy/src/mechglue/README
@@ -0,0 +1,7 @@
+Eventually here we will have the mechglue to module to be loaded in various gssapi
+implementations.
+
+At the moment we just need a conformant client in order to test the server
+implementation. So will just implement a simple RFC2744 API. It will
+be easy to turn this API into the mechglue SPI later on.
+
diff --git a/proxy/src/mechglue/gpm_common.c b/proxy/src/mechglue/gpm_common.c
new file mode 100644
index 0000000..5c20778
--- /dev/null
+++ b/proxy/src/mechglue/gpm_common.c
@@ -0,0 +1,527 @@
+/*
+ GSS-PROXY
+
+ Copyright (C) 2011 Red Hat, Inc.
+ Copyright (C) 2011 Simo Sorce <simo.sorce@redhat.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+
+#include "gssapi_gpm.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <pthread.h>
+
+struct gpm_ctx {
+ pthread_mutex_t lock;
+ int fd;
+ int next_xid;
+};
+
+/* a single global struct is not particularly efficient,
+ * but will do for now */
+struct gpm_ctx gpm_global_ctx;
+
+pthread_once_t gpm_init_once_control = PTHREAD_ONCE_INIT;
+
+static void gpm_init_once(void)
+{
+ pthread_mutexattr_t attr;
+ unsigned int seedp;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+
+ pthread_mutex_init(&gpm_global_ctx.lock, &attr);
+
+ gpm_global_ctx.fd = -1;
+
+ seedp = time(NULL) + getpid() + pthread_self();
+ gpm_global_ctx.next_xid = rand_r(&seedp);
+
+ pthread_mutexattr_destroy(&attr);
+}
+
+#define GP_SOCKET_NAME "gssproxy.socket"
+
+static int get_pipe_name(struct gpm_ctx *gpmctx, char *name)
+{
+ int ret;
+
+ /* TODO: get socket name from config file */
+
+ ret = snprintf(name, PATH_MAX, "%s/%s", PIPE_PATH, GP_SOCKET_NAME);
+ if (ret < 0 || ret >= PATH_MAX) {
+ return ENAMETOOLONG;
+ }
+
+ return 0;
+}
+
+static int gpm_open_socket(struct gpm_ctx *gpmctx)
+{
+ struct sockaddr_un addr = {0};
+ char name[PATH_MAX];
+ int ret;
+ int fd = -1;
+
+ ret = get_pipe_name(gpmctx, name);
+ if (ret) {
+ return ret;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, name, sizeof(addr.sun_path)-1);
+ addr.sun_path[sizeof(addr.sun_path)-1] = '\0';
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ ret = errno;
+ goto done;
+ }
+
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+
+done:
+ if (ret) {
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+ }
+ gpmctx->fd = fd;
+ return ret;
+}
+
+static void gpm_close_socket(struct gpm_ctx *gpmctx)
+{
+ int ret;
+
+ do {
+ ret = close(gpmctx->fd);
+ /* in theory we should retry to close() on EINTR,
+ * but on same system the fd will be invalid after
+ * close() has been called, so closing again may
+ * cause a race with another thread that just happend
+ * to open an unrelated file descriptor.
+ * So until POSIX finally amends language around close()
+ * and at least the Linux kernel changes its behavior,
+ * it is better to risk a leak than closing an unrelated
+ * file descriptor */
+ ret = 0;
+ } while (ret == EINTR);
+
+ gpmctx->fd = -1;
+}
+
+static int gpm_grab_sock(struct gpm_ctx *gpmctx)
+{
+ int ret;
+
+ ret = pthread_mutex_lock(&gpmctx->lock);
+ if (ret) {
+ return ret;
+ }
+
+ if (gpmctx->fd == -1) {
+ ret = gpm_open_socket(gpmctx);
+ }
+
+ return ret;
+}
+
+static int gpm_release_sock(struct gpm_ctx *gpmctx)
+{
+ return pthread_mutex_unlock(&gpmctx->lock);
+}
+
+static int gpm_send_buffer(struct gpm_ctx *gpmctx,
+ char *buffer, uint32_t length)
+{
+ uint32_t size;
+ size_t wn;
+ size_t pos;
+ bool retry;
+ int ret;
+
+ if (length > MAX_RPC_SIZE) {
+ return EINVAL;
+ }
+
+ gpm_grab_sock(gpmctx);
+
+ size = htonl(length);
+
+ retry = false;
+ do {
+ ret = 0;
+ do {
+ wn = write(gpmctx->fd, &size, sizeof(uint32_t));
+ if (wn == -1) {
+ ret = errno;
+ }
+ } while (ret == EINTR);
+ if (wn != 4) {
+ /* reopen and retry once */
+ if (retry == false) {
+ gpm_close_socket(gpmctx);
+ ret = gpm_open_socket(gpmctx);
+ if (ret == 0) {
+ retry = true;
+ continue;
+ }
+ } else {
+ ret = EIO;
+ }
+ goto done;
+ }
+ retry = false;
+ } while (retry);
+
+ pos = 0;
+ while (length > pos) {
+ wn = write(gpmctx->fd, buffer + pos, length - pos);
+ if (wn == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ ret = errno;
+ goto done;
+ }
+ pos += wn;
+ }
+
+ ret = 0;
+
+done:
+ if (ret) {
+ /* on errors we can only close the fd and return */
+ gpm_close_socket(gpmctx);
+ }
+ gpm_release_sock(gpmctx);
+ return ret;
+}
+
+static int gpm_recv_buffer(struct gpm_ctx *gpmctx,
+ char *buffer, uint32_t *length)
+{
+ uint32_t size;
+ size_t rn;
+ size_t pos;
+ bool retry;
+ int ret;
+
+ gpm_grab_sock(gpmctx);
+
+ retry = false;
+ do {
+ ret = 0;
+ do {
+ rn = read(gpmctx->fd, &size, sizeof(uint32_t));
+ if (rn == -1) {
+ ret = errno;
+ }
+ } while (ret == EINTR);
+ if (rn != 4) {
+ /* reopen and retry once */
+ if (retry == false) {
+ gpm_close_socket(gpmctx);
+ ret = gpm_open_socket(gpmctx);
+ if (ret == 0) {
+ retry = true;
+ continue;
+ }
+ } else {
+ ret = EIO;
+ }
+ goto done;
+ }
+ retry = false;
+ } while (retry);
+
+ *length = ntohl(size);
+
+ if (*length > MAX_RPC_SIZE) {
+ ret = EMSGSIZE;
+ goto done;
+ }
+
+ pos = 0;
+ while (*length > pos) {
+ rn = read(gpmctx->fd, buffer + pos, *length - pos);
+ if (rn == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ ret = errno;
+ goto done;
+ }
+ if (rn == 0) {
+ ret = EIO;
+ goto done;
+ }
+ pos += rn;
+ }
+
+ ret = 0;
+
+done:
+ if (ret) {
+ /* on errors we can only close the fd and return */
+ gpm_close_socket(gpmctx);
+ }
+ gpm_release_sock(gpmctx);
+ return ret;
+}
+
+static int gpm_next_xid(struct gpm_ctx *gpmctx, uint32_t *xid)
+{
+ int ret;
+
+ ret = gpm_grab_sock(gpmctx);
+ if (ret) {
+ goto done;
+ }
+
+ if (gpmctx->next_xid < 0) {
+ *xid = 0;
+ gpmctx->next_xid = 1;
+ } else {
+ *xid = gpmctx->next_xid++;
+ }
+
+done:
+ gpm_release_sock(gpmctx);
+ return ret;
+}
+
+static struct gpm_ctx *gpm_get_ctx(void)
+{
+ int ret;
+
+ pthread_once(&gpm_init_once_control, gpm_init_once);
+
+ ret = gpm_grab_sock(&gpm_global_ctx);
+ if (ret) {
+ return NULL;
+ }
+
+ return &gpm_global_ctx;
+}
+
+static void gpm_release_ctx(struct gpm_ctx *gpmctx)
+{
+ gpm_release_sock(gpmctx);
+}
+
+OM_uint32 gpm_release_name(OM_uint32 *minor_status,
+ gss_name_t *input_name)
+{
+ if (*input_name != GSS_C_NO_NAME) {
+ xdr_free((xdrproc_t)xdr_gssx_name, (char *)(*input_name));
+ free(*input_name);
+ *input_name = GSS_C_NO_NAME;
+ }
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32 gpm_release_buffer(OM_uint32 *minor_status,
+ gss_buffer_t buffer)
+{
+ if (buffer != GSS_C_NO_BUFFER) {
+ if (buffer->value) {
+ free(buffer->value);
+ }
+ buffer->length = 0;
+ buffer->value = NULL;
+ }
+ return GSS_S_COMPLETE;
+}
+
+struct gpm_rpc_fn_set {
+ xdrproc_t arg_fn;
+ xdrproc_t res_fn;
+} gpm_xdr_set[] = {
+ { /* NULLPROC */
+ (xdrproc_t)xdr_void,
+ (xdrproc_t)xdr_void,
+ },
+ { /* GSSX_INDICATE_MECHS */
+ (xdrproc_t)xdr_gssx_arg_indicate_mechs,
+ (xdrproc_t)xdr_gssx_res_indicate_mechs,
+ },
+ { /* GSSX_GET_CALL_CONTEXT */
+ (xdrproc_t)xdr_gssx_arg_get_call_context,
+ (xdrproc_t)xdr_gssx_res_get_call_context,
+ },
+ { /* GSSX_IMPORT_AND_CANON_NAME */
+ (xdrproc_t)xdr_gssx_arg_import_and_canon_name,
+ (xdrproc_t)xdr_gssx_res_import_and_canon_name,
+ },
+ { /* GSSX_EXPORT_CRED */
+ (xdrproc_t)xdr_gssx_arg_export_cred,
+ (xdrproc_t)xdr_gssx_res_export_cred,
+ },
+ { /* GSSX_IMPORT_CRED */
+ (xdrproc_t)xdr_gssx_arg_import_cred,
+ (xdrproc_t)xdr_gssx_res_import_cred,
+ },
+ { /* GSSX_ACQUIRE_CRED */
+ (xdrproc_t)xdr_gssx_arg_acquire_cred,
+ (xdrproc_t)xdr_gssx_res_acquire_cred,
+ },
+ { /* GSSX_STORE_CRED */
+ (xdrproc_t)xdr_gssx_arg_store_cred,
+ (xdrproc_t)xdr_gssx_res_store_cred,
+ },
+ { /* GSSX_INIT_SEC_CONTEXT */
+ (xdrproc_t)xdr_gssx_arg_init_sec_context,
+ (xdrproc_t)xdr_gssx_res_init_sec_context,
+ },
+ { /* GSSX_ACCEPT_SEC_CONTEXT */
+ (xdrproc_t)xdr_gssx_arg_accept_sec_context,
+ (xdrproc_t)xdr_gssx_res_accept_sec_context,
+ },
+ { /* GSSX_RELEASE_HANDLE */
+ (xdrproc_t)xdr_gssx_arg_release_handle,
+ (xdrproc_t)xdr_gssx_res_release_handle,
+ },
+ { /* GSSX_GET_MIC */
+ (xdrproc_t)xdr_gssx_arg_get_mic,
+ (xdrproc_t)xdr_gssx_res_get_mic,
+ },
+ { /* GSSX_VERIFY */
+ (xdrproc_t)xdr_gssx_arg_verify_mic,
+ (xdrproc_t)xdr_gssx_res_verify_mic,
+ },
+ { /* GSSX_WRAP */
+ (xdrproc_t)xdr_gssx_arg_wrap,
+ (xdrproc_t)xdr_gssx_res_wrap,
+ },
+ { /* GSSX_UNWRAP */
+ (xdrproc_t)xdr_gssx_arg_unwrap,
+ (xdrproc_t)xdr_gssx_res_unwrap,
+ },
+ { /* GSSX_WRAP_SIZE_LIMIT */
+ (xdrproc_t)xdr_gssx_arg_wrap_size_limit,
+ (xdrproc_t)xdr_gssx_res_wrap_size_limit,
+ }
+};
+
+int gpm_make_call(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res)
+{
+ struct gpm_ctx *gpmctx;
+ gp_rpc_msg msg;
+ XDR xdr_call_ctx;
+ XDR xdr_reply_ctx;
+ char buffer[MAX_RPC_SIZE];
+ uint32_t length;
+ uint32_t xid;
+ bool xdrok;
+ int ret;
+
+ xdrmem_create(&xdr_call_ctx, buffer, MAX_RPC_SIZE, XDR_ENCODE);
+ xdrmem_create(&xdr_reply_ctx, buffer, MAX_RPC_SIZE, XDR_DECODE);
+
+ memset(&msg, 0, sizeof(gp_rpc_msg));
+ msg.header.type = GP_RPC_CALL;
+ msg.header.gp_rpc_msg_union_u.chdr.rpcvers = 2;
+ msg.header.gp_rpc_msg_union_u.chdr.prog = GSSPROXY;
+ msg.header.gp_rpc_msg_union_u.chdr.vers = GSSPROXYVERS;
+ msg.header.gp_rpc_msg_union_u.chdr.proc = proc;
+ msg.header.gp_rpc_msg_union_u.chdr.cred.flavor = GP_RPC_AUTH_NONE;
+ msg.header.gp_rpc_msg_union_u.chdr.cred.body.body_len = 0;
+ msg.header.gp_rpc_msg_union_u.chdr.cred.body.body_val = NULL;
+ msg.header.gp_rpc_msg_union_u.chdr.verf.flavor = GP_RPC_AUTH_NONE;
+ msg.header.gp_rpc_msg_union_u.chdr.verf.body.body_len = 0;
+ msg.header.gp_rpc_msg_union_u.chdr.verf.body.body_val = NULL;
+
+ gpmctx = gpm_get_ctx();
+ if (!gpmctx) {
+ return EINVAL;
+ }
+
+ ret = gpm_next_xid(gpmctx, &xid);
+ if (ret) {
+ goto done;
+ }
+ msg.xid = xid;
+
+ /* encode header */
+ xdrok = xdr_gp_rpc_msg(&xdr_call_ctx, &msg);
+ if (!xdrok) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* encode data */
+ xdrok = gpm_xdr_set[proc].arg_fn(&xdr_call_ctx, (char *)arg);
+ if (!xdrok) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* send to proxy */
+ ret = gpm_send_buffer(gpmctx, buffer, xdr_getpos(&xdr_call_ctx));
+ if (ret) {
+ goto done;
+ }
+
+ /* receive answer */
+ ret = gpm_recv_buffer(gpmctx, buffer, &length);
+ if (ret) {
+ goto done;
+ }
+
+ /* decode header */
+ xdrok = xdr_gp_rpc_msg(&xdr_reply_ctx, &msg);
+ if (!xdrok) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (msg.xid != xid ||
+ msg.header.type != GP_RPC_REPLY ||
+ msg.header.gp_rpc_msg_union_u.rhdr.status != GP_RPC_MSG_ACCEPTED ||
+ msg.header.gp_rpc_msg_union_u.rhdr.gp_rpc_reply_header_u.accepted.reply_data.status != GP_RPC_SUCCESS) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* decode answer */
+ xdrok = gpm_xdr_set[proc].res_fn(&xdr_reply_ctx, (char *)res);
+ if (!xdrok) {
+ ret = EINVAL;
+ }
+
+done:
+ xdr_free((xdrproc_t)xdr_gp_rpc_msg, (char *)&msg);
+ xdr_destroy(&xdr_call_ctx);
+ xdr_destroy(&xdr_reply_ctx);
+ gpm_release_ctx(gpmctx);
+ return ret;
+}
+
+void gpm_free_xdrs(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res)
+{
+ xdr_free(gpm_xdr_set[proc].arg_fn, (char *)arg);
+ xdr_free(gpm_xdr_set[proc].res_fn, (char *)res);
+}
diff --git a/proxy/src/mechglue/gssapi_gpm.h b/proxy/src/mechglue/gssapi_gpm.h
new file mode 100644
index 0000000..388ff33
--- /dev/null
+++ b/proxy/src/mechglue/gssapi_gpm.h
@@ -0,0 +1,47 @@
+/*
+ GSS-PROXY
+
+ Copyright (C) 2011 Red Hat, Inc.
+ Copyright (C) 2011 Simo Sorce <simo.sorce@redhat.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef _GSSAPI_GPM_H_
+#define _GSSAPI_GPM_H_
+
+#include "config.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <gssapi/gssapi.h>
+#include "rpcgen/gp_rpc.h"
+#include "rpcgen/gss_proxy.h"
+#include "src/gp_common.h"
+#include "src/gp_conv.h"
+
+int gpm_make_call(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res);
+void gpm_free_xdrs(int proc, union gp_rpc_arg *arg, union gp_rpc_res *res);
+
+OM_uint32 gpm_release_name(OM_uint32 *minor_status,
+ gss_name_t *input_name);
+OM_uint32 gpm_release_buffer(OM_uint32 *minor_status,
+ gss_buffer_t buffer);
+
+#endif /* _GSSAPI_GPM_H_ */