#include "proxy.h"
#include "client.h"
#include "server.h"
#include "helper.h"
#include "tmpbuffer.h"
#include "ldap.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#define MAXPENDING 200
typedef struct
{
BOOL homeDirectory;
BOOL homeMount;
BOOL gidNumber;
BOOL gecos;
BOOL loginShell;
BOOL uid;
BOOL uidNumber;
BOOL cn;
// Internal
BOOL hasUser;
} 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_posixAccount, s_user, s_uid, s_sAMAccountName, s_objectSid;
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 struct string s_1001, s_homeMount, s_member, s_memberUid;
static struct string str_ADUSER;
//
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);
static int proxy_localSearchRequest(epoll_client_t *client, const unsigned long messageId, const struct SearchRequest *req);
//
#define SETSTR(x) s_ ## x.s = #x; s_ ## x.l = strlen( #x )
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);
SETSTR(user);
SETSTR(uid);
SETSTR(sAMAccountName);
SETSTR(objectSid);
SETSTR(objectClass);
SETSTR(objectclass);
SETSTR(homeDirectory);
SETSTR(homeMount);
SETSTR(gidNumber);
SETSTR(gecos);
SETSTR(loginShell);
SETSTR(uidNumber);
SETSTR(mail);
SETSTR(objectCategory);
SETSTR(memberOf);
SETSTR(distinguishedName);
SETSTR(cn);
SETSTR(dn);
SETSTR(member);
SETSTR(memberUid);
SETSTR(1001);
// TODO: configurable
str_ADUSER.s = "ad_user";
str_ADUSER.l = strlen("ad_user");
}
#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);
case UnbindRequest:
return 0;
}
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 BOOL request_isUserFilter(struct Filter *filter);
static BOOL request_getGroupFilter(struct Filter *filter, struct string *wantedGroupName, uint32_t *wantedGroupId, BOOL *wantsMember);
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, attr_t *attr);
static BOOL request_isUserFilter(struct Filter *filter)
{
for (; filter != NULL; filter = filter->next) {
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;
}
/**
* 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.
* You could actually get real group memberships using the memberOf attributes of the user, and do additional
* queries for these groups, but for that to make any sense you'd also have to implement useful permission
* handling.... So as all we really want is authentication and optionally mounting a home directory, we pretend
* there is just one group with id 1001 and name ad_user.
* This function will try to figure out if the given filter is a lookup for a group name or group id.
*/
static BOOL request_getGroupFilter(struct Filter *filter, struct string *wantedGroupName, uint32_t *wantedGroupId, BOOL *wantsMember)
{
BOOL retval = FALSE;
for (; filter != NULL; filter = filter->next) {
switch (filter->type) {
case AND:
case OR:
if (filter->x != NULL && request_getGroupFilter(filter->x, wantedGroupName, wantedGroupId, wantsMember)) 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;
} else if (equals(&filter->ava.desc, &s_member) || equals(&filter->ava.desc, &s_memberUid)) {
*wantsMember = TRUE;
}
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, NULL);
break;
case EQUAL:
case GREATEQUAL:
case LESSEQUAL:
case APPROX:
request_replaceAttribute(server, &filter->ava.desc, &filter->ava.value, NULL);
break;
default: break;
}
}
}
#define elifSETATTR(x) else if (equals(&(*adl)->a, &s_ ## x)) attr->x = TRUE, next = (*adl)->next, free(*adl), *adl = next
static void request_replaceAdl(server_t *server, struct AttributeDescriptionList **adl, attr_t *attr)
{
while (*adl != NULL) {
struct AttributeDescriptionList *next = NULL;
if (attr == NULL) { }
elifSETATTR(homeDirectory);
elifSETATTR(gidNumber);
elifSETATTR(gecos);
elifSETATTR(loginShell);
else request_replaceAttribute(server, &(*adl)->a, NULL, attr);
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
}
if (!attr->hasUser) {
if (attr->homeDirectory || attr->gecos || attr->homeMount) {
struct AttributeDescriptionList *user = calloc(1, sizeof(struct AttributeDescriptionList));
user->a = s_sAMAccountName;
user->next = *adl;
*adl = user;
}
}
}
#undef elifSETATTR
static void request_replaceAttribute(server_t *server, struct string *attribute, struct string *value, attr_t *attr)
{
if (equals(attribute, &s_uid)) {
*attribute = s_sAMAccountName;
if (attr) attr->hasUser = TRUE;
} else if (equals(attribute, &s_homeMount)) {
*attribute = s_homeDirectory;
if (attr != NULL) attr->homeMount = TRUE;
} else if (iequals(attribute, &s_objectclass)) {
if (value == NULL) return;
if (equals(value, &s_shadowAccount)) *value = s_user;
else if (equals(value, &s_posixAccount)) *value = s_user;
} else if (equals(attribute, &s_uidNumber)) {
*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(server->sid + (SIDLEN - 4), &tmp, 4);
value->s = server->sid;
value->l = SIDLEN;
}
}
// --------- AD to client replacements
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 BOOL response_filterHomeDir(struct PartialAttributeList *pal);
static struct PartialAttributeList* response_addPal(struct PartialAttributeList *pal, struct string *attribute, const char *format, ...);
#define ADDATTR(x,...) do { if (attr->x) *pal = response_addPal(*pal, &s_ ## x, __VA_ARGS__); } while (0)
#define elifDELATTR(x) else if (equals(&(*pal)->type, &s_ ## x)) next = (*pal)->next, del = TRUE, attr->x = TRUE
#define elifDEL(x) else if (equals(&(*pal)->type, &s_ ## x)) next = (*pal)->next, del = TRUE
static void response_replacePal(server_t *server, struct PartialAttributeList **pal, attr_t *attr)
{
struct string *username = NULL;
struct AttributeDescriptionList *lastObjectClass = NULL;
struct PartialAttributeList *next = NULL;
while (*pal != NULL) {
BOOL del = FALSE;
if (0) { } // Remove fields we don't want from AD
elifDELATTR(gidNumber);
elifDELATTR(gecos);
elifDELATTR(loginShell);
elifDELATTR(uidNumber);
elifDEL(mail);
elifDELATTR(cn);
elifDEL(memberOf);
else if (equals(&(*pal)->type, &s_homeDirectory)) {
// homeDirectory is set in AD - it can either be a local path (in which case it's useless)
// or a UNC path, which we can easily mount via mount.cifs
if (!response_filterHomeDir(*pal)) {
del = TRUE;
attr->homeMount = TRUE;
next = (*pal)->next;
} else {
attr->homeMount = FALSE;
}
}
// Entry should be removed, free structs
if (del) {
free_ldapadl((*pal)->values);
free(*pal);
*pal = next;
continue;
}
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;
}
// Map objectClass user back to posixAccount and shadowAccount
if (lastObjectClass == NULL && iequals(&(*pal)->type, &s_objectclass)) {
BOOL hasUser = FALSE;
for (struct AttributeDescriptionList *adl = (*pal)->values; adl != NULL; adl = adl->next) {
if (!hasUser && iequals(&adl->a, &s_user)) hasUser = TRUE;
if (hasUser && adl->next == NULL) lastObjectClass = adl;
}
}
pal = &(*pal)->next;
}
if (username != NULL) {
char *user = tmpbuffer_get();
snprintf(user, TMPLEN, "%.*s", (int)username->l, username->s);
ADDATTR(homeDirectory, "/home/%s", user);
ADDATTR(gecos, "%s,,,", user);
ADDATTR(cn, "%s", user);
if (attr->homeMount && server->homeTemplate[0] != '\0') {
ADDATTR(homeMount, server->homeTemplate, user, user, user, user, user, user);
}
}
if (lastObjectClass != NULL) {
ADDATTR(loginShell, "/bin/bash");
ADDATTR(gidNumber, "1001");
// TODO: Nicer
lastObjectClass->next = calloc(1, sizeof(struct AttributeDescriptionList));
lastObjectClass->next->a = s_posixAccount;
lastObjectClass = lastObjectClass->next;
lastObjectClass->next = calloc(1, sizeof(struct AttributeDescriptionList));
lastObjectClass->next->a = s_shadowAccount;
}
}
#undef ADDATTR
#undef elifDELATTR
static void response_replaceAdl(server_t *server, 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(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(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 (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);
// 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);
}
}
static BOOL response_filterHomeDir(struct PartialAttributeList *pal)
{
for (struct AttributeDescriptionList *adl = pal->values; adl != NULL; adl = pal->values /* sic */) {
if (adl->a.l > 2 && adl->a.s[0] == '\\' && adl->a.s[1] == '\\') {
for (size_t i = 0; i < adl->a.l; ++i) if (adl->a.s[i] == '\\') *((char*)adl->a.s + i) = '/';
free_ldapadl(adl->next);
adl->next = NULL;
pal->type = s_homeMount;
return TRUE;
}
pal->values = adl->next;
free(adl);
}
return FALSE;
}
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);
next->next = pal;
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;
const size_t res = scan_ldapsearchrequest(client->readBuffer + offset, client->readBuffer + maxLen, &req);
if (res == 0) return -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;
}
//
pending_t *pending = proxy_getFreePendingSlot(client);
if (pending == NULL) {
printf("No more slots for pending requests\n");
free_ldapsearchrequest(&req);
return -1;
}
if (req.attributes == NULL) {
memset(&pending->attr, -1, sizeof(pending->attr));
} else {
request_replaceAdl(server, &req.attributes, &pending->attr);
}
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');
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;
response_replacePal(server->serverData, &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;
char buffer[800];
char *bufoff = buffer + 100;
size_t bodyLen;
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 {
// 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
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;
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;
BOOL wantsMember = FALSE;
name.l = 0;
if (request_getGroupFilter(req->filter, &name, &number, &wantsMember)) {
// Request for group (by number or by name)?
if (number == 2 && name.l == 0 && !wantsMember) {
// 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
printf("Sending fake group membership\n");
// 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);
} else {
printf("Sending empty posixGroup search result.\n");
}
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;
}