From bedd2e7ccb1595c23e159eaa952ae1b0b5a3d2ad Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Sat, 15 Mar 2014 01:49:50 +0100 Subject: Lean and mean initial commit Not much functionality yet --- proxy.c | 445 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 proxy.c (limited to 'proxy.c') 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 +#include +#include +#include +#include + +#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; +} + -- cgit v1.2.3-55-g7522