From 3b0d00c743c1fd068cd7e65a0c773c0597feabc0 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Tue, 2 Jan 2024 02:47:29 +0000 Subject: [PATCH] New option [libdefaults] socks4a_proxy. All network traffic to KDC goes through the SOCKS4a proxy if it is configured. This is deliberately kept simple -- and is not generalized to SOCKS4 or SOCKS5 or other types of proxies -- so it is easy to audit for network and DNS leaks. (SOCKS4 works in IP addresses, and so invites DNS leaks. SOCKS5 can be OK, if used judiciously, but takes more work to implement.) This only affects krb5_sendto -- the other initiator of network traffic in libkrb5, krb5_change_password, will be fixed to respect socks4a_proxy in a subsequent commit. XXX Need to figure out where the socks4a.c code should go. fix https://github.com/heimdal/heimdal/issues/1151 --- lib/krb5/Makefile.am | 2 + lib/krb5/NTMakefile | 2 + lib/krb5/context.c | 1 + lib/krb5/krb5.conf.5 | 10 + lib/krb5/krb5_locl.h | 4 + lib/krb5/send_to_kdc.c | 172 +++++++++++++++- lib/krb5/socks4a.c | 384 ++++++++++++++++++++++++++++++++++++ lib/krb5/socks4a.h | 76 +++++++ lib/krb5/verify_krb5_conf.c | 1 + 9 files changed, 650 insertions(+), 2 deletions(-) create mode 100644 lib/krb5/socks4a.c create mode 100644 lib/krb5/socks4a.h diff --git a/lib/krb5/Makefile.am b/lib/krb5/Makefile.am index 17a911847..b390517c3 100644 --- a/lib/krb5/Makefile.am +++ b/lib/krb5/Makefile.am @@ -239,6 +239,8 @@ dist_libkrb5_la_SOURCES = \ sendauth.c \ set_default_realm.c \ sock_principal.c \ + socks4a.c \ + socks4a.h \ store.c \ store-int.c \ store-int.h \ diff --git a/lib/krb5/NTMakefile b/lib/krb5/NTMakefile index 866774b9c..a353f89d1 100644 --- a/lib/krb5/NTMakefile +++ b/lib/krb5/NTMakefile @@ -148,6 +148,7 @@ libkrb5_OBJS = \ $(OBJ)\sendauth.obj \ $(OBJ)\set_default_realm.obj \ $(OBJ)\sock_principal.obj \ + $(OBJ)\socks4a.obj \ $(OBJ)\sp800-108-kdf.obj \ $(OBJ)\store.obj \ $(OBJ)\store-int.obj \ @@ -309,6 +310,7 @@ dist_libkrb5_la_SOURCES = \ sendauth.c \ set_default_realm.c \ sock_principal.c \ + socks4a.c \ sp800-108-kdf.c \ store.c \ store-int.c \ diff --git a/lib/krb5/context.c b/lib/krb5/context.c index 800258613..dc03283b4 100644 --- a/lib/krb5/context.c +++ b/lib/krb5/context.c @@ -349,6 +349,7 @@ init_context_from_config_file(krb5_context context) INIT_FIELD(context, int, max_retries, 3, "max_retries"); INIT_FIELD(context, string, http_proxy, NULL, "http_proxy"); + INIT_FIELD(context, string, socks4a_proxy, NULL, "socks4a_proxy"); ret = init_openssl(context, &context->ossl); if (ret) diff --git a/lib/krb5/krb5.conf.5 b/lib/krb5/krb5.conf.5 index 3c0ace1a6..d58cdeb8b 100644 --- a/lib/krb5/krb5.conf.5 +++ b/lib/krb5/krb5.conf.5 @@ -316,6 +316,16 @@ enable this option unconditionally. .It Li warn_pwexpire = Va time How soon to warn for expiring password. Default is seven days. +.It Li socks4a_proxy = Va host Ns Oo Li : Ns Va port Oc +SOCKS4a proxy to use when talking to the KDC. +Default port is 1080. +.Pp +Only TCP service to KDC is allowed in this case, not UDP or HTTP. +.Pp +The KDC hostname is passed to the SOCKS4a proxy verbatim without any +DNS resolution first. +Other DNS resolution, of the proxy address and for any realm mapping or +KDC discovery, may still be done outside the SOCKS4a proxy. .It Li http_proxy = Va proxy-spec A HTTP-proxy to use when talking to the KDC via HTTP. .It Li dns_proxy = Va proxy-spec diff --git a/lib/krb5/krb5_locl.h b/lib/krb5/krb5_locl.h index c6995ea15..16fb63a40 100644 --- a/lib/krb5/krb5_locl.h +++ b/lib/krb5/krb5_locl.h @@ -191,6 +191,9 @@ typedef void (KRB5_LIB_CALL *krb5_gssic_delete_sec_context)( #define KRB5_GSS_IC_FLAG_RELEASE_CRED 1 +struct socks4a; /* XXX */ +struct socks4a_io; /* XXX */ + #include #include "heim_threads.h" @@ -338,6 +341,7 @@ typedef struct krb5_context_data { const krb5_cc_ops **cc_ops; int num_cc_ops; const char *http_proxy; + const char *socks4a_proxy; const char *time_fmt; krb5_boolean log_utc; const char *default_keytab; diff --git a/lib/krb5/send_to_kdc.c b/lib/krb5/send_to_kdc.c index fdf216cae..0705b92b2 100644 --- a/lib/krb5/send_to_kdc.c +++ b/lib/krb5/send_to_kdc.c @@ -35,6 +35,7 @@ #include "krb5_locl.h" #include "send_to_kdc_plugin.h" +#include "socks4a.h" /** * @section send_to_kdc Locating and sending packets to the KDC @@ -326,11 +327,12 @@ struct host_fun { }; struct host { - enum host_state { CONNECT, CONNECTING, CONNECTED, WAITING_REPLY, DEAD } state; + enum host_state { CONNECT, CONNECTING, PROXYING, CONNECTED, WAITING_REPLY, DEAD } state; krb5_krbhst_info *hi; struct addrinfo *freeai; struct addrinfo *ai; rk_socket_t fd; + struct socks4a *socks4a; const struct host_fun *fun; unsigned int tries; time_t timeout; @@ -394,6 +396,10 @@ deallocate_host(void *ptr) struct host *host = ptr; if (!rk_IS_BAD_SOCKET(host->fd)) rk_closesocket(host->fd); + heim_assert((host->state == PROXYING) == (host->socks4a != NULL), + "inconsistent socks4a proxy state"); + _krb5_socks4a_free(host->socks4a); + host->socks4a = NULL; /* paranoia */ krb5_data_free(&host->data); if (host->freeai) freeaddrinfo(host->freeai); @@ -479,6 +485,40 @@ host_next_timeout(krb5_context context, struct host *host) host->timeout += time(NULL); } +/* + * Proxy I/O + */ + +static int +host_socks4a_read(void *vcontext, void *vhost, void *buf, unsigned len) +{ + krb5_context context = vcontext; + struct host *host = vhost; + + return krb5_net_read(context, &host->fd, buf, len); +} + +static int +host_socks4a_write(void *vcontext, void *vhost, const void *buf, unsigned len) +{ + krb5_context context = vcontext; + struct host *host = vhost; + + return krb5_net_write(context, &host->fd, buf, len); +} + +static struct socks4a_io +host_socks4a_io(krb5_context context, struct host *host) +{ + + return (struct socks4a_io) { + .sio_context = context, + .sio_cookie = host, + .sio_read = &host_socks4a_read, + .sio_write = &host_socks4a_write, + }; +} + /* * connected host */ @@ -488,6 +528,43 @@ host_connected(krb5_context context, krb5_sendto_ctx ctx, struct host *host) { krb5_error_code ret; + /* + * If we have a SOCKS4a proxy configured, we need to request + * proxying between when the underlying socket connection succeeds + * and when we enter the CONNECTED state meaning we're ready to + * send and receive application data. + */ + if (context->socks4a_proxy) { + if (host->state == CONNECTING) { + /* + * The underlying socket connection has just succeeded. + * Attempt to request proxying and enter the intermediate + * PROXYING state. + */ + debug_host(context, 5, host, "socks4a proxying"); + host->socks4a = NULL; + ret = _krb5_socks4a_connect(host_socks4a_io(context, host), + host->hi->hostname, host->hi->port, /*userid*/NULL, + &host->socks4a); + if (ret) { + host_dead(context, host, "socks4a proxy failed"); + return; + } + host->state = PROXYING; + return; + } else { + debug_host(context, 5, host, "socks4a proxied"); + heim_assert(host->state == PROXYING, "bad host_connected state"); + /* + * The proxy has accepted our proxying request. We are now + * ready to enter the CONNECTED state as if we had no proxy + * in the way. + */ + _krb5_socks4a_free(host->socks4a); + host->socks4a = NULL; + } + } + host->state = CONNECTED; /* * Now prepare data to send to host @@ -762,6 +839,43 @@ eval_host_state(krb5_context context, if (host->state == CONNECTING && writeable) host_connected(context, ctx, host); + if (host->state == PROXYING) { + /* + * Proxy is still connecting. Do an I/O step to see if we + * can make progress. + */ + debug_host(context, 10, host, "socks4a i/o (reading=%d writing=%d)", + _krb5_socks4a_reading(host->socks4a), + _krb5_socks4a_writing(host->socks4a)); + heim_assert(context->socks4a_proxy, "proxying without proxy"); + heim_assert(_krb5_socks4a_reading(host->socks4a) ? readable : 1, + "woken for read when not readable"); + heim_assert(_krb5_socks4a_writing(host->socks4a) ? writeable : 1, + "woken for read when not readable"); + ret = _krb5_socks4a_io(host->socks4a); + if (ret) { + _krb5_socks4a_free(host->socks4a); + host->socks4a = NULL; + host_dead(context, host, "socks4a proxy failed"); + return 0; /* no reply yet */ + } + if (!_krb5_socks4a_connected(host->socks4a)) { + /* + * Proxy is still connecting. Wait until we can do another + * I/O step. + */ + debug_host(context, 10, host, "socks4a still connecting"); + return 0; + } + /* + * Proxy has connected on our behalf, transition to CONNECTED + * and start over since _krb5_socks4a_io has consumed the I/O + * state. + */ + host_connected(context, ctx, host); + return 0; + } + if (readable) { debug_host(context, 5, host, "reading packet"); @@ -828,7 +942,55 @@ submit_request(krb5_context context, krb5_sendto_ctx ctx, krb5_krbhst_info *hi) gettimeofday(&nrstart, NULL); - if (hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) { + if (context->socks4a_proxy) { + char *proxy, *proxyhost, *proxyport; + struct addrinfo hints; + + /* + * Refuse anything but TCP connections when we have a SOCKS4a + * proxy configured. + */ + if (hi->proto != KRB5_KRBHST_TCP) + return KRB5_KDC_UNREACH; + + /* + * Parse `[:]' into parts. + */ + if ((proxy = strdup(context->socks4a_proxy)) == NULL) + return ENOMEM; + proxyhost = proxy; + if ((proxyport = strchr(proxy, ':')) != NULL) + *proxyport++ = '\0'; + + /* + * Set up getaddrinfo hints for stream connection. + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + /* + * If block_dns is enabled, make sure AI_CANONNAME is clear and + * AI_NUMERICHOST|AI_NUMERICSERV are both set to avoid the + * potential for DNS leaks. + */ + if (krb5_config_get_bool(context, NULL, "libdefaults", "block_dns", + NULL)) { + hints.ai_flags &= ~AI_CANONNAME; + hints.ai_flags |= AI_NUMERICHOST|AI_NUMERICSERV; + } + + /* + * Resolve the proxy's address. + */ + ret = getaddrinfo(proxyhost, proxyport ? proxyport : "1080", &hints, + &ai); + free(proxy); + if (ret) + return krb5_eai_to_heim_errno(ret, errno); + freeai = ai; + + } else if (hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) { char *proxy2 = strdup(context->http_proxy); char *el, *proxy = proxy2; struct addrinfo hints; @@ -1018,6 +1180,12 @@ wait_setup(heim_object_t obj, void *iter_ctx, int *stop) FD_SET(h->fd, &wait_ctx->rfds); FD_SET(h->fd, &wait_ctx->wfds); break; + case PROXYING: + if (_krb5_socks4a_reading(h->socks4a)) + FD_SET(h->fd, &wait_ctx->rfds); + if (_krb5_socks4a_writing(h->socks4a)) + FD_SET(h->fd, &wait_ctx->wfds); + break; default: debug_host(wait_ctx->context, 5, h, "invalid sendto host state"); heim_abort("invalid sendto host state"); diff --git a/lib/krb5/socks4a.c b/lib/krb5/socks4a.c new file mode 100644 index 000000000..222a0c1b2 --- /dev/null +++ b/lib/krb5/socks4a.c @@ -0,0 +1,384 @@ +/*- + * Copyright (c) 2024 Taylor R. Campbell + * 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 AUTHOR 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 AUTHOR 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. + */ + +/* https://ftp.icm.edu.pl/packages/socks/socks4/SOCKS4.protocol */ + +#define _POSIX_C_SOURCE 200809L + +#include "socks4a.h" + +#include +#include +#include +#include + +/* + * enc16be(buf, x) + * + * Encode the 16-bit integer x in big-endian at buf. + */ +static void +enc16be(void *buf, uint16_t x) +{ + uint8_t *p = buf; + + p[0] = x >> 8; + p[1] = x; +} + +/* + * enc32be(buf, x) + * + * Encode the 32-bit integer x in big-endian at buf. + */ +static void +enc32be(void *buf, uint32_t x) +{ + uint8_t *p = buf; + + p[0] = x >> 24; + p[1] = x >> 16; + p[2] = x >> 8; + p[3] = x; +} + +#define SOCKS4A_MAXUSERHOST0 \ + (SOCKS4A_MAXUSERID + 1 + SOCKS4A_MAXHOSTNAME + 1) + +struct socks4a_request { + uint8_t vn; + uint8_t cd; + uint8_t dstport[2]; + uint8_t dstip[4]; + char userhost[SOCKS4A_MAXUSERHOST0]; +}; + +struct socks4a_reply { + uint8_t vn; + uint8_t cd; + uint8_t dstport[2]; + uint8_t dstip[4]; +}; + +struct socks4a { + struct socks4a_io io; + char *p; + unsigned n; + enum { NO, RD, WR } dir; + enum socks4a_state { + CONNECTING_REQ, + CONNECTING_REPLY, + CONNECTED, + } state; + union { + struct socks4a_request request; + struct socks4a_reply reply; + } u; +}; + +/* + * _krb5_socks4a_free(S) + * + * Free a SOCKS4a connection state yielded by + * _krb5_socks4a_connect. Safe when S is null. + */ +void +_krb5_socks4a_free(struct socks4a *S) +{ + + free(S); +} + +/* + * strmove0(&p, &n, s) + * + * If the NUL-terminated string s has at most n bytes, including + * NUL terminator, then: + * 1. copy it (including the NUL terminator) to p, + * 2. advance p by the number of bytes copied (including NUL + * terminator), + * 3. reduce n by the number of bytes copied (including NUL + * terminator), and + * 4. return 0. + * + * Otherwise, return E2BIG with no side effects. + */ +static int +strmove0(char **pp, size_t *np, const char *s) +{ + size_t k = strlen(s) + 1; /* count NUL terminator */ + + if (k > *np) + return E2BIG; + memcpy(*pp, s, k); + *pp += k; + *np -= k; + return 0; +} + +/* + * _krb5_socks4a_connect(io, hostname, port, userid, &S) + * + * Allocate and initialize state to request a SOCKS4a proxy + * connection, with the given I/O medium and proxy request. + * + * On success, store a struct socks4a pointer at S and return 0. + * Caller must free S with _krb5_socks4a_free(S) when done. + * + * On failure, return an errno error code. + * + * Only allocates and initializes memory; does not perform I/O. + */ +int +_krb5_socks4a_connect(struct socks4a_io io, + const char *hostname, uint16_t port, const char *userid, + struct socks4a **socks4a_ret) +{ + struct socks4a *S = NULL; + char *p; + size_t n; + int error; + + /* + * Validate the userid and hostname input lengths. + */ + if (userid && strlen(userid) > SOCKS4A_MAXUSERID) { + error = EINVAL; + goto out; + } + if (strlen(hostname) > SOCKS4A_MAXHOSTNAME) { + error = EINVAL; + goto out; + } + + /* + * Allocate state for the SOCKS connection. + */ + S = calloc(1, sizeof(*S)); + if (S == NULL) { + error = errno; + goto out; + } + S->io = io; + + /* + * Format the CONNECT request. + */ + memset(&S->u.request, 0, sizeof S->u.request); /* paranoia */ + S->u.request.vn = 4; /* version -- SOCKS4 */ + S->u.request.cd = 1; /* command -- CONNECT */ + enc16be(S->u.request.dstport, port); + enc32be(S->u.request.dstip, 0x00000001); /* 0.0.0.1 -- SOCKS4a */ + p = S->u.request.userhost; + n = sizeof S->u.request.userhost; + error = strmove0(&p, &n, userid ? userid : ""); + if (error) + goto out; + error = strmove0(&p, &n, hostname); + if (error) + goto out; + + /* + * Prepare I/O to send the CONNECT request. + */ + S->state = CONNECTING_REQ; + S->p = (void *)&S->u.request; + S->n = p - (char *)S->p; + S->dir = WR; + error = 0; + +out: if (error) { + free(S); + S = NULL; + } + *socks4a_ret = S; + return error; +} + +/* + * _krb5_socks4a_connected(S) + * + * True if and only if the SOCKS4a proxy connection has been + * established. + * + * If this returns false, the caller should wait with select/poll + * or equivalent until it can read or write data, according to + * _krb5_socks4a_reading(S) or _krb5_socks4a_writing(S), and then + * call _krb5_socks4a_io(S) before testing + * _krb5_socks4a_connected(S) again. + * + * Once this is true, bytes written to the underlying I/O medium + * will be sent by the proxy to the remote host, and bytes + * received by the proxy from the remote host will come flying out + * of the underlying I/O medium. + */ +int +_krb5_socks4a_connected(const struct socks4a *S) +{ + + return S->state == CONNECTED; +} + +/* + * _krb5_socks4a_reading(S) + * + * If the SOCKS4a proxy connection is not yet established, true + * iff we are waiting to read a reply from the proxy. + */ +int +_krb5_socks4a_reading(const struct socks4a *S) +{ + + return S->dir == RD; +} + +/* + * _krb5_socks4a_writing(S) + * + * If the SOCKS4a proxy connection is not yet established, true + * iff we are waiting to write a request to the proxy. + */ +int +_krb5_socks4a_writing(const struct socks4a *S) +{ + + return S->dir == WR; +} + +/* + * _krb5_socks4a_io(S) + * + * Do an I/O step to establish a SOCKS4a proxy connection. To be + * called when (a) the SOCKS4a proxy connection has yet to be + * established, and (b) the I/O needed by the SOCKS4a protocol -- + * reads if _krb5_socks4a_reading(S), writes if + * _krb5_socks4a_writing(S) -- is ready. Caller should call + * _krb5_socks4a_connected(S) when this succeeds to see if the + * connection has completed. + * + * Returns 0 on success, errno code on error. EINTR and EAGAIN + * are transient errors; others such as EIO are fatal. + */ +int +_krb5_socks4a_io(struct socks4a *S) +{ + enum socks4a_state state = S->state; + int k; + + /* + * Verify we're in a state where I/O is relevant. Otherwise, + * fail with EINVAL -- this is an application error, most + * likely calling socks4a_io(S) when socks4a_connected(S) is + * already true. + */ + switch (state) { + case CONNECTING_REQ: + case CONNECTING_REPLY: + break; + case CONNECTED: + default: + return EINVAL; + } + + /* + * Do an increment of I/O by reading from or writing to the + * appropriate fd. + */ + switch (S->dir) { + case NO: + default: + return EINVAL; /* paranoia */ + case RD: + k = (*S->io.sio_read)(S->io.sio_context, S->io.sio_cookie, + S->p, S->n); + if (k == 0) /* EOF */ + return EIO; + break; + case WR: + k = (*S->io.sio_write)(S->io.sio_context, S->io.sio_cookie, + S->p, S->n); + break; + } + + /* + * If the read or write failed, it returned an error code in + * errno, so return that. + */ + if (k == -1) + return errno; + + /* + * If the read or write returned more bytes than we asked for, + * something is amiss, so fail with EIO. + */ + if ((unsigned)k > S->n) + return EIO; + + /* + * Advance the I/O pointer. If there's more I/O to do, stop + * here and let the caller wait before calling socks4a_io(S) + * again. Clear the I/O direction out of paranoia. + */ + S->p += (unsigned)k; + S->n -= (unsigned)k; + if (S->n > 0) + return 0; + S->dir = NO; /* paranoia */ + + /* + * One I/O transfer has completed. Transition to the next + * state. + */ + switch (state) { + case CONNECTING_REQ: /* + * CONNECT request sent. Start reading + * a reply. + */ + S->p = (void *)&S->u.reply; + S->n = sizeof S->u.reply; + S->dir = RD; + S->state = CONNECTING_REPLY; + return 0; + case CONNECTING_REPLY: /* + * CONNECT reply received. Parse it + * and determine whether we're + * sucessfully connected or not. + * + * Ignore dstport and dstip -- not + * relevant to CONNECT, only to BIND. + */ + if (S->u.reply.vn != 0) + return EIO; + if (S->u.reply.cd != 0x5a) /* 0x5a: request granted */ + /* XXX report more specific error */ + return ECONNREFUSED; + S->state = CONNECTED; + return 0; + case CONNECTED: + default: /* XXX unreachable */ + return EIO; + } +} diff --git a/lib/krb5/socks4a.h b/lib/krb5/socks4a.h new file mode 100644 index 000000000..35a3d7c7c --- /dev/null +++ b/lib/krb5/socks4a.h @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2024 Taylor R. Campbell + * 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 AUTHOR 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 AUTHOR 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 SOCKS4A_H +#define SOCKS4A_H + +#include + +/* + * Arbitrary but matches SOCKS5. + */ +#define SOCKS4A_MAXUSERID 255 + +/* + * Binary DNS name -- *(n(1 byte), label(n bytes)), 0(1 byte) -- is + * limited to 255 bytes. Hostname text notation with dots doesn't have + * the zero length byte for the trailing empty label, so that's limited + * to 254 bytes with a trailing dot, or 253 bytes without. To keep it + * simple and allow the trailing dot or not, we'll just take 254 as the + * maximum length. + */ +#define SOCKS4A_MAXHOSTNAME 254 + +struct socks4a; + +struct socks4a_io { + void *sio_context; + void *sio_cookie; + int (*sio_read)(void *, void *, void *, unsigned); + int (*sio_write)(void *, void *, const void *, unsigned); +}; + +/* + * To accommodate static linking without namespace pollution, we name + * the symbols `_krb5_socks4a_...'. + * + * To accommodate make-proto.pl, we spell that out every time -- in the + * declarations here, in the definitions in socks4a.c, and in all the + * uses -- rather than `#define socks4a_connect _krb5_socks4a_connect' + * macro renames or `__asm("_krb5_socks4a_connect")' symbol renames in + * the declaration, since make-proto.pl doesn't know about this kind of + * renaming and assumes symbols that don't start with `_' are public. + */ + +int _krb5_socks4a_connect(struct socks4a_io, const char *, uint16_t, + const char *, struct socks4a **); +int _krb5_socks4a_connected(const struct socks4a *); +int _krb5_socks4a_reading(const struct socks4a *); +int _krb5_socks4a_writing(const struct socks4a *); +int _krb5_socks4a_io(struct socks4a *); +void _krb5_socks4a_free(struct socks4a *); + +#endif /* SOCKS4A_H */ diff --git a/lib/krb5/verify_krb5_conf.c b/lib/krb5/verify_krb5_conf.c index ad4a8fb54..699866380 100644 --- a/lib/krb5/verify_krb5_conf.c +++ b/lib/krb5/verify_krb5_conf.c @@ -454,6 +454,7 @@ struct entry libdefaults_entries[] = { { "proxiable", krb5_config_string, check_boolean, 0 }, { "renew_lifetime", krb5_config_string, check_time, 0 }, { "scan_interfaces", krb5_config_string, check_boolean, 0 }, + { "socks4a_proxy", krb5_config_string, check_host, 0 }, { "srv_lookup", krb5_config_string, check_boolean, 0 }, { "srv_try_txt", krb5_config_string, check_boolean, 0 }, { "ticket_lifetime", krb5_config_string, check_time, 0 },