#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; }