summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2019-06-13 18:11:44 +0200
committerSimon Rettberg2019-06-13 18:11:44 +0200
commit4df0a33063960b1ab660a1d8ceb3211766627b78 (patch)
tree316d3c939bd815ea47b9e80a3e006c3d9a8cbde2
parenttag class CONTEXT_SPECIFIC <-> PRIVATE was swapped (diff)
downloadldadp-4df0a33063960b1ab660a1d8ceb3211766627b78.tar.gz
ldadp-4df0a33063960b1ab660a1d8ceb3211766627b78.tar.xz
ldadp-4df0a33063960b1ab660a1d8ceb3211766627b78.zip
Add STARTTLS support
-rw-r--r--ldadp.c6
-rw-r--r--openssl.c2
-rw-r--r--server.c107
-rw-r--r--server.h2
-rw-r--r--types.h2
5 files changed, 103 insertions, 16 deletions
diff --git a/ldadp.c b/ldadp.c
index c203362..3f69745 100644
--- a/ldadp.c
+++ b/ldadp.c
@@ -72,11 +72,11 @@ int main(int argc, char **argv)
lsn.callback = &listen_callback;
lsn.fd = socket_tcp4();
if (certFile != NULL && keyFile != NULL) {
- printf("Using SSL\n");
+ printf("[Listener] Using SSL\n");
ssl_init();
lsn.sslContext = ssl_newServerCtx(certFile, keyFile);
} else {
- printf("Not using SSL\n");
+ printf("[Listener] Not using SSL\n");
}
if (lsn.fd == -1) bail("Could not create listen socket");
if (socket_bind4_reuse(lsn.fd, listen_addr, localPort) == -1) bail("Could not bind to listening port");
@@ -183,6 +183,8 @@ static int loadConfig_handler(void *stuff, const char *section, const char *key,
server_setUidMapStore(section, value);
} else if (strcmp(key, "genuidnumber") == 0) {
server_setGenUidNumber(section, value);
+ } else if (strcmp(key, "usestarttls") == 0) {
+ server_setUseStartTls(section, value);
} else if (strncmp(key, "map.", 4) == 0) {
server_setMap(section, key+4, value);
} else {
diff --git a/openssl.c b/openssl.c
index 0b64382..7611c0c 100644
--- a/openssl.c
+++ b/openssl.c
@@ -52,7 +52,7 @@ SSL_CTX* ssl_newClientCtx(const char *cabundle)
if (ctx == NULL) ssl_printErrors("newClientCtx: ctx is NULL");
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); // | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
- if (cabundle != NULL && cabundle[0] != '\0') {
+ if (cabundle != NULL && cabundle[0] != '\0' && strcmp(cabundle, "*") != 0) {
if (SSL_CTX_load_verify_locations(ctx, cabundle, NULL) == 0) {
ssl_printErrors("Loading trusted certs failed");
exit(1);
diff --git a/server.c b/server.c
index 54fd154..9bf9e58 100644
--- a/server.c
+++ b/server.c
@@ -33,6 +33,7 @@ static BOOL server_ensureConnected(server_t *server);
static BOOL server_ensureSendBuffer(epoll_server_t * const s, const size_t len);
static int server_connectInternal(server_t *server);
static BOOL server_connectSsl(epoll_server_t *server);
+static BOOL server_handleStartTlsResponse(epoll_server_t *server, const size_t len);
// Generate a message ID for request to AD
static inline uint32_t msgId()
@@ -93,6 +94,14 @@ void server_setGenUidNumber(const char *server, const char *enabledStr)
plog(DEBUG_VERBOSE, "Using UID mapping for %s: %d", server, (int)entry->genUidNumber);
}
+void server_setUseStartTls(const char *server, const char *enabledStr)
+{
+ server_t *entry = server_create(server);
+ if (entry == NULL) return;
+ entry->useStartTls = parseBool(enabledStr);
+ plog(DEBUG_VERBOSE, "STARTTLS for %s: %d", server, (int)entry->useStartTls);
+}
+
static void strtolower(char *str)
{
while (*str != '\0') {
@@ -167,12 +176,14 @@ void server_setCaBundle(const char *server, const char *file)
server_t *entry = server_create(server);
if (entry == NULL) return;
if (file == NULL || *file == '\0') return;
- int fh = open(file, O_RDONLY);
- if (fh == -1) {
- printf("Error: cabundle '%s' not readable.\n", file);
- exit(1);
+ if (strcmp(file, "*") != 0) {
+ int fh = open(file, O_RDONLY);
+ if (fh == -1) {
+ printf("Error: cabundle '%s' not readable.\n", file);
+ exit(1);
+ }
+ close(fh);
}
- close(fh);
if (snprintf(entry->cabundle, MAXPATH, "%s", file) >= MAXPATH) printf("Warning: CaBundle for %s is too long.\n", server);
ssl_init();
}
@@ -201,7 +212,7 @@ void server_setHomeTemplate(const char *server, const char *hometemplate)
void server_setHomeAttribute(const char *server, const char *homeattribute)
{
server_t *entry = server_create(server);
- if (entry == NULL || entry->sslContext != NULL) return;
+ if (entry == NULL) return;
free((void*)entry->map.homemount.s);
char *tmp = strdup(homeattribute);
strtolower(tmp);
@@ -238,7 +249,7 @@ void server_setFingerprint(const char *server, const char *fingerprint)
chars++;
}
if (chars / 2 != FINGERPRINTLEN || val == -1) {
- printf("Warning: Fingerprint for %s is invalid (adsha1 should be a SHA-1 hash of the cert in hex representation.)\n", server);
+ printf("Warning: Fingerprint for %s is invalid (should be a SHA-1 hash of the cert in hex representation.)\n", server);
return;
}
printf("Using fingerprint ");
@@ -284,6 +295,10 @@ BOOL server_initServers()
if (server->cabundle[0] != '\0' || memcmp(server->fingerprint, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) != 0) {
// Have cabundle or fingerprint - use SSL to talk to server
server->sslContext = ssl_newClientCtx(server->cabundle);
+ if (strcmp(server->cabundle, "*") == 0) {
+ server->cabundle[0] = '\0';
+ }
+ printf("Using SSL for Uplink\n");
}
printf("%s:\n Bind: %s\n Base: %s\n", server->addr, server->bind, server->base);
printf("Plain LDAP-LDAP: %d\n", (int)server->plainLdap);
@@ -363,6 +378,7 @@ uint32_t server_tryUserBind(server_t *server, struct string *binddn, struct stri
con->fd = -1;
con->bound = FALSE;
con->dynamic = TRUE;
+ con->startTlsId = 0;
con->sbPos = con->sbFill = 0;
int sock = server_connectInternal(server);
if (sock == -1) {
@@ -444,6 +460,7 @@ void server_free(epoll_server_t *server)
proxy_removeServer(server);
server->bound = FALSE;
server->kill = FALSE;
+ server->startTlsId = 0;
if (server->ssl != NULL) {
SSL_free(server->ssl);
server->ssl = NULL;
@@ -530,7 +547,13 @@ static void server_haveIn(epoll_server_t *server)
len += consumed;
if (len > server->rbPos) break; // Body not complete
//printf("[AD] Received complete reply (need %d, have %d)...\n", (int)len, (int)server->rbPos);
- if (!proxy_fromServer(server, len)) {
+ // Complete STARTTLS
+ if (server->ssl == NULL && server->startTlsId != 0) {
+ if (!server_handleStartTlsResponse(server, len)) {
+ server->kill = TRUE;
+ return;
+ }
+ } else if (!proxy_fromServer(server, len)) {
if (server->dynamic) {
server->kill = TRUE;
return;
@@ -588,6 +611,7 @@ static BOOL server_haveOut(epoll_server_t * const server)
{
if (server->kill) return FALSE;
if (server->ssl != NULL && !server->sslConnected) return TRUE;
+ if (server->ssl == NULL && server->startTlsId != 0) return TRUE; // We asked the server for TLS, don't send anything until we get a reply
// Bind not sent/acknowledged yet - send bind if pending, otherwise do nothing
if (!server->bound) {
if (server->bindLen == 0) return TRUE;
@@ -745,17 +769,17 @@ static int server_connectInternal(server_t *server)
if (server->lastLookup + 300 < time(NULL)) {
sock = helper_connect4(server->addr, port, server->ip);
if (sock == -1) {
- printf("[Proxy] Could not resolve hostname or connect to AD server %s (errno=%d)\n", server->addr, errno);
+ printf("[Server] Could not resolve hostname or connect to AD server %s (errno=%d)\n", server->addr, errno);
return -1;
}
} else {
sock = helper_newSocket();
if (sock == -1) {
- printf("[Proxy] Could not allocate socket for connection to AD server %s (errno=%d)\n", server->addr, errno);
+ printf("[Server] Could not allocate socket for connection to AD server %s (errno=%d)\n", server->addr, errno);
return -1;
}
if (socket_connect4(sock, server->ip, port) == -1) {
- printf("[Proxy] Could not connect to cached IP (%s) of %s (errno=%d)\n", server->ip, server->addr, errno);
+ printf("[Server] Could not connect to cached IP (%s) of %s (errno=%d)\n", server->ip, server->addr, errno);
server->lastLookup = 0;
close(sock);
return -1;
@@ -768,13 +792,29 @@ static BOOL server_connectSsl(epoll_server_t *server)
{
if (server->serverData->sslContext == NULL) return TRUE;
server->sslConnected = FALSE;
+ if (server->serverData->useStartTls && server->startTlsId == 0) {
+ static const char oid[] = "1.3.6.1.4.1.1466.20037";
+ static const size_t oidlen = sizeof(oid) - 1;
+ char buf[200];
+ size_t len;
+ uint32_t mid = msgId();
+ len = fmt_asn1string(NULL, CONTEXT_SPECIFIC, 0, 0, oid, oidlen);
+ len = fmt_ldapmessage(buf, mid, ExtendedRequest, len);
+ len += fmt_asn1string(buf + len, CONTEXT_SPECIFIC, 0, 0, oid, oidlen);
+ if (!server_send(server, buf, len, FALSE)) {
+ printf("[Server] Sending STARTTLS request failed\n");
+ return FALSE;
+ }
+ server->startTlsId = mid;
+ return TRUE;
+ }
server->ssl = ssl_new(server->fd, server->serverData->sslContext);
if (server->ssl == NULL) {
- printf("[Proxy] Could not get SSL client from context\n");
+ printf("[Server] Could not get SSL client from context\n");
return FALSE;
}
if (!ssl_connectServer(server)) {
- printf("[Proxy] SSL connect to AD server %s failed.\n", server->serverData->addr);
+ printf("[Server] SSL connect to AD server %s failed.\n", server->serverData->addr);
SSL_free(server->ssl);
server->ssl = NULL;
return FALSE;
@@ -782,3 +822,44 @@ static BOOL server_connectSsl(epoll_server_t *server)
return TRUE;
}
+static BOOL server_handleStartTlsResponse(epoll_server_t *server, const size_t maxLen)
+{
+ unsigned long messageId, op;
+ size_t len;
+ const size_t res = scan_ldapmessage(server->readBuffer, server->readBuffer + maxLen, &messageId, &op, &len);
+ if (res == 0) {
+ printf("[Server] Error parsing STARTLS reply\n");
+ return FALSE;
+ }
+ if (messageId != server->startTlsId) {
+ printf("[Server] STARTTLS reply doesn't have expected message id\n");
+ return FALSE;
+ }
+ if (op != ExtendedResponse) {
+ printf("[Server] STARTTLS reply doesn't have op ExtendedResponse\n");
+ return FALSE;
+ }
+ enum asn1_tagclass tc;
+ enum asn1_tagtype tt;
+ unsigned long tag;
+ long value;
+ len = scan_asn1int(server->readBuffer + res, server->readBuffer + maxLen, &tc, &tt, &tag, &value);
+ if (len == 0) {
+ printf("[Server] Could not read resultCode int from STARTTLS reply\n");
+ return FALSE;
+ }
+ if (tag != ENUMERATED && tag != INTEGER) {
+ printf("[Server] STARTTLS reply doesn't contain resultCode\n");
+ return FALSE;
+ }
+ if (value != 0) {
+ printf("[Server] STARTTLS reply has resultCode != 0\n");
+ return FALSE;
+ }
+ if (!server_connectSsl(server)) {
+ printf("[Server] ...after successful STARTTLS reply\n");
+ return FALSE;
+ }
+ return TRUE;
+}
+
diff --git a/server.h b/server.h
index 1a01935..4626e1d 100644
--- a/server.h
+++ b/server.h
@@ -16,6 +16,8 @@ void server_setUidMapStore(const char *server, const char *fileName);
void server_setGenUidNumber(const char *server, const char *value);
+void server_setUseStartTls(const char *server, const char *enabledStr);
+
void server_setMap(const char *server, const char *attribute, const char *value);
void server_setBind(const char *server, const char *bind);
diff --git a/types.h b/types.h
index 94e2ddd..ff2ddf4 100644
--- a/types.h
+++ b/types.h
@@ -86,6 +86,7 @@ struct _epoll_server_t_ {
BOOL bound; // Already bound to server?
BOOL dynamic;
BOOL sslConnected;
+ uint32_t startTlsId; // Whether STARTTLS request was already sent (>0)
BOOL kill; // Should the connection be killed?
BOOL writeBlocked; // An SSL_write returned WANT_*, so we must not reallocate the current send buffer
time_t lastActive;
@@ -129,6 +130,7 @@ struct _server_t_ {
BOOL plainLdap;
BOOL fixNumeric; // prefix numeric account names with an 's'
BOOL genUidNumber; // generate uidNumber attribute locally (and keep track)
+ BOOL useStartTls; // Establish plain connection, then upgrade via STARTTLS
uint16_t port;
SSL_CTX *sslContext;
epoll_server_t con;