From dba026b5ef703d625abd7c5d2471bb474f6b8128 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Tue, 4 Feb 2014 23:02:01 -0500 Subject: [PATCH] Introduce and apply krb5_storage_from_socket On Windows a file descriptor is an int value allocated by the local module instance of the C Run Time Library. A socket handle is a SOCKET value allocated by a Winsock Provider for the requested family and protocol. These two values cannot be mixed and there is no mechanism for converting between the two. The _get_osfhandle() and _open_osfhandle() functions can work with a standard HANDLE (file, pipe, etc) but cannot be used for a SOCKET. The Heimdal krb5_storage_from_fd() routine counted on the osf conversion functions working on SOCKET values. Since they do not any attempt to call krb5_storage_from_fd() on a socket resulted in an assertion being thrown by the C RTL. Another problem is SOCKET value truncation when storing a 64-bit value into a 32-bit int. To address these problems a new krb5_storage_from_socket() routine is introduced. This routine setups a krb5_storage that stores a socket value as a rk_socket_t and provides a set of helper routines that always use network ready functions. The krb5_storage_from_fd() routines no longer use net_read() and net_write() but provide helpers that follow their logic so that pipes can be processed. All call sites that allocate a socket now store the socket as rk_socket_t and call krb5_storage_from_socket(). All locations that previously called the bare close() on a socket value now call rk_closesocket(). Change-Id: I045f775b2a5dbf5cf803751409490bc27fffe597 --- appl/gssmask/gssmaestro.c | 48 +++++----- appl/gssmask/gssmask.c | 25 +++--- kadmin/rpc.c | 2 +- lib/kadm5/private.h | 2 +- lib/kadm5/send_recv.c | 6 +- lib/krb5/Makefile.am | 1 + lib/krb5/NTMakefile | 2 + lib/krb5/libkrb5-exports.def.in | 3 +- lib/krb5/store_emem.c | 1 + lib/krb5/store_fd.c | 58 +++++++++--- lib/krb5/store_mem.c | 1 + lib/krb5/store_sock.c | 155 ++++++++++++++++++++++++++++++++ lib/krb5/version-script.map | 1 + 13 files changed, 252 insertions(+), 53 deletions(-) create mode 100644 lib/krb5/store_sock.c diff --git a/appl/gssmask/gssmaestro.c b/appl/gssmask/gssmaestro.c index 054e3a378..c3a850f1f 100644 --- a/appl/gssmask/gssmaestro.c +++ b/appl/gssmask/gssmaestro.c @@ -270,26 +270,27 @@ wait_log(struct client *c) int32_t port; struct sockaddr_storage sast; socklen_t salen = sizeof(sast); - int fd, fd2, ret; + krb5_socket_t sock, sock2; + int ret; memset(&sast, 0, sizeof(sast)); assert(sizeof(sast) >= c->salen); - fd = socket(c->sa->sa_family, SOCK_STREAM, 0); - if (fd < 0) + sock = socket(c->sa->sa_family, SOCK_STREAM, 0); + if (sock == rk_INVALID_SOCKET) err(1, "failed to build socket for %s's logging port", c->moniker); sast.ss_family = c->sa->sa_family; - ret = bind(fd, (struct sockaddr *)&sast, c->salen); + ret = bind(sock, (struct sockaddr *)&sast, c->salen); if (ret < 0) err(1, "failed to bind %s's logging port", c->moniker); - if (listen(fd, SOMAXCONN) < 0) + if (listen(sock, SOMAXCONN) < 0) err(1, "failed to listen %s's logging port", c->moniker); salen = sizeof(sast); - ret = getsockname(fd, (struct sockaddr *)&sast, &salen); + ret = getsockname(sock, (struct sockaddr *)&sast, &salen); if (ret < 0) err(1, "failed to get address of local socket for %s", c->moniker); @@ -299,12 +300,12 @@ wait_log(struct client *c) put32(c, ntohs(port)); salen = sizeof(sast); - fd2 = accept(fd, (struct sockaddr *)&sast, &salen); - if (fd2 < 0) + sock2 = accept(sock, (struct sockaddr *)&sast, &salen); + if (sock2 == rk_INVALID_SOCKET) err(1, "failed to accept local socket for %s", c->moniker); - close(fd); + rk_closesocket(sock); - return fd2; + return sock2; } @@ -610,7 +611,8 @@ connect_client(const char *slave) char *name, *port; struct client *c = ecalloc(1, sizeof(*c)); struct addrinfo hints, *res0, *res; - int ret, fd; + int ret; + krb5_socket_t sock; name = estrdup(slave); port = strchr(name, ':'); @@ -628,13 +630,13 @@ connect_client(const char *slave) if (ret) errx(1, "error resolving %s", name); - for (res = res0, fd = -1; res; res = res->ai_next) { - fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (fd < 0) + for (res = res0, sock = rk_INVALID_SOCKET; res; res = res->ai_next) { + sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sock == rk_INVALID_SOCKET) continue; - if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { - close(fd); - fd = -1; + if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { + rk_closesocket(sock); + sock = rk_INVALID_SOCKET; continue; } c->sa = ecalloc(1, res->ai_addrlen); @@ -642,12 +644,12 @@ connect_client(const char *slave) c->salen = res->ai_addrlen; break; /* okay we got one */ } - if (fd < 0) + if (sock == rk_INVALID_SOCKET) err(1, "connect to host: %s", name); freeaddrinfo(res); - c->sock = krb5_storage_from_fd(fd); - close(fd); + c->sock = krb5_storage_from_socket(sock); + rk_closesocket(sock); if (c->sock == NULL) errx(1, "krb5_storage_from_fd"); @@ -669,10 +671,10 @@ connect_client(const char *slave) if (logfile) { printf("starting log socket to client %s\n", c->moniker); - fd = wait_log(c); + sock = wait_log(c); - c->logsock = krb5_storage_from_fd(fd); - close(fd); + c->logsock = krb5_storage_from_socket(sock); + rk_closesocket(sock); if (c->logsock == NULL) errx(1, "failed to create log krb5_storage"); #ifdef ENABLE_PTHREAD_SUPPORT diff --git a/appl/gssmask/gssmask.c b/appl/gssmask/gssmask.c index 5a454fc81..e90c1cab5 100644 --- a/appl/gssmask/gssmask.c +++ b/appl/gssmask/gssmask.c @@ -687,7 +687,8 @@ static int HandleOP(SetLoggingSocket) { int32_t portnum; - int fd, ret; + krb5_socket_t sock; + int ret; ret32(c, portnum); @@ -696,22 +697,22 @@ HandleOP(SetLoggingSocket) socket_set_port((struct sockaddr *)(&c->sa), htons(portnum)); - fd = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0); - if (fd < 0) + sock = socket(((struct sockaddr *)&c->sa)->sa_family, SOCK_STREAM, 0); + if (sock == rk_INVALID_SOCKET) return 0; - ret = connect(fd, (struct sockaddr *)&c->sa, c->salen); + ret = connect(sock, (struct sockaddr *)&c->sa, c->salen); if (ret < 0) { logmessage(c, __FILE__, __LINE__, 0, "failed connect to log port: %s", strerror(errno)); - close(fd); + rk_closesocket(sock); return 0; } if (c->logging) krb5_storage_free(c->logging); - c->logging = krb5_storage_from_fd(fd); - close(fd); + c->logging = krb5_storage_from_socket(sock); + rk_closesocket(sock); krb5_store_int32(c->logging, eLogSetMoniker); store_string(c->logging, c->moniker); @@ -1087,7 +1088,7 @@ find_op(int32_t op) } static struct client * -create_client(int fd, int port, const char *moniker) +create_client(krb5_socket_t sock, int port, const char *moniker) { struct client *c; int ret; @@ -1109,18 +1110,18 @@ create_client(int fd, int port, const char *moniker) { c->salen = sizeof(c->sa); - getpeername(fd, (struct sockaddr *)&c->sa, &c->salen); + getpeername(sock, (struct sockaddr *)&c->sa, &c->salen); getnameinfo((struct sockaddr *)&c->sa, c->salen, c->servername, sizeof(c->servername), NULL, 0, NI_NUMERICHOST); } - c->sock = krb5_storage_from_fd(fd); + c->sock = krb5_storage_from_socket(sock); if (c->sock == NULL) - errx(1, "krb5_storage_from_fd"); + errx(1, "krb5_storage_from_socket"); - close(fd); + rk_closesocket(sock); return c; } diff --git a/kadmin/rpc.c b/kadmin/rpc.c index 445a96a54..631eb8426 100644 --- a/kadmin/rpc.c +++ b/kadmin/rpc.c @@ -1097,7 +1097,7 @@ handle_mit(krb5_context contextp, void *buf, size_t len, krb5_socket_t sock) dcontext = contextp; - sp = krb5_storage_from_fd(sock); + sp = krb5_storage_from_socket(sock); INSIST(sp != NULL); process_stream(contextp, buf, len, sp); diff --git a/lib/kadm5/private.h b/lib/kadm5/private.h index d93a5957f..5a085eb39 100644 --- a/lib/kadm5/private.h +++ b/lib/kadm5/private.h @@ -113,7 +113,7 @@ typedef struct kadm5_client_context { char *realm; char *admin_server; int kadmind_port; - int sock; + krb5_socket_t sock; char *client_name; char *service_name; krb5_prompter_fct prompter; diff --git a/lib/kadm5/send_recv.c b/lib/kadm5/send_recv.c index cd63293f9..3384ddeab 100644 --- a/lib/kadm5/send_recv.c +++ b/lib/kadm5/send_recv.c @@ -43,7 +43,7 @@ _kadm5_client_send(kadm5_client_context *context, krb5_storage *sp) size_t len; krb5_storage *sock; - assert(context->sock != -1); + assert(context->sock != rk_INVALID_SOCKET); len = krb5_storage_seek(sp, 0, SEEK_CUR); ret = krb5_data_alloc(&msg, len); @@ -59,7 +59,7 @@ _kadm5_client_send(kadm5_client_context *context, krb5_storage *sp) if(ret) return ret; - sock = krb5_storage_from_fd(context->sock); + sock = krb5_storage_from_socket(context->sock); if(sock == NULL) { krb5_clear_error_message(context->context); krb5_data_free(&out); @@ -81,7 +81,7 @@ _kadm5_client_recv(kadm5_client_context *context, krb5_data *reply) krb5_data data; krb5_storage *sock; - sock = krb5_storage_from_fd(context->sock); + sock = krb5_storage_from_socket(context->sock); if(sock == NULL) { krb5_clear_error_message(context->context); return ENOMEM; diff --git a/lib/krb5/Makefile.am b/lib/krb5/Makefile.am index 21f7fa57c..6095f516b 100644 --- a/lib/krb5/Makefile.am +++ b/lib/krb5/Makefile.am @@ -227,6 +227,7 @@ dist_libkrb5_la_SOURCES = \ store_emem.c \ store_fd.c \ store_mem.c \ + store_sock.c \ plugin.c \ ticket.c \ time.c \ diff --git a/lib/krb5/NTMakefile b/lib/krb5/NTMakefile index 9a3e20e87..4b7f7c4c4 100644 --- a/lib/krb5/NTMakefile +++ b/lib/krb5/NTMakefile @@ -147,6 +147,7 @@ libkrb5_OBJS = \ $(OBJ)\store_emem.obj \ $(OBJ)\store_fd.obj \ $(OBJ)\store_mem.obj \ + $(OBJ)\store_sock.obj \ $(OBJ)\ticket.obj \ $(OBJ)\time.obj \ $(OBJ)\transited.obj \ @@ -297,6 +298,7 @@ dist_libkrb5_la_SOURCES = \ store_emem.c \ store_fd.c \ store_mem.c \ + store_sock.c \ pcache.c \ plugin.c \ ticket.c \ diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in index 671966c4c..db5e66507 100644 --- a/lib/krb5/libkrb5-exports.def.in +++ b/lib/krb5/libkrb5-exports.def.in @@ -603,7 +603,8 @@ EXPORTS krb5_storage_from_fd krb5_storage_from_mem krb5_storage_from_readonly_mem - krb5_storage_fsync + krb5_storage_from_socket + krb5_storage_fsync krb5_storage_get_byteorder krb5_storage_get_eof_code krb5_storage_is_flags diff --git a/lib/krb5/store_emem.c b/lib/krb5/store_emem.c index a9b5aa6bc..6d95bcf52 100644 --- a/lib/krb5/store_emem.c +++ b/lib/krb5/store_emem.c @@ -156,6 +156,7 @@ emem_free(krb5_storage *sp) * @sa krb5_storage_from_readonly_mem() * @sa krb5_storage_from_fd() * @sa krb5_storage_from_data() + * @sa krb5_storage_from_socket() */ KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL diff --git a/lib/krb5/store_fd.c b/lib/krb5/store_fd.c index 165e56605..4f7466eeb 100644 --- a/lib/krb5/store_fd.c +++ b/lib/krb5/store_fd.c @@ -43,13 +43,47 @@ typedef struct fd_storage { static ssize_t fd_fetch(krb5_storage * sp, void *data, size_t size) { - return net_read(FD(sp), data, size); + char *cbuf = (char *)data; + ssize_t count; + size_t rem = size; + + /* similar pattern to net_read() to support pipes */ + while (rem > 0) { + count = read (FD(sp), cbuf, rem); + if (count < 0) { + if (errno == EINTR) + continue; + else + return count; + } else if (count == 0) { + return count; + } + cbuf += count; + rem -= count; + } + return size; } static ssize_t fd_store(krb5_storage * sp, const void *data, size_t size) { - return net_write(FD(sp), data, size); + const char *cbuf = (const char *)data; + ssize_t count; + size_t rem = size; + + /* similar pattern to net_write() to support pipes */ + while (rem > 0) { + count = write(FD(sp), cbuf, rem); + if (count < 0) { + if (errno == EINTR) + continue; + else + return count; + } + cbuf += count; + rem -= count; + } + return size; } static off_t @@ -91,26 +125,26 @@ fd_free(krb5_storage * sp) * @sa krb5_storage_from_mem() * @sa krb5_storage_from_readonly_mem() * @sa krb5_storage_from_data() + * @sa krb5_storage_from_socket() */ KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL -krb5_storage_from_fd(krb5_socket_t fd_in) +krb5_storage_from_fd(int fd_in) { krb5_storage *sp; int saved_errno; int fd; -#ifdef SOCKET_IS_NOT_AN_FD #ifdef _MSC_VER - if (_get_osfhandle(fd_in) != -1) { - fd = dup(fd_in); - } else { - fd = _open_osfhandle(fd_in, 0); - } + /* + * This function used to try to pass the input to + * _get_osfhandle() to test if the value is a HANDLE + * but this doesn't work because doing so throws an + * exception that will result in Watson being triggered + * to file a Windows Error Report. + */ + fd = _dup(fd_in); #else -#error Dont know how to deal with fd that may or may not be a socket. -#endif -#else /* SOCKET_IS_NOT_AN_FD */ fd = dup(fd_in); #endif diff --git a/lib/krb5/store_mem.c b/lib/krb5/store_mem.c index c68e9c37d..ff2a570ca 100644 --- a/lib/krb5/store_mem.c +++ b/lib/krb5/store_mem.c @@ -120,6 +120,7 @@ mem_no_trunc(krb5_storage *sp, off_t offset) * @sa krb5_storage_from_readonly_mem() * @sa krb5_storage_from_data() * @sa krb5_storage_from_fd() + * @sa krb5_storage_from_socket() */ KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL diff --git a/lib/krb5/store_sock.c b/lib/krb5/store_sock.c new file mode 100644 index 000000000..56bb57a77 --- /dev/null +++ b/lib/krb5/store_sock.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * 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. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE 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 INSTITUTE 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 "krb5_locl.h" +#include "store-int.h" + +#ifdef _WIN32 +#include +#endif + +typedef struct socket_storage { + krb5_socket_t sock; +} socket_storage; + +#define SOCK(S) (((socket_storage*)(S)->data)->sock) + +static ssize_t +socket_fetch(krb5_storage * sp, void *data, size_t size) +{ + return net_read(SOCK(sp), data, size); +} + +static ssize_t +socket_store(krb5_storage * sp, const void *data, size_t size) +{ + return net_write(SOCK(sp), data, size); +} + +static off_t +socket_seek(krb5_storage * sp, off_t offset, int whence) +{ + return lseek(SOCK(sp), offset, whence); +} + +static int +socket_trunc(krb5_storage * sp, off_t offset) +{ + if (ftruncate(SOCK(sp), offset) == -1) + return errno; + return 0; +} + +static int +socket_sync(krb5_storage * sp) +{ + if (fsync(SOCK(sp)) == -1) + return errno; + return 0; +} + +static void +socket_free(krb5_storage * sp) +{ + rk_closesocket(SOCK(sp)); +} + +/** + * + * + * @return A krb5_storage on success, or NULL on out of memory error. + * + * @ingroup krb5_storage + * + * @sa krb5_storage_emem() + * @sa krb5_storage_from_mem() + * @sa krb5_storage_from_readonly_mem() + * @sa krb5_storage_from_data() + * @sa krb5_storage_from_fd() + */ + +KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL +krb5_storage_from_socket(krb5_socket_t sock_in) +{ + krb5_storage *sp; + int saved_errno; + krb5_socket_t sock; + +#ifdef _WIN32 + WSAPROTOCOL_INFO info; + + if (WSADuplicateSocket(sock_in, GetCurrentProcessId(), &info) == 0) + { + + sock = WSASocket( FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &info, 0, 0); + } +#else + sock = dup(sock_in); +#endif + + if (sock == rk_INVALID_SOCKET) + return NULL; + + errno = ENOMEM; + sp = malloc(sizeof(krb5_storage)); + if (sp == NULL) { + saved_errno = errno; + rk_closesocket(sock); + errno = saved_errno; + return NULL; + } + + errno = ENOMEM; + sp->data = malloc(sizeof(socket_storage)); + if (sp->data == NULL) { + saved_errno = errno; + rk_closesocket(sock); + free(sp); + errno = saved_errno; + return NULL; + } + sp->flags = 0; + sp->eof_code = HEIM_ERR_EOF; + SOCK(sp) = sock; + sp->fetch = socket_fetch; + sp->store = socket_store; + sp->seek = socket_seek; + sp->trunc = socket_trunc; + sp->fsync = socket_sync; + sp->free = socket_free; + sp->max_alloc = UINT_MAX/8; + return sp; +} diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index 4987a5cca..d11de9a73 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -596,6 +596,7 @@ HEIMDAL_KRB5_2.0 { krb5_storage_from_fd; krb5_storage_from_mem; krb5_storage_from_readonly_mem; + krb5_storage_from_socket; krb5_storage_fsync; krb5_storage_get_byteorder; krb5_storage_get_eof_code;