summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rettberg2014-03-18 19:32:40 +0100
committerSimon Rettberg2014-03-18 19:32:40 +0100
commitf0b46f7a343a79d1eeb29c0d45942df646e9cc35 (patch)
tree6969611ee51f412370c1c8b3171b6e9b72b4f551
parentFix double free in scan_ldapsearchfilter (diff)
downloadldadp-f0b46f7a343a79d1eeb29c0d45942df646e9cc35.tar.gz
ldadp-f0b46f7a343a79d1eeb29c0d45942df646e9cc35.tar.xz
ldadp-f0b46f7a343a79d1eeb29c0d45942df646e9cc35.zip
First working version with user and group support, login relaying
-rw-r--r--helper.c34
-rw-r--r--ldadp.c2
-rw-r--r--ldap.h1
-rw-r--r--proxy.c288
-rw-r--r--server.c111
-rw-r--r--server.h6
-rw-r--r--types.h22
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 <io.h>
#include <string.h>
#include <stdlib.h>
+#include <signal.h>
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 <stdarg.h>
#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 <errno.h>
#include <socket.h>
-#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 <stdint.h>
#include <time.h>
+#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