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 },