From 266b81ef65dc4d41a328341799b1493508ecb24b Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Sun, 29 Jan 2012 12:01:30 -0500 Subject: Add infrastructure for client side mechglue library --- proxy/Makefile.am | 2 + proxy/src/mechglue/README | 7 + proxy/src/mechglue/gpm_common.c | 527 ++++++++++++++++++++++++++++++++++++++++ proxy/src/mechglue/gssapi_gpm.h | 47 ++++ 4 files changed, 583 insertions(+) create mode 100644 proxy/src/mechglue/README create mode 100644 proxy/src/mechglue/gpm_common.c create mode 100644 proxy/src/mechglue/gssapi_gpm.h 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 + + 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 +#include +#include +#include +#include + +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 + + 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 +#include +#include +#include +#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_ */ -- cgit