Hi Omar Thanks for the feedback. A updated patch is attached.
[2024-01-23 11:26] Omar Polo <o...@omarpolo.com> > On 2024/01/23 01:24:57 +0100, Philipp <phil...@bureaucracy.de> wrote: > > I have had a bit of time and implemented ldaps support for table-ldap. > > It is currently untested and has some todos. But I would say it's > > complete enough to share. So other can comment on the code. A patch > > is attached > > I don't use ldap and completely lack the experience with it, so I can > only provide some feedback on the code itself, not if it makes sense to > have TLS in here nor provide testing. As said in the other thread I'll test this tomorrow. > [...] > > @@ -69,15 +71,15 @@ enum aldap_protocol { > > }; > > > > struct aldap_url { > > - int protocol; > > - char *host; > > - in_port_t port; > > - char *dn; > > + enum aldap_protocol protocol; > > tbh I would have just kept 'int protocol' here. not sure if specifying > the enum and re-indent everything really helps. but i don't object it :) I just have seen that the 'enum protocol' is not used in the struct and because I had the file allready open I just changed it. This doesn't fix any bug. It's only a bit cleaner code. > > + char *host; > > + in_port_t port; > > + char *dn; > > #define MAXATTR 1024 > > - char *attributes[MAXATTR]; > > - int scope; > > - char *filter; > > - char *buffer; > > + char *attributes[MAXATTR]; > > + int scope; > > + char *filter; > > + char *buffer; > > }; > > > > enum protocol_op { > > [...] > > @@ -784,8 +785,12 @@ ber_write_elements(struct ber *ber, struct ber_element > > *root) > > return -1; > > > > /* XXX this should be moved to a different function */ > > - if (ber->fd != -1) > > + if (ber->fd != -1) { > > + if (ber->tls_ctx) { > > + return tls_write(ber->tls_ctx, ber->br_wbuf, len); > > This, and the other calls to tls_read are wrong. Even in the blocking > case, we need to wrap these in a loop to handle TLS_WANT_POLLIN and > TLS_WANT_POLLOUT, as per the manpage example. > > I remember being bitten by this, as libretls (libtls over openssl) > returns the TLS_WANT_* more often than LibreSSL' libtls. A good to know. I'm a bit unsure about my solution. Do I need to acualy poll() in the read/write wrapper or is it good enough to just call it in a loop? > > [...] > > @@ -80,6 +81,34 @@ table_ldap_fetch(int service, struct dict *params, char > > *dst, size_t sz) > > return -1; > > } > > > > +static struct tls * > > +ldaps_connect(int fd, char *hostname) > > +{ > > + /* XXX log */ > > + struct tls *ctx = NULL; > > + struct tls_config *config = tls_config_new(); > > + if (!config) > > + goto fail; > > + if (tls_config_set_ca_path(config, "/etc/ssl/certs") == -1) > > + goto fail; > > Is this needed? out-of-the-box on OpenBSD this directory doesn't exist, > but don't know if it's the "de-facto" place to store certs for ldap. In > any case, I think it should be at least configurable. This is WIP, I have just added the default ca store of debian. I coudn't find if libtls does this by default. A config option for a ca file is in planing. > > + if ((ctx = tls_client()) == NULL) > > + goto fail; > > + if (tls_configure(ctx, config) == -1) > > + goto fail; > > + if (tls_connect_socket(ctx, fd, hostname) == -1) > > + goto fail; > > + if (tls_handshake(ctx) == -1) > > + goto fail; > > tls_handshake() needs almost the same loop as tls_write and tls_read to > handle TLS_WANT_POLLIN/OUT. > > > + tls_config_free(config); > > + return ctx; > > +fail: > > + tls_close(ctx); > > + tls_free(ctx); > > + tls_config_free(config); > > these close/free calls needs to be guarded since ctx and config may be > NULL when here and these functions will deference the argument. Only for the tls_close, tls_free() and tls_config_free() behave like free() and are noops with a NULL as argument. > > + return NULL; > > +} > > + > > static struct aldap * > > ldap_connect(const char *addr) > > { > > @@ -121,13 +150,25 @@ ldap_connect(const char *addr) > > ouch... ldap_connect is really a bit over-complicate. I'd prefer if we > simplify this a bit first. I'll soon send a diff for this. Nice, I have rebased my patch on the cleanup.
From 4a49cdea6ff08ed758544022ca0842b1c4a19076 Mon Sep 17 00:00:00 2001 From: Philipp Takacs <phil...@bureaucracy.de> Date: Tue, 23 Jan 2024 00:55:23 +0100 Subject: [PATCH 03/11] table-ldap add ldaps support untested based on libtls, autohell is ugly ass hell need also some log messages --- configure.ac | 2 +- extras/tables/table-ldap/Makefile.am | 1 + extras/tables/table-ldap/aldap.c | 24 +++++++++-- extras/tables/table-ldap/aldap.h | 20 +++++---- extras/tables/table-ldap/ber.c | 26 +++++++++--- extras/tables/table-ldap/ber.h | 1 + extras/tables/table-ldap/table_ldap.c | 42 ++++++++++++++++++- extras/tables/table-ldap/tlswrapper.c | 60 +++++++++++++++++++++++++++ extras/tables/table-ldap/tlswrapper.h | 3 ++ 9 files changed, 158 insertions(+), 21 deletions(-) create mode 100644 extras/tables/table-ldap/tlswrapper.c create mode 100644 extras/tables/table-ldap/tlswrapper.h diff --git a/configure.ac b/configure.ac index 410a61b..14608b1 100644 --- a/configure.ac +++ b/configure.ac @@ -577,7 +577,7 @@ AC_ARG_WITH([libssl], ] ) ## XXX chl -lssl manually added -LIBS="-lcrypto -lssl $LIBS" +LIBS="-lcrypto -lssl -ltls $LIBS" AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL], [1], [Define if your ssl headers are included with #include <openssl/header.h>])], diff --git a/extras/tables/table-ldap/Makefile.am b/extras/tables/table-ldap/Makefile.am index f2ecba2..6a4f6c2 100644 --- a/extras/tables/table-ldap/Makefile.am +++ b/extras/tables/table-ldap/Makefile.am @@ -7,3 +7,4 @@ table_ldap_SOURCES = $(SRCS) table_ldap_SOURCES += table_ldap.c table_ldap_SOURCES += aldap.c table_ldap_SOURCES += ber.c +table_ldap_SOURCES += tlswrapper.c diff --git a/extras/tables/table-ldap/aldap.c b/extras/tables/table-ldap/aldap.c index 552ea52..edd3cc3 100644 --- a/extras/tables/table-ldap/aldap.c +++ b/extras/tables/table-ldap/aldap.c @@ -21,6 +21,7 @@ #include <inttypes.h> #include <string.h> #include <stdlib.h> +#include <tls.h> #include <unistd.h> #include "aldap.h" @@ -55,6 +56,12 @@ void ldap_debug_elements(struct ber_element *); int aldap_close(struct aldap *al) { + if (al->ber.tls_ctx) { + if (tls_close(al->ber.tls_ctx) == -1) + return (-1); + tls_free(al->ber.tls_ctx); + } + if (close(al->ber.fd) == -1) return (-1); @@ -65,13 +72,14 @@ aldap_close(struct aldap *al) } struct aldap * -aldap_init(int fd) +aldap_init(int fd, struct tls *ctx) { struct aldap *a; if ((a = calloc(1, sizeof(*a))) == NULL) return NULL; a->ber.fd = fd; + a->ber.tls_ctx = ctx; return a; } @@ -574,10 +582,15 @@ aldap_parse_url(char *url, struct aldap_url *lu) p = lu->buffer; /* protocol */ - if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) != 0) + if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) == 0) { + lu->protocol = LDAP; + p += strlen(LDAP_URL); + } else if (strncasecmp(LDAPS_URL, p, strlen(LDAP_URL)) == 0) { + lu->protocol = LDAPS; + p += strlen(LDAPS_URL); + } else { goto fail; - lu->protocol = LDAP; - p += strlen(LDAP_URL); + } /* host and optional port */ if ((forward = strchr(p, '/')) != NULL) @@ -594,6 +607,9 @@ aldap_parse_url(char *url, struct aldap_url *lu) } } else { lu->port = LDAP_PORT; + if (lu->protocol == LDAPS) { + lu->port = LDAPS_PORT; + } } /* fail if no host is given */ if (strlen(p) == 0) diff --git a/extras/tables/table-ldap/aldap.h b/extras/tables/table-ldap/aldap.h index 7cfd637..fec106b 100644 --- a/extras/tables/table-ldap/aldap.h +++ b/extras/tables/table-ldap/aldap.h @@ -20,6 +20,8 @@ #define LDAP_URL "ldap://" #define LDAP_PORT 389 +#define LDAPS_URL "ldaps://" +#define LDAPS_PORT 636 #define LDAP_PAGED_OID "1.2.840.113556.1.4.319" struct aldap { @@ -69,15 +71,15 @@ enum aldap_protocol { }; struct aldap_url { - int protocol; - char *host; - in_port_t port; - char *dn; + enum aldap_protocol protocol; + char *host; + in_port_t port; + char *dn; #define MAXATTR 1024 - char *attributes[MAXATTR]; - int scope; - char *filter; - char *buffer; + char *attributes[MAXATTR]; + int scope; + char *filter; + char *buffer; }; enum protocol_op { @@ -186,7 +188,7 @@ enum ldap_subfilter { LDAP_FILT_SUBS_FIN = 2, }; -struct aldap *aldap_init(int fd); +struct aldap *aldap_init(int fd, struct tls *ctx); int aldap_close(struct aldap *); struct aldap_message *aldap_parse(struct aldap *); void aldap_freemsg(struct aldap_message *); diff --git a/extras/tables/table-ldap/ber.c b/extras/tables/table-ldap/ber.c index 0f92bb2..21a1924 100644 --- a/extras/tables/table-ldap/ber.c +++ b/extras/tables/table-ldap/ber.c @@ -24,12 +24,14 @@ #include <limits.h> #include <stdlib.h> #include <err.h> /* XXX for debug output */ +#include <stdarg.h> #include <stdio.h> /* XXX for debug output */ #include <string.h> +#include <tls.h> #include <unistd.h> -#include <stdarg.h> #include "ber.h" +#include "tlswrapper.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) @@ -784,8 +786,12 @@ ber_write_elements(struct ber *ber, struct ber_element *root) return -1; /* XXX this should be moved to a different function */ - if (ber->fd != -1) + if (ber->fd != -1) { + if (ber->tls_ctx) { + return tls_write_wrapper(ber->tls_ctx, ber->br_wbuf, len); + } return write(ber->fd, ber->br_wbuf, len); + } return (len); } @@ -1233,8 +1239,12 @@ ber_getc(struct ber *b, unsigned char *c) */ if (b->fd == -1) r = ber_readbuf(b, c, 1); - else - r = read(b->fd, c, 1); + else { + if (b->tls_ctx) + r = tls_read_wrapper(b->tls_ctx, c, 1); + else + r = read(b->fd, c, 1); + } return r; } @@ -1253,8 +1263,12 @@ ber_read(struct ber *ber, void *buf, size_t len) while (remain > 0) { if (ber->fd == -1) r = ber_readbuf(ber, b, remain); - else - r = read(ber->fd, b, remain); + else { + if (ber->tls_ctx) + r = tls_read_wrapper(ber->tls_ctx, b, remain); + else + r = read(ber->fd, b, remain); + } if (r == -1) { if (errno == EINTR || errno == EAGAIN) continue; diff --git a/extras/tables/table-ldap/ber.h b/extras/tables/table-ldap/ber.h index d656508..4987fc2 100644 --- a/extras/tables/table-ldap/ber.h +++ b/extras/tables/table-ldap/ber.h @@ -34,6 +34,7 @@ struct ber_element { struct ber { int fd; + struct tls *tls_ctx; unsigned char *br_wbuf; unsigned char *br_wptr; unsigned char *br_wend; diff --git a/extras/tables/table-ldap/table_ldap.c b/extras/tables/table-ldap/table_ldap.c index 79a26a8..3ea17a0 100644 --- a/extras/tables/table-ldap/table_ldap.c +++ b/extras/tables/table-ldap/table_ldap.c @@ -26,10 +26,12 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <tls.h> #include <unistd.h> #include <smtpd-api.h> #include "aldap.h" +#include "tlswrapper.h" #define MAX_LDAP_IDENTIFIER 32 #define MAX_LDAP_URL 256 @@ -80,6 +82,36 @@ table_ldap_fetch(int service, struct dict *params, char *dst, size_t sz) return -1; } +static struct tls * +ldaps_connect(int fd, char *hostname) +{ + /* XXX log */ + struct tls *ctx = NULL; + struct tls_config *config = tls_config_new(); + if (!config) + goto fail; + /* XXX fix needed, build time config and run time for ca_file */ + if (tls_config_set_ca_path(config, "/etc/ssl/certs") == -1) + goto fail; + if ((ctx = tls_client()) == NULL) + goto fail; + if (tls_configure(ctx, config) == -1) + goto fail; + if (tls_connect_socket(ctx, fd, hostname) == -1) + goto fail; + if (tls_handshake_wrapper(ctx, fd) == -1) + goto fail; + + tls_config_free(config); + return ctx; +fail: + if (ctx) + tls_close(ctx); + tls_free(ctx); + tls_config_free(config); + return NULL; +} + static struct aldap * ldap_connect(const char *addr) { @@ -123,8 +155,16 @@ ldap_connect(const char *addr) continue; if (connect(fd, res->ai_addr, res->ai_addrlen) == 0) { + struct tls *ctx = NULL; + if (lu.protocol == LDAPS) { + if ((ctx = ldaps_connect(fd, lu.host)) == NULL) { + close(fd); + fd = -1; + continue; + } + } aldap_free_url(&lu); - return aldap_init(fd); + return aldap_init(fd, ctx); } close(fd); diff --git a/extras/tables/table-ldap/tlswrapper.c b/extras/tables/table-ldap/tlswrapper.c new file mode 100644 index 0000000..03487ec --- /dev/null +++ b/extras/tables/table-ldap/tlswrapper.c @@ -0,0 +1,60 @@ +#include <poll.h> +#include <tls.h> +#include "tlswrapper.h" + +int +tls_handshake_wrapper(struct tls *ctx, int fd) +{ + struct pollfd pfd[1]; + pfd[0].fd = fd; + + while (1) { + switch (tls_handshake(ctx)) { + case -1: + return -1; + case TLS_WANT_POLLIN: + pfd[0].events = POLLIN; + break; + case TLS_WANT_POLLOUT: + pfd[0].events = POLLOUT; + break; + default: + return 0; + } + poll(pfd, 1, -1); + } +} + +ssize_t +tls_write_wrapper(struct tls *ctx, const void *buf, size_t buflen) +{ + ssize_t ret; + ssize_t len = buflen; + + while (len > 0) { + ret = tls_write(ctx, buf, len); + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) + continue; + if (ret == -1) + return ret; + buf += ret; + len -= ret; + } + return buflen; +} + +ssize_t +tls_read_wrapper(struct tls *ctx, void *buf, size_t buflen) +{ + ssize_t rlen = 0; + ssize_t ret; + while (rlen <= 0) { + ret = tls_read(ctx, buf, buflen); + if (ret == -1) + return -1; + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) + continue; + rlen += ret; + } + return rlen; +} diff --git a/extras/tables/table-ldap/tlswrapper.h b/extras/tables/table-ldap/tlswrapper.h new file mode 100644 index 0000000..02aa02d --- /dev/null +++ b/extras/tables/table-ldap/tlswrapper.h @@ -0,0 +1,3 @@ +int tls_handshake_wrapper(struct tls *ctx, int fd); +ssize_t tls_write_wrapper(struct tls *ctx, const void *buf, size_t buflen); +ssize_t tls_read_wrapper(struct tls *ctx, void *buf, size_t buflen); -- 2.39.2