From f0b46f7a343a79d1eeb29c0d45942df646e9cc35 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 18 Mar 2014 19:32:40 +0100 Subject: First working version with user and group support, login relaying --- helper.c | 34 +++++--- ldadp.c | 2 + ldap.h | 1 + proxy.c | 288 +++++++++++++++++++++++++++++++++++++++++++++++++-------------- server.c | 111 +++++++++++++++++------- server.h | 6 +- types.h | 22 +++++ 7 files changed, 357 insertions(+), 107 deletions(-) diff --git a/helper.c b/helper.c index 1ba11b0..ff4d251 100644 --- a/helper.c +++ b/helper.c @@ -19,6 +19,8 @@ void bail(char *args, ...) vprintf(args, argList); va_end(argList); printf("\n"); + fflush(stdout); + fflush(stderr); exit(1); } @@ -79,7 +81,7 @@ void helper_nonblock(const int fd) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); } -void helper_printava(struct AttributeValueAssertion* a,const char* rel) +static void helper_printavaInt(struct AttributeValueAssertion* a,const char* rel) { putchar('['); printf("%.*s", (int)a->desc.l,a->desc.s); @@ -90,6 +92,12 @@ void helper_printava(struct AttributeValueAssertion* a,const char* rel) putchar(']'); } +void helper_printava(struct AttributeValueAssertion* a,const char* rel) +{ + helper_printavaInt(a, rel); + putchar('\n'); +} + void helper_printal(struct AttributeDescriptionList* a) { while (a) { @@ -102,14 +110,14 @@ void helper_printal(struct AttributeDescriptionList* a) putchar('\n'); } -void helper_printfilter(struct Filter* f) +static void helper_printfilterInt(struct Filter* f) { switch (f->type) { case AND: printf("&("); mergesub: - helper_printfilter(f->x); - printf(")\n"); + helper_printfilterInt(f->x); + printf(")"); break; case OR: printf("|("); @@ -119,7 +127,7 @@ mergesub: printf("!("); goto mergesub; case EQUAL: - helper_printava(&f->ava,"=="); + helper_printavaInt(&f->ava,"=="); break; case SUBSTRING: { struct Substring* s=f->substrings; @@ -149,16 +157,16 @@ mergesub: } break; case GREATEQUAL: - helper_printava(&f->ava,">="); + helper_printavaInt(&f->ava,">="); break; case LESSEQUAL: - helper_printava(&f->ava,"<="); + helper_printavaInt(&f->ava,"<="); break; case PRESENT: - helper_printava(&f->ava,"\\exist"); + helper_printavaInt(&f->ava,"\\exist"); break; case APPROX: - helper_printava(&f->ava,"\\approx"); + helper_printavaInt(&f->ava,"\\approx"); break; case EXTENSIBLE: printf("[extensible]"); @@ -166,9 +174,13 @@ mergesub: } if (f->next) { putchar(','); - helper_printfilter(f->next); + helper_printfilterInt(f->next); } +} + +void helper_printfilter(struct Filter* f) +{ + helper_printfilterInt(f); putchar('\n'); - fflush(stdout); } diff --git a/ldadp.c b/ldadp.c index 0d2f818..90647df 100644 --- a/ldadp.c +++ b/ldadp.c @@ -10,6 +10,7 @@ #include #include #include +#include static void listen_callback(void *data, int haveIn, int haveOut, int doCleanup); static void loadConfig(char *file); @@ -20,6 +21,7 @@ int main(int argc, char **argv) printf("Nö\n"); exit(1); } + signal(SIGPIPE, SIG_IGN); loadConfig(argv[1]); proxy_init(); char listen_addr[4] = {0, 0, 0, 0}; diff --git a/ldap.h b/ldap.h index 2270f56..aef89e0 100644 --- a/ldap.h +++ b/ldap.h @@ -185,6 +185,7 @@ size_t scan_ldapmodifydnrequest(const char* src,const char* max,struct ModifyDNR size_t fmt_ldapstring(char* dest,const struct string* s); size_t fmt_ldapmessage(char* dest,long messageid,long op,size_t len); size_t fmt_ldapbindrequest(char* dest,long version,const char* name,const char* simple); +size_t fmt_ldapbindrequeststring(char* dest,long version,const struct string* name,const struct string* simple); size_t fmt_ldapsearchfilter(char* dest,const struct Filter* f); size_t fmt_ldapsearchrequest(char* dest,const struct SearchRequest* s); size_t fmt_ldapsearchresultentry(char* dest,const struct SearchResultEntry* sre); diff --git a/proxy.c b/proxy.c index ca4dfc1..c396cc7 100644 --- a/proxy.c +++ b/proxy.c @@ -11,7 +11,6 @@ #include #define MAXPENDING 200 -#define BINDLEN 500 typedef struct { @@ -41,11 +40,10 @@ static pending_t _pendingRequest[MAXPENDING]; static int _pendingCount = 0; static struct string s_shadowAccount, s_posixAccount, s_user, s_uid, s_sAMAccountName, s_objectSid; -static struct string s_objectclass, s_homeDirectory, s_gidNumber, s_gecos, s_dn, s_posixGroup; +static struct string s_objectClass, s_objectclass, s_homeDirectory, s_gidNumber, s_gecos, s_cn, s_dn, s_posixGroup; static struct string s_loginShell, s_uidNumber, s_mail, s_objectCategory, s_memberOf, s_distinguishedName; - -static char osHack[200], osHackNum[12]; -static int osHackLen = 0; +static struct string s_1001; +static struct string str_ADUSER; // @@ -64,6 +62,7 @@ void proxy_init() static int done = 0; if (done) return; done = 1; + // Set up some strings we frequently use SETSTR(shadowAccount); SETSTR(posixAccount); SETSTR(posixGroup); @@ -71,6 +70,7 @@ void proxy_init() SETSTR(uid); SETSTR(sAMAccountName); SETSTR(objectSid); + SETSTR(objectClass); SETSTR(objectclass); SETSTR(homeDirectory); SETSTR(gidNumber); @@ -81,7 +81,12 @@ void proxy_init() SETSTR(objectCategory); SETSTR(memberOf); SETSTR(distinguishedName); + SETSTR(cn); SETSTR(dn); + SETSTR(1001); + // TODO: configurable + str_ADUSER.s = "ad_user"; + str_ADUSER.l = strlen("ad_user"); } #undef SETSTR @@ -98,6 +103,8 @@ int proxy_fromClient(epoll_client_t *client, const size_t maxLen) return proxy_clientBindRequest(client, messageId, res, maxLen); case SearchRequest: return proxy_clientSearchRequest(client, messageId, res, maxLen); + case UnbindRequest: + return 0; } return 0; } @@ -186,40 +193,95 @@ static inline int iequals(struct string *a, struct string *b) //#define PREF(...) do { pref(spaces, prefix); printf(__VA_ARGS__); } while (0) static BOOL request_isUserFilter(struct Filter *filter); -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 BOOL request_getGroupFilter(struct Filter *filter, struct string *wantedGroupName, uint32_t *wantedGroupId); +static void request_replaceFilter(server_t *server, struct Filter *filter); +static void request_replaceAdl(server_t *server, struct AttributeDescriptionList **adl, attr_t *attr); +static void request_replaceAttribute(server_t *server, struct string *attribute, struct string *value); static BOOL request_isUserFilter(struct Filter *filter) { for (; filter != NULL; filter = filter->next) { - if (filter->x != NULL && request_isUserFilter(filter->x)) return TRUE; - if (filter->type != EQUAL) continue; - if (iequals(&filter->ava.desc, &s_objectclass) && - (equals(&filter->ava.value, &s_posixAccount) || equals(&filter->ava.value, &s_shadowAccount))) { - return TRUE; - } else if (equals(&filter->ava.desc, &s_uid) || equals(&filter->ava.desc, &s_uidNumber)) { - return TRUE; + switch (filter->type) { + case NOT: + case AND: + case OR: + if (filter->x != NULL && request_isUserFilter(filter->x)) return TRUE; + break; + case EQUAL: + case GREATEQUAL: + case LESSEQUAL: + case APPROX: + if (iequals(&filter->ava.desc, &s_objectclass) && + (equals(&filter->ava.value, &s_posixAccount) || equals(&filter->ava.value, &s_shadowAccount))) { + return TRUE; + } + if (equals(&filter->ava.desc, &s_uid) || equals(&filter->ava.desc, &s_uidNumber)) { + return TRUE; + } + break; + default: break; } } return FALSE; } -static void request_replaceFilter(struct Filter *filter) +/** + * This is REALLY cheap. It doesn't really look at the logic operators in the filter as we assume that pam_ldap + * or nss_ldap etc. won't do anything fancy like "!(objectClass=groupAccount)", just simple AND and OR combined + * with EQUAL. + */ +static BOOL request_getGroupFilter(struct Filter *filter, struct string *wantedGroupName, uint32_t *wantedGroupId) { + BOOL retval = FALSE; 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); + switch (filter->type) { + case AND: + case OR: + if (filter->x != NULL && request_getGroupFilter(filter->x, wantedGroupName, wantedGroupId)) retval = TRUE; + break; + case EQUAL: + case APPROX: + if (iequals(&filter->ava.desc, &s_objectclass) && equals(&filter->ava.value, &s_posixGroup)) { + retval = TRUE; + } else if (equals(&filter->ava.desc, &s_gidNumber)) { + *wantedGroupId = 0; // Should we check for a valid number? I don't see how it would hurt not doing so... + for (size_t i = 0; i < filter->ava.value.l; ++i) *wantedGroupId = (*wantedGroupId * 10) + (filter->ava.value.s[i] - '0'); + } else if (equals(&filter->ava.desc, &s_cn)) { + *wantedGroupName = filter->ava.value; + } + break; + default: break; + } + } + return retval; +} + +static void request_replaceFilter(server_t *server, struct Filter *filter) +{ + for (; filter != NULL; filter = filter->next) { + switch (filter->type) { + case NOT: + case AND: + case OR: + request_replaceFilter(server, filter->x); + break; + case PRESENT: + case SUBSTRING: + request_replaceAttribute(server, &filter->ava.desc, NULL); + break; + case EQUAL: + case GREATEQUAL: + case LESSEQUAL: + case APPROX: + request_replaceAttribute(server, &filter->ava.desc, &filter->ava.value); + break; + default: break; } - //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) +static void request_replaceAdl(server_t *server, struct AttributeDescriptionList **adl, attr_t *attr) { while (*adl != NULL) { struct AttributeDescriptionList *next = NULL; @@ -228,14 +290,14 @@ static void request_replaceAdl(struct AttributeDescriptionList **adl, attr_t *at elifSETATTR(gidNumber); elifSETATTR(gecos); elifSETATTR(loginShell); - else request_replaceAttribute(&(*adl)->a, NULL); + else request_replaceAttribute(server, &(*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) +static void request_replaceAttribute(server_t *server, struct string *attribute, struct string *value) { if (equals(attribute, &s_uid)) { *attribute = s_sAMAccountName; @@ -247,23 +309,23 @@ static void request_replaceAttribute(struct string *attribute, struct string *va *attribute = s_objectSid; if (value == NULL) return; uint32_t tmp = 0; - for (size_t i = 0; i < value->l; ++i) tmp = tmp * 10 + (value->s[i] - '0'); - memcpy(osHack + osHackLen, &tmp, 4); - value->s = osHack; - value->l = osHackLen + 4; + for (size_t i = 0; i < value->l; ++i) tmp = (tmp * 10) + (value->s[i] - '0'); + memcpy(server->sid + (SIDLEN - 4), &tmp, 4); + value->s = server->sid; + value->l = SIDLEN; } } // --------- 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 void response_replacePal(server_t *server, struct PartialAttributeList **pal, attr_t *attr); +static void response_replaceAdl(server_t *server, struct string *type, struct AttributeDescriptionList **adl, attr_t *attr); +static void response_replaceAttribute(server_t *server, 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) +static void response_replacePal(server_t *server, struct PartialAttributeList **pal, attr_t *attr) { struct string *username = NULL; struct PartialAttributeList *last = NULL; @@ -284,7 +346,7 @@ static void response_replacePal(struct PartialAttributeList **pal, attr_t *attr) *pal = next; continue; } - response_replaceAdl(&(*pal)->type, &(*pal)->values, attr); + response_replaceAdl(server, &(*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; @@ -316,7 +378,7 @@ static void response_replacePal(struct PartialAttributeList **pal, attr_t *attr) #undef ADDATTR #undef elifDELATTR -static void response_replaceAdl(struct string *type, struct AttributeDescriptionList **adl, attr_t *attr) +static void response_replaceAdl(server_t *server, struct string *type, struct AttributeDescriptionList **adl, attr_t *attr) { while (*adl != NULL) { struct AttributeDescriptionList *next = NULL; @@ -326,26 +388,27 @@ static void response_replaceAdl(struct string *type, struct AttributeDescription *adl = next; continue; } - response_replaceAttribute(type, &(*adl)->a); + response_replaceAttribute(server, 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) +static void response_replaceAttribute(server_t *server, struct string *attribute, struct string *value) { if (equals(attribute, &s_sAMAccountName)) { *attribute = s_uid; } else if (equals(attribute, &s_objectSid)) { *attribute = s_uidNumber; if (value == NULL) return; - if (osHackLen == 0 && value->l > 4) { - osHackLen = value->l - 4; - memcpy(osHack, value->s, osHackLen); + if (value->l != SIDLEN) return; + // It we don't have the servers SID base yet and we see a valid one, store it + if (server->sid[0] == 0 && value->s[0] == 1 && value->s[1] == 5 && value->s[7] == 5) { + memcpy(server->sid, value->s, SIDLEN - 4); } int tmp; - memcpy(&tmp, value->s + value->l - 4, 4); - value->l = snprintf(osHackNum, 10, "%u", tmp); - value->s = osHackNum; + memcpy(&tmp, value->s + (value->l - 4), 4); + // We know value->s is in our receive buffer and there are 28 bytes available, so we reuse the buffer + value->l = snprintf((char*)value->s, value->l, "%u", tmp); } } @@ -371,15 +434,17 @@ static int proxy_clientSearchRequest(epoll_client_t *client, const unsigned long struct SearchRequest req; const size_t res = scan_ldapsearchrequest(client->readBuffer + offset, client->readBuffer + maxLen, &req); if (res == 0) return -1; - const int server = server_getFromBase(&req.baseObject); - if (server == -1) { + server_t *server = server_getFromBase(&req.baseObject); + if (server == NULL) { printf("scan_ldapsearchrequest: baseObj '%.*s' unknown.\n", (int)req.baseObject.l, req.baseObject.s); return -1; } printf("scan_ldapsearchrequest: baseObj: %.*s, scope: %d, derefAliases: %d\n", (int)req.baseObject.l, req.baseObject.s, req.scope, req.derefAliases); - // + // Try to figure out if this is a lookup for a user/multiple users, or something else (eg. group) if (!request_isUserFilter(req.filter)) { // Handle locally + printf("Handling local:\n"); + helper_printfilter(req.filter); const int ret = proxy_localSearchRequest(client, messageId, &req); free_ldapsearchrequest(&req); return ret; @@ -394,14 +459,14 @@ static int proxy_clientSearchRequest(epoll_client_t *client, const unsigned long if (req.attributes == NULL) { memset(&pending->attr, -1, sizeof(pending->attr)); } else { - request_replaceAdl(&req.attributes, &pending->attr); + request_replaceAdl(server, &req.attributes, &pending->attr); } - request_replaceFilter(req.filter); + request_replaceFilter(server, 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'); + //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) { @@ -435,7 +500,7 @@ static int proxy_serverSearchResult(epoll_server_t *server, const unsigned long struct SearchResultEntry sre; const size_t res = scan_ldapsearchresultentry(server->readBuffer + offset, server->readBuffer + maxLen, &sre); if (res == 0) return -1; - response_replacePal(&sre.attributes, &pending->attr); + response_replacePal(server->serverData, &sre.attributes, &pending->attr); bodyLen = fmt_ldapsearchresultentry(NULL, &sre); if (bodyLen == 0) { printf("Error formatting ldapsearchresultentry after transformation\n"); @@ -465,20 +530,46 @@ static int proxy_clientBindRequest(epoll_client_t *client, const unsigned long m { 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); - //for (int i = 0; i < password.l; ++i) printf("%c(%x)\n", password.s[i], (int)password.s[i]); char buffer[800]; char *bufoff = buffer + 100; size_t bodyLen; - if (strncmp(password.s, "\x08\x0a\x0d\x7fINCORRECT", 13) != 0) { // FIXME: Drexhack! - printf("Password OK\n"); - bodyLen = fmt_ldapbindresponse(bufoff, 0, "", "main screen turn on", ""); + const size_t res = scan_ldapbindrequest(client->readBuffer + offset, client->readBuffer + maxLen, &version, &name, &method); + if (res == 0) return -1; // Parsing request failed + if (method != 0) { + // Other than simple bind - currently not supported + printf("Unsupported bind method: %lu\n", method); + bodyLen = fmt_ldapbindresponse(bufoff, authMethodNotSupported, "", "SIMPLE only", ""); } else { - printf("Password WRONG\n"); - bodyLen = fmt_ldapbindresponse(bufoff, 49, "", "nix da", ""); + // Simple bind :-) + 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'\n", (int)(res + res2), version, method, (int)name.l, name.s); + if (name.l == 0 && password.l == 0) { + // Anonymous bind used for "normal" lookups + printf("Anonymous bind ok\n"); + bodyLen = fmt_ldapbindresponse(bufoff, success, "", "main screen turn on", ""); + } else { + server_t *server = server_getFromBase(&name); + if (server == NULL || strncmp(password.s, "\x08\x0a\x0d\x7fINCORRECT", 13) == 0) { + // The INCORRECT part is some weird thing I saw pam_ldap do - save the round trip to AD and deny + printf("Password INCORRECT or binddn unknown\n"); + bodyLen = fmt_ldapbindresponse(bufoff, invalidCredentials, "", "invalid credentials", ""); + } else { + // Seems to be an actual bind - forward to AD - TODO: SASL (DIGEST-MD5?) + pending_t *pending = proxy_getFreePendingSlot(client); + const unsigned long smid = server_tryUserBind(server, &name, &password); + if (pending == NULL || smid == 0) { + // Busy + if (pending != NULL) pending->client = NULL; + printf("Too many pending requests, or cannot connect to AD\n"); + bodyLen = fmt_ldapbindresponse(bufoff, busy, "", "can't handle it", ""); + } else { + // Request queued, client needs to wait + pending->clientMessageId = messageId; + pending->serverMessageId = smid; + return 0; + } + } + } } const size_t headerLen = fmt_ldapmessage(NULL, messageId, BindResponse, bodyLen); if (headerLen > 100) return -1; // Too long - don't care @@ -494,13 +585,84 @@ static int proxy_serverBindResponse(epoll_server_t *server, const unsigned long 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; + if (messageId <= 1) return 0; + // Was a forwarded auth + pending_t *pending = proxy_getPendingFromServer(messageId); + if (pending == NULL) return 0; + const size_t headerLen = fmt_ldapmessage(NULL, pending->clientMessageId, BindResponse, res); + char buffer[headerLen]; + fmt_ldapmessage(buffer, pending->clientMessageId, BindResponse, res); + client_send(pending->client, buffer, headerLen, TRUE); + client_send(pending->client, server->readBuffer + offset, maxLen, FALSE); + pending->client = NULL; + return -1; } // ---- Local handling ---- static int proxy_localSearchRequest(epoll_client_t *client, const unsigned long messageId, const struct SearchRequest *req) { + struct string name; + uint32_t number = 2; + name.l = 0; + if (request_getGroupFilter(req->filter, &name, &number)) { + // Request for group (by number or by name)? + if (number == 2 && name.l == 0) { + // posixGroup requested, but neither gidNumber nor cn requested, so it must be "list all" + number = 1001; + name.l = 1; + } else if (name.l != 0 && strncmp(name.s, "ad_user", name.l) != 0) { + // We know only one group... + name.l = 0; + } + if (number == 1001 || name.l != 0) { + // At least one of them was set + // TODO: Helper for setting this stuff up + struct SearchResultEntry sre; + struct PartialAttributeList gidNumber, cn, objectClass; + struct AttributeDescriptionList gidNumberVal, cnVal, objectClassVal; + //sre.objectName.l = snprintf(dnbuffer, BASELEN, "cn=ad_user,%.*s", (int)req->baseObject.l, req->baseObject.s); + //sre.objectName.s = dnbuffer; + //if (sre.objectName.l > BASELEN) sre.objectName.l = BASELEN; + sre.objectName.l = 0; + memset(&sre, 0, sizeof(sre)); + memset(&gidNumber, 0, sizeof(gidNumber)); + memset(&cn, 0, sizeof(cn)); + memset(&cnVal, 0, sizeof(cnVal)); + cn.next = sre.attributes; + sre.attributes = &cn; + cn.type = s_cn; + cn.values = &cnVal; + cnVal.a = str_ADUSER; + memset(&gidNumber, 0, sizeof(gidNumber)); + memset(&gidNumberVal, 0, sizeof(gidNumberVal)); + gidNumber.next = sre.attributes; + sre.attributes = &gidNumber; + gidNumber.type = s_gidNumber; + gidNumber.values = &gidNumberVal; + gidNumberVal.a = s_1001; + memset(&objectClass, 0, sizeof(objectClass)); + memset(&objectClassVal, 0, sizeof(objectClassVal)); + objectClass.next = sre.attributes; + sre.attributes = &objectClass; + objectClass.type = s_objectClass; + objectClass.values = &objectClassVal; + objectClassVal.a = s_posixGroup; + // Build reply + const size_t bodyLen = fmt_ldapsearchresultentry(NULL, &sre); + const size_t headerLen = fmt_ldapmessage(NULL, messageId, SearchResultEntry, bodyLen); + char buffer[headerLen + bodyLen]; + fmt_ldapmessage(buffer, messageId, SearchResultEntry, bodyLen); + fmt_ldapsearchresultentry(buffer + headerLen, &sre); + client_send(client, buffer, headerLen + bodyLen, TRUE); + } + const size_t doneLen = fmt_ldapsearchresultdone(NULL, success, "", "", ""); + const size_t doneHeaderLen = fmt_ldapmessage(NULL, messageId, SearchResultDone, doneLen); + char buffer[doneLen + doneHeaderLen]; + fmt_ldapsearchresultdone(buffer + doneHeaderLen, success, "", "", ""); + fmt_ldapmessage(buffer, messageId, SearchResultDone, doneLen); + return client_send(client, buffer, doneHeaderLen + doneLen, FALSE); + } return -1; } diff --git a/server.c b/server.c index 91f5703..2c3aebe 100644 --- a/server.c +++ b/server.c @@ -11,35 +11,19 @@ #include #include -#define ADDRLEN 40 -#define BINDLEN 200 -#define PWLEN 40 -#define BASELEN 100 -#define ALIASLEN 40 - #define AD_PORT 3268 #define MSGID_BIND 1 -typedef struct { - size_t baseLen; - char ip[4]; - time_t lastLookup; - char addr[ADDRLEN]; - char bind[BINDLEN]; - char password[PWLEN]; - char base[BASELEN]; - epoll_server_t con; -} server_t; - #define MAX_SERVERS 10 static server_t *servers = NULL; static int serverCount = 0; static void server_init(); static server_t *server_create(const char *server); +static void server_free(epoll_server_t *server); static void server_callback(void *data, int haveIn, int haveOut, int doCleanup); static void server_flush(epoll_server_t * const server); -static BOOL server_ensureConnected(const int index); +static BOOL server_ensureConnected(server_t *server); static void server_ensureSendBuffer(epoll_server_t * const s, const size_t len); // Generate a message ID for request to AD @@ -81,13 +65,13 @@ void server_initServers() printf("%d servers configured.\n", serverCount); for (i = 0; i < serverCount; ++i) { printf("%s:\n Bind: %s\n Base: %s\n", servers[i].addr, servers[i].bind, servers[i].base); - server_ensureConnected(i); + server_ensureConnected(&servers[i]); } } // What the proxy calls -int server_getFromBase(struct string *in) +server_t *server_getFromBase(struct string *in) { int i; char buffer[TMPLEN]; @@ -97,13 +81,13 @@ int server_getFromBase(struct string *in) for (i = 0; i < serverCount; ++i) { if (searchLen < servers[i].baseLen) continue; if (strcmp(servers[i].base, buffer + (searchLen - servers[i].baseLen)) == 0) { - return i; + return &servers[i]; } } - return -1; + return NULL; } -uint32_t server_searchRequest(int server, struct SearchRequest *req) +uint32_t server_searchRequest(server_t *server, struct SearchRequest *req) { if (!server_ensureConnected(server)) return 0; const uint32_t msgid = msgId(); @@ -113,11 +97,68 @@ uint32_t server_searchRequest(int server, struct SearchRequest *req) char *bufoff = buffer + 50; fmt_ldapsearchrequest(bufoff, req); fmt_ldapmessage(bufoff - headerLen, msgid, SearchRequest, bodyLen); - epoll_server_t * const s = &servers[server].con; + epoll_server_t * const s = &server->con; server_send(s, bufoff - headerLen, headerLen + bodyLen, FALSE); return msgid; } +uint32_t server_tryUserBind(server_t *server, struct string *binddn, struct string *password) +{ + epoll_server_t *con = calloc(1, sizeof(epoll_server_t)); + con->serverData = server; + con->fd = -1; + con->bound = FALSE; + con->dynamic = TRUE; + printf("Connecting to AD '%s' for %.*ss bind...\n", server->addr, (int)binddn->l, binddn->s); + con->sbPos = con->sbFill = 0; + int sock; + if (server->lastLookup + 300 < time(NULL)) { + sock = helper_connect4(server->addr, AD_PORT, server->ip); + if (sock == -1) { + printf("[ADB] Could not resolve/connect to AD server %s\n", server->addr); + server_free(con); + return 0; + } + } else { + sock = socket_tcp4b(); + if (sock == -1) { + printf("[ADB] Could not allocate socket for connection to AD\n"); + server_free(con); + return 0; + } + if (socket_connect4(sock, server->ip, AD_PORT) == -1) { + printf("[ADB] Could not connect to cached IP of %s\n", server->addr); + close(sock); + server_free(con); + return 0; + } + } + printf("[ADB] Connected, binding....\n"); + helper_nonblock(sock); + con->fd = sock; + con->callback = &server_callback; + if (ePoll_add(EPOLLIN | EPOLLOUT | EPOLLET, (epoll_item_t*)con) == -1) { + printf("[ADB] epoll_add failed for ad server %s\n", server->addr); + server_free(con); + return 0; + } + // Now bind - TODO: SASL (DIGEST-MD5?) + const uint32_t id = msgId(); + const size_t bodyLen = fmt_ldapbindrequeststring(NULL, 3, binddn, password); + const size_t headerLen = fmt_ldapmessage(NULL, id, BindRequest, bodyLen); + char buffer[bodyLen + 50]; + char *bufoff = buffer + 50; + if (headerLen >= 50) { + printf("[ADB] bind too long for %s\n", server->addr); + server_free(con); + return 0; + } + fmt_ldapbindrequeststring(bufoff, 3, binddn, password); + fmt_ldapmessage(bufoff - headerLen, id, BindRequest, bodyLen); + server_send(con, bufoff - headerLen, bodyLen + headerLen, FALSE); + return id; +} + // // Private stuff @@ -140,6 +181,7 @@ static server_t *server_create(const char *server) } snprintf(servers[serverCount].addr, ADDRLEN, "%s", server); servers[serverCount].con.fd = -1; + servers[serverCount].con.serverData = &servers[serverCount]; return &servers[serverCount++]; } @@ -149,6 +191,11 @@ static void server_free(epoll_server_t *server) if (server->fd != -1) close(server->fd); server->fd = -1; server->sbPos = server->sbFill = 0; + if (server->dynamic) { + printf("Freeing Bind-AD-Connection\n"); + free(server->sendBuffer); + free(server); + } } static void server_callback(void *data, int haveIn, int haveOut, int doCleanup) @@ -185,9 +232,12 @@ static void server_callback(void *data, int haveIn, int haveOut, int doCleanup) if (len > server->rbPos) break; // Body not complete printf("[AD] Received complete reply...\n"); if (proxy_fromServer(server, len) == -1) { + if (server->dynamic) { + server_free(server); + return; + } printf("Error parsing reply from AD.\n"); - server_free(server); - return; + // Let's try to go on with the next message.... } // Shift remaining buffer contents if (len == server->rbPos) { @@ -248,14 +298,13 @@ static void server_flush(epoll_server_t * const server) server->sbPos = server->sbFill = 0; } -static BOOL server_ensureConnected(const int index) +static BOOL server_ensureConnected(server_t *server) { - server_t * const server = &servers[index]; epoll_server_t * const con = &server->con; if (con->fd != -1 && con->lastActive + 120 > time(NULL)) return TRUE; if (con->fd != -1) close(con->fd); con->bound = FALSE; - printf("Connecting to AD %s...\n", server->addr); + printf("Connecting to AD '%s'...\n", server->addr); con->sbPos = con->sbFill = 0; int sock; if (server->lastLookup + 300 < time(NULL)) { @@ -286,9 +335,9 @@ static BOOL server_ensureConnected(const int index) con->fd = -1; return FALSE; } - // Now bind + // Now bind - TODO: SASL (DIGEST-MD5?) const size_t bodyLen = fmt_ldapbindrequest(NULL, 3, server->bind, server->password); - const size_t headerLen = fmt_ldapmessage(NULL, MSGID_BIND, BindResponse, bodyLen); + const size_t headerLen = fmt_ldapmessage(NULL, MSGID_BIND, BindRequest, bodyLen); char buffer[bodyLen + 50]; char *bufoff = buffer + 50; if (headerLen >= 50) { diff --git a/server.h b/server.h index 79a6f4e..5252048 100644 --- a/server.h +++ b/server.h @@ -16,9 +16,11 @@ void server_initServers(); int server_send(epoll_server_t *server, const char *buffer, size_t len, const BOOL cork); -int server_getFromBase(struct string *in); +server_t *server_getFromBase(struct string *in); -uint32_t server_searchRequest(int server, struct SearchRequest *req); +uint32_t server_searchRequest(server_t *server, struct SearchRequest *req); + +uint32_t server_tryUserBind(server_t *server, struct string *binddn, struct string *password); #endif diff --git a/types.h b/types.h index 417baa1..6959197 100644 --- a/types.h +++ b/types.h @@ -5,6 +5,12 @@ #include #include +#define ADDRLEN 40 +#define BINDLEN 250 +#define PWLEN 40 +#define BASELEN 250 +#define SIDLEN 28 + #define REQLEN 4000 #define MAXMSGLEN 100000 @@ -12,6 +18,8 @@ #define TRUE 1 #define FALSE 0 +typedef struct _server_t_ server_t; + typedef struct { void (*callback)(void *data, int haveIn, int haveOut, int doCleanup); int fd; @@ -45,8 +53,22 @@ typedef struct { size_t rbPos; char readBuffer[MAXMSGLEN]; BOOL bound; + BOOL dynamic; //unsigned long messageId; // ID of message currently being received time_t lastActive; + server_t *serverData; } epoll_server_t; +struct _server_t_ { + size_t baseLen; + char ip[4]; + time_t lastLookup; + char addr[ADDRLEN]; + char bind[BINDLEN]; + char password[PWLEN]; + char base[BASELEN]; + char sid[SIDLEN]; + epoll_server_t con; +}; + #endif -- cgit v1.2.3-55-g7522