summaryrefslogtreecommitdiffstats
path: root/proxy.c
diff options
context:
space:
mode:
authorSimon Rettberg2014-03-18 19:32:40 +0100
committerSimon Rettberg2014-03-18 19:32:40 +0100
commitf0b46f7a343a79d1eeb29c0d45942df646e9cc35 (patch)
tree6969611ee51f412370c1c8b3171b6e9b72b4f551 /proxy.c
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
Diffstat (limited to 'proxy.c')
-rw-r--r--proxy.c288
1 files changed, 225 insertions, 63 deletions
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;
}