summaryrefslogtreecommitdiffstats
path: root/proxy.c
diff options
context:
space:
mode:
authorSimon Rettberg2014-03-15 01:49:50 +0100
committerSimon Rettberg2014-03-15 01:49:50 +0100
commitbedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad (patch)
treec7d1995a09f6ed0c4e6873252e957d72f5d07d07 /proxy.c
downloadldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.gz
ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.tar.xz
ldadp-bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad.zip
Lean and mean initial commit
Not much functionality yet
Diffstat (limited to 'proxy.c')
-rw-r--r--proxy.c445
1 files changed, 445 insertions, 0 deletions
diff --git a/proxy.c b/proxy.c
new file mode 100644
index 0000000..c627238
--- /dev/null
+++ b/proxy.c
@@ -0,0 +1,445 @@
+#include "proxy.h"
+#include "client.h"
+#include "server.h"
+#include "helper.h"
+#include "tmpbuffer.h"
+#include "ldap.h"
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define MAXPENDING 200
+#define BINDLEN 500
+
+typedef struct
+{
+ BOOL homeDirectory;
+ BOOL gidNumber;
+ BOOL gecos;
+ BOOL loginShell;
+ BOOL uid;
+ BOOL uidNumber;
+} attr_t;
+
+typedef struct
+{
+ unsigned long clientMessageId;
+ unsigned long serverMessageId;
+ epoll_client_t *client;
+ attr_t attr;
+} pending_t;
+
+typedef struct
+{
+ const char *search;
+ struct string replace;
+} replace_t;
+
+static pending_t _pendingRequest[MAXPENDING];
+static int _pendingCount = 0;
+
+static struct string s_shadowAccount, s_user, s_uid, s_sAMAccountName, s_objectSid;
+static struct string s_objectclass, s_homeDirectory, s_gidNumber, s_gecos, s_dn;
+static struct string s_loginShell, s_uidNumber, s_mail, s_objectCategory, s_memberOf, s_distinguishedName;
+
+//
+
+static int proxy_clientBindRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen);
+static int proxy_serverBindResponse(epoll_server_t *server, const unsigned long messageId, const size_t offset, const size_t maxLen);
+static int proxy_clientSearchRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen);
+static int proxy_serverSearchResult(epoll_server_t *server, const unsigned long messageId, const unsigned long type, const size_t offset, const size_t maxLen);
+
+//
+
+#define SETSTR(x) s_ ## x.s = #x; s_ ## x.l = strlen( #x )
+void proxy_init()
+{
+ static int done = 0;
+ if (done) return;
+ done = 1;
+ SETSTR(shadowAccount);
+ SETSTR(user);
+ SETSTR(uid);
+ SETSTR(sAMAccountName);
+ SETSTR(objectSid);
+ SETSTR(objectclass);
+ SETSTR(homeDirectory);
+ SETSTR(gidNumber);
+ SETSTR(gecos);
+ SETSTR(loginShell);
+ SETSTR(uidNumber);
+ SETSTR(mail);
+ SETSTR(objectCategory);
+ SETSTR(memberOf);
+ SETSTR(distinguishedName);
+ SETSTR(dn);
+}
+#undef SETSTR
+
+int proxy_fromClient(epoll_client_t *client, const size_t maxLen)
+{
+ unsigned long messageId, op;
+ size_t len;
+ const size_t res = scan_ldapmessage(client->readBuffer, client->readBuffer + maxLen, &messageId, &op, &len);
+ if (res == 0) return -1;
+ printf("[C] scan_ldapmessage: Consumed %d, remaining length %d, id %lu, op %lu\n", (int)res, (int)len, messageId, op);
+ // TODO: Caching
+ switch (op) {
+ case BindRequest:
+ return proxy_clientBindRequest(client, messageId, res, maxLen);
+ case SearchRequest:
+ return proxy_clientSearchRequest(client, messageId, res, maxLen);
+ }
+ return 0;
+}
+
+void proxy_removeClient(const epoll_client_t *client)
+{
+ int i, lastValid = -1;
+ for (i = 0; i < _pendingCount; ++i) {
+ if (_pendingRequest[i].client == client) _pendingRequest[i].client = NULL;
+ else if (_pendingRequest[i].client != NULL) lastValid = i;
+ }
+ _pendingCount = lastValid + 1;
+}
+
+int proxy_fromServer(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) return -1;
+ printf("[AD] scan_ldapmessage: Consumed %d, remaining length %d, id %lu, op %lu\n", (int)res, (int)len, messageId, op);
+ switch (op) {
+ case BindResponse:
+ return proxy_serverBindResponse(server, messageId, res, maxLen);
+ case SearchResultEntry:
+ case SearchResultDone:
+ return proxy_serverSearchResult(server, messageId, op, res, maxLen);
+ }
+ printf("Unsupported op: %lu\n", op);
+ return -1;
+}
+
+//
+
+static pending_t* proxy_getFreePendingSlot(epoll_client_t *client)
+{
+ for (int i = 0; i < _pendingCount; ++i) {
+ if (_pendingRequest[i].client == NULL) {
+ memset(&_pendingRequest[i], 0, sizeof(pending_t));
+ _pendingRequest[i].client = client;
+ return &_pendingRequest[i];
+ }
+ }
+ if (_pendingCount < MAXPENDING) {
+ memset(&_pendingRequest[_pendingCount], 0, sizeof(pending_t));
+ _pendingRequest[_pendingCount].client = client;
+ return &_pendingRequest[_pendingCount++];
+ }
+ return NULL;
+}
+
+static pending_t* proxy_getPendingFromServer(unsigned long serverMessageId)
+{
+ for (int i = 0; i < _pendingCount; ++i) {
+ if (_pendingRequest[i].client == NULL) continue;
+ if (_pendingRequest[i].serverMessageId == serverMessageId) return &_pendingRequest[i];
+ }
+ return NULL;
+}
+
+/*
+static void pref(int spaces, char prefix)
+{
+ for (int i = 0; i < spaces; ++i) putchar(' ');
+ putchar(prefix);
+ putchar(' ');
+}
+*/
+
+static inline int equals(struct string *a, struct string *b)
+{
+ if (a->l != b->l) return 0;
+ return strncmp(a->s, b->s, a->l) == 0;
+}
+
+static inline int iequals(struct string *a, struct string *b)
+{
+ if (a->l != b->l) return 0;
+ for (size_t i = 0; i < a->l; ++i) {
+ if (tolower(a->s[i]) != b->s[i]) return 0;
+ }
+ return 1;
+}
+
+// ---- client to AD replacements
+
+//#define PREF(...) do { pref(spaces, prefix); printf(__VA_ARGS__); } while (0)
+static void request_replaceFilter(struct Filter *filter);
+static void request_replaceAdl(struct AttributeDescriptionList **adl, attr_t *attr);
+static void request_replaceAttribute(struct string *attribute, struct string *value);
+
+static void request_replaceFilter(struct Filter *filter)
+{
+ for (; filter != NULL; filter = filter->next) {
+ if (filter->type == PRESENT || filter->type == SUBSTRING) {
+ request_replaceAttribute(&filter->ava.desc, NULL);
+ } else if (filter->type == EQUAL) {
+ request_replaceAttribute(&filter->ava.desc, &filter->ava.value);
+ }
+ //request_replaceAdl(&filter->a, NULL, spaces + 2, prefix);
+ request_replaceFilter(filter->x);
+ }
+}
+
+#define elifSETATTR(x) else if (equals(&(*adl)->a, &s_ ## x)) attr->x = TRUE, next = (*adl)->next, free(*adl), *adl = next
+static void request_replaceAdl(struct AttributeDescriptionList **adl, attr_t *attr)
+{
+ while (*adl != NULL) {
+ struct AttributeDescriptionList *next = NULL;
+ if (attr == NULL) { }
+ elifSETATTR(homeDirectory);
+ elifSETATTR(gidNumber);
+ elifSETATTR(gecos);
+ elifSETATTR(loginShell);
+ elifSETATTR(uidNumber);
+ else request_replaceAttribute(&(*adl)->a, NULL);
+ if (*adl == NULL) break;
+ if (next == NULL) adl = &(*adl)->next; // If next is not NULL, we removed an entry, so we don't need to shift
+ }
+}
+#undef elifSETATTR
+
+static void request_replaceAttribute(struct string *attribute, struct string *value)
+{
+ if (equals(attribute, &s_uid)) {
+ *attribute = s_sAMAccountName;
+ } else if (iequals(attribute, &s_objectclass)) {
+ if (value == NULL) return;
+ if (equals(value, &s_shadowAccount)) *value = s_user;
+ }
+}
+
+// --------- AD to client replacements
+
+static void response_replacePal(struct PartialAttributeList **pal, attr_t *attr);
+static void response_replaceAdl(struct string *type, struct AttributeDescriptionList **adl, attr_t *attr);
+static void response_replaceAttribute(struct string *attribute, struct string *value);
+static struct PartialAttributeList* response_addPal(struct PartialAttributeList *pal, struct string *attribute, const char *format, ...);
+
+#define ADDATTR(x,...) do { if (attr->x) last = response_addPal(last, &s_ ## x, __VA_ARGS__); } while (0)
+#define elifDELATTR(x) else if (equals(&(*pal)->type, &s_ ## x)) next = (*pal)->next
+static void response_replacePal(struct PartialAttributeList **pal, attr_t *attr)
+{
+ struct string *username = NULL;
+ struct PartialAttributeList *last = NULL;
+ while (*pal != NULL) {
+ last = *pal;
+ struct PartialAttributeList *next = NULL;
+ if (0) { }
+ elifDELATTR(homeDirectory);
+ elifDELATTR(gidNumber);
+ elifDELATTR(gecos);
+ elifDELATTR(loginShell);
+ elifDELATTR(uidNumber);
+ elifDELATTR(mail);
+ if (next != NULL) {
+ free_ldapadl((*pal)->values);
+ free(*pal);
+ *pal = next;
+ continue;
+ }
+ response_replaceAdl(&(*pal)->type, &(*pal)->values, attr);
+ // Fetch user name so we can add our fake fields later
+ if (username == NULL && equals(&(*pal)->type, &s_uid)) {
+ username = &(*pal)->values->a;
+ }
+ pal = &(*pal)->next;
+ }
+ if (username != NULL) {
+ ADDATTR(homeDirectory, "/home/%.*s", (int)username->l, username->s);
+ ADDATTR(gecos, "%.*s,,,", (int)username->l, username->s);
+ ADDATTR(loginShell, "/bin/bash");
+ ADDATTR(gidNumber, "1001");
+ }
+}
+#undef ADDATTR
+#undef elifDELATTR
+
+static void response_replaceAdl(struct string *type, struct AttributeDescriptionList **adl, attr_t *attr)
+{
+ while (*adl != NULL) {
+ struct AttributeDescriptionList *next = NULL;
+ // Maybe delete entries here later
+ if (next != NULL) {
+ free(*adl);
+ *adl = next;
+ continue;
+ }
+ response_replaceAttribute(type, &(*adl)->a);
+ adl = &(*adl)->next; // If next is not NULL, we removed an entry, so we don't need to shift
+ }
+}
+
+static void response_replaceAttribute(struct string *attribute, struct string *value)
+{
+ if (equals(attribute, &s_sAMAccountName)) {
+ *attribute = s_uid;
+ } else if (iequals(attribute, &s_objectclass)) {
+ if (value == NULL) return;
+ if (equals(value, &s_user)) *value = s_shadowAccount;
+ } else if (
+ equals(attribute, &s_dn) ||
+ equals(attribute, &s_distinguishedName) ||
+ equals(attribute, &s_memberOf) ||
+ equals(attribute, &s_objectCategory)
+ ) {
+ if (value == NULL) return;
+ struct string alias;
+ if (server_baseToAlias(value, &alias) != -1) {
+ *value = alias;
+ }
+ }
+}
+
+static struct PartialAttributeList* response_addPal(struct PartialAttributeList *pal, struct string *attribute, const char *format, ...)
+{
+ struct PartialAttributeList *next = malloc(sizeof(struct PartialAttributeList));
+ va_list args;
+ va_start(args, format);
+ pal->next = next;
+ next->next = NULL;
+ next->type = *attribute;
+ next->values = malloc(sizeof(struct AttributeDescriptionList));
+ tmpbuffer_formatva(&next->values->a, format, args);
+ next->values->next = NULL;
+ va_end(args);
+ return next;
+}
+
+// -----
+
+static int proxy_clientSearchRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen)
+{
+ struct SearchRequest req;
+ struct string realBase;
+ const size_t res = scan_ldapsearchrequest(client->readBuffer + offset, client->readBuffer + maxLen, &req);
+ if (res == 0) return -1;
+ const int server = server_aliasToBase(&req.baseObject, &realBase);
+ if (server == -1) {
+ printf("scan_ldapsearchrequest: baseObj %.*s unknown.\n", (int)req.baseObject.l, req.baseObject.s);
+ return -1;
+ }
+ req.baseObject = realBase;
+ printf("scan_ldapsearchrequest: baseO: %.*s, scope: %d, derefAliases: %d\n", (int)req.baseObject.l, req.baseObject.s, req.scope, req.derefAliases);
+ //
+ pending_t *pending = proxy_getFreePendingSlot(client);
+ if (pending == NULL) {
+ printf("No more slots for pending requests\n");
+ return -1;
+ }
+ if (req.attributes == NULL) {
+ memset(&pending->attr, -1, sizeof(pending->attr));
+ } else {
+ request_replaceAdl(&req.attributes, &pending->attr);
+ }
+ request_replaceFilter(req.filter);
+ helper_printfilter(req.filter);
+ helper_printal(req.attributes);
+ printf("Attrs: ");
+ for (size_t i = 0; i < sizeof(attr_t); ++i) putchar(((char*)&pending->attr)[i] == 0 ? '0' : '1');
+ putchar('\n');
+ pending->clientMessageId = messageId;
+ pending->serverMessageId = server_searchRequest(server, &req);
+ if (pending->serverMessageId == 0) {
+ // Failed to forward.. TODO: Fail client
+ pending->client = NULL;
+ }
+ free_ldapsearchrequest(&req);
+ //
+ if (pending->client == NULL) return -1;
+ return 0;
+}
+
+static int proxy_serverSearchResult(epoll_server_t *server, const unsigned long messageId, const unsigned long type, const size_t offset, const size_t maxLen)
+{
+ static char *bodyBuffer = NULL;
+ if (bodyBuffer == NULL) bodyBuffer = malloc(MAXMSGLEN);
+ pending_t *pending = proxy_getPendingFromServer(messageId);
+ if (pending == NULL) {
+ printf("No client matching server message id %lu\n", messageId);
+ return 0;
+ }
+ printf("ServerID %lu -> ClientID %lu\n", messageId, pending->clientMessageId);
+ const char *body;
+ size_t bodyLen;
+ if (type == SearchResultDone) {
+ // Just forward with new header
+ bodyLen = maxLen - offset;
+ body = server->readBuffer + offset;
+ } else {
+ // Transform reply
+ struct SearchResultEntry sre;
+ const size_t res = scan_ldapsearchresultentry(server->readBuffer + offset, server->readBuffer + maxLen, &sre);
+ if (res == 0) return -1;
+ struct string alias;
+ if (server_baseToAlias(&sre.objectName, &alias) != -1) {
+ sre.objectName = alias;
+ }
+ response_replacePal(&sre.attributes, &pending->attr);
+ bodyLen = fmt_ldapsearchresultentry(NULL, &sre);
+ if (bodyLen == 0) {
+ printf("Error formatting ldapsearchresultentry after transformation\n");
+ free_ldapsearchresultentry(&sre);
+ return -1;
+ }
+ if (bodyLen > MAXMSGLEN) {
+ printf("ldapsearchresultentry too large after transformation\n");
+ free_ldapsearchresultentry(&sre);
+ return -1;
+ }
+ fmt_ldapsearchresultentry(bodyBuffer, &sre);
+ free_ldapsearchresultentry(&sre);
+ body = bodyBuffer;
+ }
+ // Build header and fire away
+ const size_t headerLen = fmt_ldapmessage(NULL, pending->clientMessageId, type, bodyLen);
+ char buffer[headerLen];
+ fmt_ldapmessage(buffer, pending->clientMessageId, type, bodyLen);
+ client_send(pending->client, buffer, headerLen, TRUE);
+ client_send(pending->client, body, bodyLen, FALSE);
+ if (type == SearchResultDone) pending->client = NULL;
+ return 0;
+}
+
+static int proxy_clientBindRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen)
+{
+ unsigned long version, method;
+ struct string name, password;
+ const size_t res = scan_ldapbindrequest(client->readBuffer + offset, client->readBuffer + maxLen, &version, &name, &method);
+ if (res == 0) return -1; // Parsing request failed
+ const size_t res2 = scan_ldapstring(client->readBuffer + offset + res, client->readBuffer + maxLen, &password);
+ printf("scan_ldapbindrequest: Consumed %d, version %lu, method %lu, name '%.*s', bindpw: '%.*s'\n", (int)(res + res2), version, method, (int)name.l, name.s, (int)password.l, password.s);
+ char buffer[800];
+ char *bufoff = buffer + 100;
+ const size_t bodyLen = fmt_ldapbindresponse(bufoff, 0, "", "main screen turn on", "");
+ const size_t headerLen = fmt_ldapmessage(NULL, messageId, BindResponse, bodyLen);
+ if (headerLen > 100) return -1; // Too long - don't care
+ fmt_ldapmessage(bufoff - headerLen, messageId, BindResponse, bodyLen);
+ return client_send(client, bufoff - headerLen, bodyLen + headerLen, FALSE);
+}
+
+static int proxy_serverBindResponse(epoll_server_t *server, const unsigned long messageId, const size_t offset, const size_t maxLen)
+{
+ unsigned long result;
+ struct string binddn, error, refer;
+ const size_t res = scan_ldapbindresponse(server->readBuffer + offset, server->readBuffer + maxLen, &result, &binddn, &error, &refer);
+ if (res == 0) return -1; // Parsing request failed
+ printf("scan_ldapbindresponse: Consumed %d, result: %lu, binddn: %.*s, error: %.*s, referral: %.*s\n", (int)res, result, (int)binddn.l, binddn.s, (int)error.l, error.s, (int)refer.l, refer.s);
+ if (result == success) server->bound = TRUE;
+ return 0;
+}
+