summaryrefslogblamecommitdiffstats
path: root/proxy.c
blob: ca4dfc109c24f97793952b927452927960f0ea05 (plain) (tree)









































                                             

                                                                                                   

                                                                                                          


                                       






                                                                                                                                                               

                                                                                                                            








                                                          

                             





















































































































                                                                                                                               
                                                        



                                                                                     














                                                                                                                                






















                                                                                                                          













                                                                                                                             








                                                                                           















                                                                                                                                         
                                                                




















                                                                          







                                                                                                                   







                                                                                    






                                                                                           






















                                                                                                           

                                                     
                                          


                                                            
                 



                                                              






















                                                                                                                                        

                                                                                                                  
                                                               
                           
                                                                                                                     

                          







                                                                                                                                                             



                                                               
                                             













































                                                                                                                                                              

































                                                                                                                                                                                                  
                                                                                                     

                                    







                                                                                            
















                                                                                                                                                                                                        






                                                                                                                           
#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
#define BINDLEN      500

typedef struct
{
	BOOL homeDirectory;
	BOOL gidNumber;
	BOOL gecos;
	BOOL loginShell;
	BOOL uid;
	BOOL uidNumber;
} attr_t;

typedef struct
{
	unsigned long clientMessageId;
	unsigned long serverMessageId;
	epoll_client_t *client;
	attr_t attr;
} pending_t;

typedef struct
{
	const char *search;
	struct string replace;
} replace_t;

static pending_t _pendingRequest[MAXPENDING];
static int _pendingCount = 0;

static struct string s_shadowAccount, s_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_loginShell, s_uidNumber, s_mail, s_objectCategory, s_memberOf, s_distinguishedName;

static char osHack[200], osHackNum[12];
static int osHackLen = 0;

//

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;
	SETSTR(shadowAccount);
	SETSTR(posixAccount);
	SETSTR(posixGroup);
	SETSTR(user);
	SETSTR(uid);
	SETSTR(sAMAccountName);
	SETSTR(objectSid);
	SETSTR(objectclass);
	SETSTR(homeDirectory);
	SETSTR(gidNumber);
	SETSTR(gecos);
	SETSTR(loginShell);
	SETSTR(uidNumber);
	SETSTR(mail);
	SETSTR(objectCategory);
	SETSTR(memberOf);
	SETSTR(distinguishedName);
	SETSTR(dn);
}
#undef SETSTR

int proxy_fromClient(epoll_client_t *client, const size_t maxLen)
{
	unsigned long messageId, op;
	size_t len;
	const size_t res = scan_ldapmessage(client->readBuffer, client->readBuffer + maxLen, &messageId, &op, &len);
	if (res == 0) return -1;
	printf("[C] scan_ldapmessage: Consumed %d, remaining length %d, id %lu, op %lu\n", (int)res, (int)len, messageId, op);
	// TODO: Caching
	switch (op) {
	case BindRequest:
		return proxy_clientBindRequest(client, messageId, res, maxLen);
	case SearchRequest:
		return proxy_clientSearchRequest(client, messageId, res, maxLen);
	}
	return 0;
}

void proxy_removeClient(const epoll_client_t *client)
{
	int i, lastValid = -1;
	for (i = 0; i < _pendingCount; ++i) {
		if (_pendingRequest[i].client == client) _pendingRequest[i].client = NULL;
		else if (_pendingRequest[i].client != NULL) lastValid = i;
	}
	_pendingCount = lastValid + 1;
}

int proxy_fromServer(epoll_server_t *server, const size_t maxLen)
{
	unsigned long messageId, op;
	size_t len;
	const size_t res = scan_ldapmessage(server->readBuffer, server->readBuffer + maxLen, &messageId, &op, &len);
	if (res == 0) return -1;
	printf("[AD] scan_ldapmessage: Consumed %d, remaining length %d, id %lu, op %lu\n", (int)res, (int)len, messageId, op);
	switch (op) {
	case BindResponse:
		return proxy_serverBindResponse(server, messageId, res, maxLen);
	case SearchResultEntry:
	case SearchResultDone:
		return proxy_serverSearchResult(server, messageId, op, res, maxLen);
	}
	printf("Unsupported op: %lu\n", op);
	return -1;
}

//

static pending_t* proxy_getFreePendingSlot(epoll_client_t *client)
{
	for (int i = 0; i < _pendingCount; ++i) {
		if (_pendingRequest[i].client == NULL) {
			memset(&_pendingRequest[i], 0, sizeof(pending_t));
			_pendingRequest[i].client = client;
			return &_pendingRequest[i];
		}
	}
	if (_pendingCount < MAXPENDING) {
		memset(&_pendingRequest[_pendingCount], 0, sizeof(pending_t));
		_pendingRequest[_pendingCount].client = client;
		return &_pendingRequest[_pendingCount++];
	}
	return NULL;
}

static pending_t* proxy_getPendingFromServer(unsigned long serverMessageId)
{
	for (int i = 0; i < _pendingCount; ++i) {
		if (_pendingRequest[i].client == NULL) continue;
		if (_pendingRequest[i].serverMessageId == serverMessageId) return &_pendingRequest[i];
	}
	return NULL;
}

/*
static void pref(int spaces, char prefix)
{
	for (int i = 0; i < spaces; ++i) putchar(' ');
	putchar(prefix);
	putchar(' ');
}
*/

static inline int equals(struct string *a, struct string *b)
{
	if (a->l != b->l) return 0;
	return strncmp(a->s, b->s, a->l) == 0;
}

static inline int iequals(struct string *a, struct string *b)
{
	if (a->l != b->l) return 0;
	for (size_t i = 0; i < a->l; ++i) {
		if (tolower(a->s[i]) != b->s[i]) return 0;
	}
	return 1;
}

// ---- client to AD replacements

//#define PREF(...) do { pref(spaces, prefix); printf(__VA_ARGS__); } while (0)
static 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_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;
		}
	}
	return FALSE;
}

static void request_replaceFilter(struct Filter *filter)
{
	for (; filter != NULL; filter = filter->next) {
		if (filter->type == PRESENT || filter->type == SUBSTRING) {
			request_replaceAttribute(&filter->ava.desc, NULL);
		} else if (filter->type == EQUAL) {
			request_replaceAttribute(&filter->ava.desc, &filter->ava.value);
		}
		//request_replaceAdl(&filter->a, NULL, spaces + 2, prefix);
		request_replaceFilter(filter->x);
	}
}

#define elifSETATTR(x) else if (equals(&(*adl)->a, &s_ ## x)) attr->x = TRUE, next = (*adl)->next, free(*adl), *adl = next
static void request_replaceAdl(struct AttributeDescriptionList **adl, attr_t *attr)
{
	while (*adl != NULL) {
		struct AttributeDescriptionList *next = NULL;
		if (attr == NULL) { }
		elifSETATTR(homeDirectory);
		elifSETATTR(gidNumber);
		elifSETATTR(gecos);
		elifSETATTR(loginShell);
		else request_replaceAttribute(&(*adl)->a, NULL);
		if (*adl == NULL) break;
		if (next == NULL) adl = &(*adl)->next; // If next is not NULL, we removed an entry, so we don't need to shift
	}
}
#undef elifSETATTR

static void request_replaceAttribute(struct string *attribute, struct string *value)
{
	if (equals(attribute, &s_uid)) {
		*attribute = s_sAMAccountName;
	} else if (iequals(attribute, &s_objectclass)) {
		if (value == NULL) return;
		if (equals(value, &s_shadowAccount)) *value = s_user;
		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(osHack + osHackLen, &tmp, 4);
		value->s = osHack;
		value->l = osHackLen + 4;
	}
}

// --------- AD to client replacements

static void response_replacePal(struct PartialAttributeList **pal, attr_t *attr);
static void response_replaceAdl(struct string *type, struct AttributeDescriptionList **adl, attr_t *attr);
static void response_replaceAttribute(struct string *attribute, struct string *value);
static struct PartialAttributeList* response_addPal(struct PartialAttributeList *pal, struct string *attribute, const char *format, ...);

#define ADDATTR(x,...) do { if (attr->x) last = response_addPal(last, &s_ ## x, __VA_ARGS__); } while (0)
#define elifDELATTR(x) else if (equals(&(*pal)->type, &s_ ## x)) next = (*pal)->next
static void response_replacePal(struct PartialAttributeList **pal, attr_t *attr)
{
	struct string *username = NULL;
	struct PartialAttributeList *last = NULL;
	struct AttributeDescriptionList *lastObjectClass = NULL;
	while (*pal != NULL) {
		last = *pal;
		struct PartialAttributeList *next = NULL;
		if (0) { }
		elifDELATTR(homeDirectory);
		elifDELATTR(gidNumber);
		elifDELATTR(gecos);
		elifDELATTR(loginShell);
		elifDELATTR(uidNumber);
		elifDELATTR(mail);
		if (next != NULL) {
			free_ldapadl((*pal)->values);
			free(*pal);
			*pal = next;
			continue;
		}
		response_replaceAdl(&(*pal)->type, &(*pal)->values, attr);
		// Fetch user name so we can add our fake fields later
		if (username == NULL && equals(&(*pal)->type, &s_uid)) {
			username = &(*pal)->values->a;
		}
		// 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) {
		ADDATTR(homeDirectory, "/home/%.*s", (int)username->l, username->s);
		ADDATTR(gecos, "%.*s,,,", (int)username->l, username->s);
		ADDATTR(loginShell, "/bin/bash");
		ADDATTR(gidNumber, "1001");
	}
	if (lastObjectClass != NULL) {
		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(struct string *type, struct AttributeDescriptionList **adl, attr_t *attr)
{
	while (*adl != NULL) {
		struct AttributeDescriptionList *next = NULL;
		// Maybe delete entries here later
		if (next != NULL) {
			free(*adl);
			*adl = next;
			continue;
		}
		response_replaceAttribute(type, &(*adl)->a);
		adl = &(*adl)->next; // If next is not NULL, we removed an entry, so we don't need to shift
	}
}

static void response_replaceAttribute(struct string *attribute, struct string *value)
{
	if (equals(attribute, &s_sAMAccountName)) {
		*attribute = s_uid;
	} else if (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);
		}
		int tmp;
		memcpy(&tmp, value->s + value->l - 4, 4);
		value->l = snprintf(osHackNum, 10, "%u", tmp);
		value->s = osHackNum;
	}
}

static struct PartialAttributeList* response_addPal(struct PartialAttributeList *pal, struct string *attribute, const char *format, ...)
{
	struct PartialAttributeList *next = malloc(sizeof(struct PartialAttributeList));
	va_list args;
	va_start(args, format);
	pal->next = next;
	next->next = NULL;
	next->type = *attribute;
	next->values = malloc(sizeof(struct AttributeDescriptionList));
	tmpbuffer_formatva(&next->values->a, format, args);
	next->values->next = NULL;
	va_end(args);
	return next;
}

// -----

static int proxy_clientSearchRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen)
{
	struct SearchRequest req;
	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) {
		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);
	//
	if (!request_isUserFilter(req.filter)) {
		// Handle locally
		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(&req.attributes, &pending->attr);
	}
	request_replaceFilter(req.filter);
	helper_printfilter(req.filter);
	helper_printal(req.attributes);
	printf("Attrs: ");
	for (size_t i = 0; i < sizeof(attr_t); ++i) putchar(((char*)&pending->attr)[i] == 0 ? '0' : '1');
	putchar('\n');
	pending->clientMessageId = messageId;
	pending->serverMessageId = server_searchRequest(server, &req);
	if (pending->serverMessageId == 0) {
		// Failed to forward.. TODO: Fail client
		pending->client = NULL;
	}
	free_ldapsearchrequest(&req);
	//
	if (pending->client == NULL) return -1;
	return 0;
}

static int proxy_serverSearchResult(epoll_server_t *server, const unsigned long messageId, const unsigned long type, const size_t offset, const size_t maxLen)
{
	static char *bodyBuffer = NULL;
	if (bodyBuffer == NULL) bodyBuffer = malloc(MAXMSGLEN);
	pending_t *pending = proxy_getPendingFromServer(messageId);
	if (pending == NULL) {
		printf("No client matching server message id %lu\n", messageId);
		return 0;
	}
	printf("ServerID %lu -> ClientID %lu\n", messageId, pending->clientMessageId);
	const char *body;
	size_t bodyLen;
	if (type == SearchResultDone) {
		// Just forward with new header
		bodyLen = maxLen - offset;
		body = server->readBuffer + offset;
	} else {
		// Transform reply
		struct SearchResultEntry sre;
		const size_t res = scan_ldapsearchresultentry(server->readBuffer + offset, server->readBuffer + maxLen, &sre);
		if (res == 0) return -1;
		response_replacePal(&sre.attributes, &pending->attr);
		bodyLen = fmt_ldapsearchresultentry(NULL, &sre);
		if (bodyLen == 0) {
			printf("Error formatting ldapsearchresultentry after transformation\n");
			free_ldapsearchresultentry(&sre);
			return -1;
		}
		if (bodyLen > MAXMSGLEN) {
			printf("ldapsearchresultentry too large after transformation\n");
			free_ldapsearchresultentry(&sre);
			return -1;
		}
		fmt_ldapsearchresultentry(bodyBuffer, &sre);
		free_ldapsearchresultentry(&sre);
		body = bodyBuffer;
	}
	// Build header and fire away
	const size_t headerLen = fmt_ldapmessage(NULL, pending->clientMessageId, type, bodyLen);
	char buffer[headerLen];
	fmt_ldapmessage(buffer, pending->clientMessageId, type, bodyLen);
	client_send(pending->client, buffer, headerLen, TRUE);
	client_send(pending->client, body, bodyLen, FALSE);
	if (type == SearchResultDone) pending->client = NULL;
	return 0;
}

static int proxy_clientBindRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen)
{
	unsigned long version, method;
	struct string name, password;
	const size_t res = scan_ldapbindrequest(client->readBuffer + offset, client->readBuffer + maxLen, &version, &name, &method);
	if (res == 0) return -1; // Parsing request failed
	const size_t res2 = scan_ldapstring(client->readBuffer + offset + res, client->readBuffer + maxLen, &password);
	printf("scan_ldapbindrequest: Consumed %d, version %lu, method %lu, name '%.*s', bindpw: '%.*s'\n", (int)(res + res2), version, method, (int)name.l, name.s, (int)password.l, password.s);
	//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", "");
	} else {
		printf("Password WRONG\n");
		bodyLen = fmt_ldapbindresponse(bufoff, 49, "", "nix da", "");
	}
	const size_t headerLen = fmt_ldapmessage(NULL, messageId, BindResponse, bodyLen);
	if (headerLen > 100) return -1; // Too long - don't care
	fmt_ldapmessage(bufoff - headerLen, messageId, BindResponse, bodyLen);
	return client_send(client, bufoff - headerLen, bodyLen + headerLen, FALSE);
}

static int proxy_serverBindResponse(epoll_server_t *server, const unsigned long messageId, const size_t offset, const size_t maxLen)
{
	unsigned long result;
	struct string binddn, error, refer;
	const size_t res = scan_ldapbindresponse(server->readBuffer + offset, server->readBuffer + maxLen, &result, &binddn, &error, &refer);
	if (res == 0) return -1; // Parsing request failed
	printf("scan_ldapbindresponse: Consumed %d, result: %lu, binddn: %.*s, error: %.*s, referral: %.*s\n", (int)res, result, (int)binddn.l, binddn.s, (int)error.l, error.s, (int)refer.l, refer.s);
	if (result == success) server->bound = TRUE;
	return 0;
}

// ---- Local handling ----

static int proxy_localSearchRequest(epoll_client_t *client, const unsigned long messageId, const struct SearchRequest *req)
{
	return -1;
}