From 8b8ed36516e9a40df6ac9ac46ab355fee0e5b5f0 Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Wed, 14 Nov 2018 14:37:31 +0100 Subject: Support generating uidNumbers on proxy --- Makefile | 6 +++-- ldadp.c | 37 +++++++++++++++++++++++++--- proxy.c | 32 +++++++++++++++++++++--- server.c | 55 ++++++++++++++++++++++++++++++++++++++--- server.h | 8 ++++++ types.h | 9 +++++++ uidmap.c | 85 ++++++++++++++++++++++++++++++++-------------------------------- uidmap.h | 9 ++++--- 8 files changed, 183 insertions(+), 58 deletions(-) diff --git a/Makefile b/Makefile index 338f787..b1c322f 100644 --- a/Makefile +++ b/Makefile @@ -41,7 +41,7 @@ LIBS+=-g -lowfat -lssl -lcrypto %: %.c $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) ${LIBS} -ldadp: version.in.h tmpbuffer.o ini.o client.o server.o helper.o proxy.o epoll.o openssl.o lstring.o ldap.a asn1.a +ldadp: version.in.h tmpbuffer.o ini.o client.o server.o helper.o proxy.o epoll.o openssl.o lstring.o uidmap.o hashmap.o ldap.a asn1.a version.in.h: ./gen-version @@ -60,10 +60,12 @@ ini.o: ini.c ini.h client.o: client.c client.h types.h server.o: server.c server.h types.h helper.o: helper.c helper.h types.h -proxy.o: proxy.c proxy.h types.h helper.h +proxy.o: proxy.c proxy.h types.h helper.h lstring.h epoll.o: epoll.c epoll.h types.h openssl.o: openssl.c openssl.h types.h lstring.o: lstring.c asn1.h types.h +uidmap.o: uidmap.c uidmap.h hashmap.h lstring.h +hashmap.o: hashmap.c hashmap.h fmt_asn1int.o: fmt_asn1int.c asn1.h fmt_asn1intpayload.o: fmt_asn1intpayload.c asn1.h diff --git a/ldadp.c b/ldadp.c index bc5cdf9..c203362 100644 --- a/ldadp.c +++ b/ldadp.c @@ -20,16 +20,25 @@ #include #include #include +#include + +#define SAVE_INTERVAL_SEC (1200) static void listen_callback(void *data, int haveIn, int haveOut, int doCleanup); static BOOL loadConfig(char *file); static int localPort = 1234; static char *certFile = NULL, *keyFile = NULL; +static BOOL keepRunning = TRUE; + +static void sigTerm(int sn) +{ + keepRunning = FALSE; +} int main(int argc, char **argv) { - BOOL isdaemon = TRUE; + BOOL isdaemon = TRUE, useUidMapping; printf("Starting up ldadp %s\n", LDADP_VERSION); printf("Commit: %s\n", LDADP_COMMIT); printf("Commit time: %s\n", LDADP_COMMITTIME); @@ -42,6 +51,11 @@ int main(int argc, char **argv) } setbuf(stdout, NULL); signal(SIGPIPE, SIG_IGN); + struct sigaction sact; + memset(&sact, 0, sizeof(sact)); + sact.sa_handler = sigTerm; + sigaction(SIGTERM, &sact, NULL); + sigaction(SIGINT, &sact, NULL); if (strcmp(argv[1], "-n") == 0 && argc > 2) { isdaemon = FALSE; argv++; @@ -49,6 +63,7 @@ int main(int argc, char **argv) } if (!loadConfig(argv[1])) bail("Cannot read config file %s", argv[1]); if (localPort < 1 || localPort > 65535) bail("Invalid port given in config"); + useUidMapping = server_initUidMaps(); proxy_init(); char listen_addr[4] = {0, 0, 0, 0}; // Setup socket @@ -76,9 +91,21 @@ int main(int argc, char **argv) // Daeaeaemon if (isdaemon && daemon(1, 0) == -1) bail("daemon() failed."); // Do the mainloop - for (;;) { + struct timespec last, now; + clock_gettime(CLOCK_MONOTONIC, &now); + last = now; + while (keepRunning) { if (ePoll_wait(-1) == -1) bail("ePoll wait failed."); + if (useUidMapping) { + clock_gettime(CLOCK_MONOTONIC, &now); + if ((uint64_t)now.tv_sec - (uint64_t)last.tv_sec > SAVE_INTERVAL_SEC) { // Signed overflow not a good idea in C + last = now; + server_saveUidMaps(); + } + } } + plog(DEBUG_FATAL, "Shutting down..."); + server_saveUidMaps(); return 0; } @@ -152,10 +179,14 @@ static int loadConfig_handler(void *stuff, const char *section, const char *key, server_setPlainLdap(section, value); } else if (strcmp(key, "fixnumeric") == 0) { server_setFixNumeric(section, value); + } else if (strcmp(key, "uidmapstore") == 0) { + server_setUidMapStore(section, value); + } else if (strcmp(key, "genuidnumber") == 0) { + server_setGenUidNumber(section, value); } else if (strncmp(key, "map.", 4) == 0) { server_setMap(section, key+4, value); } else { - printf("Unknown ADS config option '%s' for server '%s'\n", key, section); + plog(DEBUG_WARNING, "WARNING: Unknown ADS config option '%s' for server '%s'\n", key, section); } } return 1; diff --git a/proxy.c b/proxy.c index 98151a4..275b4d1 100644 --- a/proxy.c +++ b/proxy.c @@ -4,6 +4,7 @@ #include "helper.h" #include "tmpbuffer.h" #include "ldap.h" +#include "uidmap.h" #include "lstring.h" #include #include @@ -511,16 +512,18 @@ static void request_replaceAdl(server_t *server, struct AttributeDescriptionList *adl = next; } } + // Set simple flag for there elifSETATTR(gidnumber, gidNumber); elifSETATTR(gecos, gecos); elifSETATTR(realaccount, realAccount); elifSETATTR(loginshell, loginShell); + // Further handling (uid, homeMount, objectClass, uidNumber) else request_replaceAttribute(server, &(*adl)->a, NULL, attr, FALSE); 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) { + if (attr->homeDirectory || attr->gecos || attr->homeMount || (server->genUidNumber && attr->uidNumber)) { struct AttributeDescriptionList *user = calloc(1, sizeof(struct AttributeDescriptionList)); user->a = server->map.uid; user->next = *adl; @@ -564,8 +567,21 @@ static BOOL request_replaceAttribute(server_t *server, struct string *attribute, else if (equals(value, &s_posixAccount)) *value = server->map.posixAccount; } else if (iequals(attribute, &s_uidnumber)) { *attribute = server->map.uidnumber; + if (attr) attr->uidNumber = TRUE; if (value == NULL) return TRUE; - if (value != NULL && !negated && value->l == 1 && value->s[0] == '0') { + if (server->genUidNumber && !(value->l == 1 && value->s[0] == '0')) { + // We're managing uidNumbers on the proxy + const struct string *name = uidmap_getNameForNumber(&server->uidmap, value); + if (name != NULL) { // Mapped to uid, so query will return all the requested fields + *attribute = server->map.uid; + *value = *name; + } else { + // Nothing, make sure query doesn't match + *value = s_uid; + } + return TRUE; + } + if (!negated && value->l == 1 && value->s[0] == '0') { // Saftey measure: Query for user with uidNumber == 0 - root; replace with something that // should never return anything *value = s_uid; @@ -657,9 +673,12 @@ static void response_replacePal(server_t *server, struct PartialAttributeList ** elifDELATTR(gidnumber, gidNumber); elifDELATTR(gecos, gecos); elifDELATTR(loginshell, loginShell); - elifDELATTR(uidnumber, uidNumber, !server->plainLdap &&); + elifDELATTR(uidnumber, uidNumber, (!server->plainLdap || server->genUidNumber) &&); elifDEL(mail); elifDELATTR(cn, cn, !iequals(&server->map.uid, &s_cn) &&); + else if (server->genUidNumber && iequals(&(*pal)->type, &server->map.uidnumber)) { + del = TRUE; + } else if (iequals(&(*pal)->type, &server->map.homemount)) { // 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 @@ -698,6 +717,13 @@ static void response_replacePal(server_t *server, struct PartialAttributeList ** pal = &(*pal)->next; } if (username != NULL) { + if (server->genUidNumber && attr->uidNumber) { + // Let's supply some uidNumber + uint32_t num = uidmap_getNumberForName(&server->uidmap, username); + if (num >= 2000) { + ADDATTR(uidNumber, "%"PRIu32, num); + } + } char *user = tmpbuffer_get(); snprintf(user, TMPLEN, "%.*s", (int)username->l, username->s); if (attr->homeDirectory) { diff --git a/server.c b/server.c index f22c5bf..54fd154 100644 --- a/server.c +++ b/server.c @@ -4,6 +4,7 @@ #include "epoll.h" #include "tmpbuffer.h" #include "openssl.h" +#include "uidmap.h" #include #include #include @@ -41,6 +42,13 @@ static inline uint32_t msgId() return id; } +static BOOL parseBool(const char *str) +{ + return atoi(str) != 0 || strcmp(str, "true") == 0 + || strcmp(str, "True") == 0 || strcmp(str, "TRUE") == 0 + || strcmp(str, "on") == 0 || strcmp(str, "On") == 0; +} + // Setting up server(s) void server_setPort(const char *server, const char *portStr) @@ -59,15 +67,30 @@ void server_setPlainLdap(const char *server, const char *enabledStr) { server_t *entry = server_create(server); if (entry == NULL) return; - entry->plainLdap = atoi(enabledStr) != 0 || strcmp(enabledStr, "true") == 0 - || strcmp(enabledStr, "True") == 0 || strcmp(enabledStr, "TRUE") == 0; + entry->plainLdap = parseBool(enabledStr); } void server_setFixNumeric(const char *server, const char *enabledStr) { server_t *entry = server_create(server); if (entry == NULL) return; - entry->fixNumeric = *enabledStr != '\0' || atoi(enabledStr) != 0; + entry->fixNumeric = parseBool(enabledStr); +} + +void server_setUidMapStore(const char *server, const char *fileName) +{ + server_t *entry = server_create(server); + if (entry == NULL) return; + if (fileName == NULL || *fileName == '\0') return; + entry->uidmap.fileName = strdup(fileName); +} + +void server_setGenUidNumber(const char *server, const char *enabledStr) +{ + server_t *entry = server_create(server); + if (entry == NULL) return; + entry->genUidNumber = parseBool(enabledStr); + plog(DEBUG_VERBOSE, "Using UID mapping for %s: %d", server, (int)entry->genUidNumber); } static void strtolower(char *str) @@ -226,6 +249,32 @@ void server_setFingerprint(const char *server, const char *fingerprint) ssl_init(); } +/** + * Initialize hashmaps for uid mapping. + * Returns TRUE if at least one server is + * configured to use uid mapping. + */ +BOOL server_initUidMaps() +{ + BOOL ret = FALSE; + for (int i = 0; i < serverCount; ++i) { + if (servers[i].genUidNumber) { + uidmap_init(&servers[i].uidmap); + ret = TRUE; + } + } + return ret; +} + +void server_saveUidMaps() +{ + for (int i = 0; i < serverCount; ++i) { + if (servers[i].genUidNumber) { + uidmap_cleanupSave(&servers[i].uidmap); + } + } +} + BOOL server_initServers() { int i; diff --git a/server.h b/server.h index 10ebb9f..1a01935 100644 --- a/server.h +++ b/server.h @@ -12,6 +12,10 @@ void server_setPlainLdap(const char *server, const char *enabledStr); void server_setFixNumeric(const char *server, const char *enabledStr); +void server_setUidMapStore(const char *server, const char *fileName); + +void server_setGenUidNumber(const char *server, const char *value); + void server_setMap(const char *server, const char *attribute, const char *value); void server_setBind(const char *server, const char *bind); @@ -28,6 +32,10 @@ void server_setFingerprint(const char *server, const char *fingerprint); void server_setCaBundle(const char *server, const char *file); +BOOL server_initUidMaps(); + +void server_saveUidMaps(); + BOOL server_initServers(); void server_free(epoll_server_t *server); diff --git a/types.h b/types.h index 4030902..94e2ddd 100644 --- a/types.h +++ b/types.h @@ -104,6 +104,13 @@ typedef struct { struct string uidnumber; // AD: objectSid } attr_map_t; +struct hashmap; +struct uidmap { + const char *fileName; + struct hashmap *nameToNum; + struct hashmap *numToName; +}; + /** * Configuration data for an ADS we're proxying. */ @@ -121,10 +128,12 @@ struct _server_t_ { char cabundle[MAXPATH]; BOOL plainLdap; BOOL fixNumeric; // prefix numeric account names with an 's' + BOOL genUidNumber; // generate uidNumber attribute locally (and keep track) uint16_t port; SSL_CTX *sslContext; epoll_server_t con; attr_map_t map; + struct uidmap uidmap; }; #endif diff --git a/uidmap.c b/uidmap.c index 8315be2..a1cb9f7 100644 --- a/uidmap.c +++ b/uidmap.c @@ -9,10 +9,6 @@ #include #include -static struct hashmap _numToName, _nameToNum; -static BOOL _initDone = FALSE; -static const char *_saveFileName = NULL; - static const uint16_t MAGIC = 1234; static const int64_t MAX_AGE = 86400 * 2; // 2 days #define MAX_NAME_LEN 1024 @@ -27,7 +23,7 @@ struct user int64_t lastUse; }; -static void insertTime(const struct string *name, const uint32_t number, const int64_t lastUse) +static void insertTime(struct uidmap *uidmap, const struct string *name, const uint32_t number, const int64_t lastUse) { uint8_t *buffer = malloc(sizeof(struct user) + sizeof(struct string) + name->l); struct user *u = (struct user*)buffer; @@ -39,18 +35,18 @@ static void insertTime(const struct string *name, const uint32_t number, const i u->name = s; u->number = number; u->lastUse = lastUse; - if (name_hashmap_put(&_nameToNum, u->name, u) != u) { + if (name_hashmap_put(uidmap->nameToNum, u->name, u) != u) { plog(DEBUG_WARNING, "[uidmap] Collision when inserting %.*s by name (%"PRIu32")", (int)s->l, s->s, number); } - if (number_hashmap_put(&_numToName, &u->number, u) != u) { + if (number_hashmap_put(uidmap->numToName, &u->number, u) != u) { plog(DEBUG_WARNING, "[uidmap] Collision when inserting %.*s by number (%"PRIu32")", (int)s->l, s->s, number); } // TODO: Leak } -static void insert(const struct string *name, const uint32_t number) +static void insert(struct uidmap *uidmap, const struct string *name, const uint32_t number) { - insertTime(name, number, time(NULL)); + insertTime(uidmap, name, number, time(NULL)); } static int uint32_compare(const void *a, const void *b) @@ -84,7 +80,7 @@ static size_t string_hash(const void *key) return hash; } -uint32_t uidmap_getNumberForName(const struct string *name) +uint32_t uidmap_getNumberForName(struct uidmap *uidmap, const struct string *name) { // Try string matching char buf[name->l]; @@ -93,7 +89,7 @@ uint32_t uidmap_getNumberForName(const struct string *name) buf[i] = tolower(name->s[i]); } // Get - struct user *u = name_hashmap_get(&_nameToNum, &lower); + struct user *u = name_hashmap_get(uidmap->nameToNum, &lower); if (u != NULL) { u->lastUse = time(NULL); return u->number; @@ -101,10 +97,10 @@ uint32_t uidmap_getNumberForName(const struct string *name) // Not known yet - try numeric uint32_t asNumber = parseUInt32(name); if (asNumber >= 2000) { - u = number_hashmap_get(&_numToName, &asNumber); + u = number_hashmap_get(uidmap->numToName, &asNumber); if (u == NULL) { // No collision, use name as number - insert(&lower, asNumber); + insert(uidmap, &lower, asNumber); return asNumber; } plog(DEBUG_WARNING, "%.*s is numeric (%u) but collides", (int)name->l, name->s, asNumber); @@ -116,49 +112,52 @@ uint32_t uidmap_getNumberForName(const struct string *name) } for (;;) { //plog(DEBUG_WARNING, "Using number %u for %.*s", asNumber, (int)name->l, name->s); - u = number_hashmap_get(&_numToName, &asNumber); + u = number_hashmap_get(uidmap->numToName, &asNumber); if (u == NULL) break; do { asNumber = rand() + 2000; } while (asNumber < 2000); } - insert(&lower, asNumber); + insert(uidmap, &lower, asNumber); return asNumber; } -const struct string *uidmap_getNameForNumber(const uint32_t number) +const struct string *uidmap_getNameForNumber(struct uidmap *uidmap, const struct string *numberString) { - struct user *u = number_hashmap_get(&_numToName, &number); + uint32_t number = parseUInt32(numberString); + if (number < 2000) + return NULL; + struct user *u = number_hashmap_get(uidmap->numToName, &number); if (u == NULL) return NULL; u->lastUse = time(NULL); return u->name; } -void uidmap_init(const char *fileName) +void uidmap_init(struct uidmap *uidmap) { - if (_initDone) + if (uidmap->nameToNum != NULL) return; - _initDone = TRUE; - hashmap_init(&_numToName, uint32_hash, uint32_compare, 0); - hashmap_init(&_nameToNum, string_hash, string_compare, 0); - if (fileName == NULL) + uidmap->numToName = calloc(2, sizeof(struct hashmap)); + uidmap->nameToNum = uidmap->numToName + 1; + hashmap_init(uidmap->numToName, uint32_hash, uint32_compare, 0); + hashmap_init(uidmap->nameToNum, string_hash, string_compare, 0); + if (uidmap->fileName == NULL) return; - // We have a filename, remember it and try to load DB - _saveFileName = strdup(fileName); - FILE *fh = fopen(_saveFileName, "rb"); + // We have a filename, try to load DB + FILE *fh = fopen(uidmap->fileName, "rb"); if (fh == NULL) return; fseek(fh, 0, SEEK_END); const int fsize = ftell(fh); fseek(fh, 0, SEEK_SET); - if (fsize > 0) { + if (fsize > 0) { // Guess required map size const size_t entries = (size_t)fsize / 25; - hashmap_destroy(&_numToName); - hashmap_destroy(&_nameToNum); - hashmap_init(&_numToName, uint32_hash, uint32_compare, entries); - hashmap_init(&_nameToNum, string_hash, string_compare, entries); + hashmap_destroy(uidmap->numToName); + hashmap_destroy(uidmap->nameToNum); + hashmap_init(uidmap->numToName, uint32_hash, uint32_compare, entries); + hashmap_init(uidmap->nameToNum, string_hash, string_compare, entries); } // Not endian safe, do not copy file around... uint16_t len = 0; @@ -196,38 +195,38 @@ void uidmap_init(const char *fileName) lastUse = now; // Add name.l = len; - insertTime(&name, uidNumber, lastUse); + insertTime(uidmap, &name, uidNumber, lastUse); loaded++; } fclose(fh); - plog(DEBUG_INFO, "Loaded %d entries from %s", loaded, _saveFileName); + plog(DEBUG_INFO, "Loaded %d entries from %s", loaded, uidmap->fileName); } -void uidmap_cleanupSave() +void uidmap_cleanupSave(struct uidmap *uidmap) { - if (!_initDone) + if (uidmap->nameToNum == NULL) return; FILE *fh = NULL; - if (_saveFileName != NULL) { - fh = fopen(_saveFileName, "wb"); + if (uidmap->fileName != NULL) { + fh = fopen(uidmap->fileName, "wb"); if (fh != NULL) { if (fwrite(&MAGIC, sizeof(MAGIC), 1, fh) != 1) { - plog(DEBUG_WARNING, "Cannot write magic to %s", _saveFileName); + plog(DEBUG_WARNING, "Cannot write magic to %s", uidmap->fileName); fclose(fh); fh = NULL; } } } const int64_t deadline = time(NULL) - MAX_AGE; - struct hashmap_iter *it = hashmap_iter(&_nameToNum); + struct hashmap_iter *it = hashmap_iter(uidmap->nameToNum); struct user *u = NULL; int saved = 0, removed = 0; uint16_t len; while (it != NULL) { u = name_hashmap_iter_get_data(it); if (u->lastUse < deadline) { - it = hashmap_iter_remove(&_nameToNum, it); - if (number_hashmap_remove(&_numToName, &u->number) != u) { + it = hashmap_iter_remove(uidmap->nameToNum, it); + if (number_hashmap_remove(uidmap->numToName, &u->number) != u) { plog(DEBUG_WARNING, "Could not remove user from numToName that was in nameToNum"); } free(u); @@ -242,14 +241,14 @@ void uidmap_cleanupSave() || fwrite(&u->number, sizeof(u->number), 1, fh) != 1 || fwrite(u->name->s, len, 1, fh) != 1 ) { - plog(DEBUG_WARNING, "Could not write record to %s", _saveFileName); + plog(DEBUG_WARNING, "Could not write record to %s", uidmap->fileName); fclose(fh); fh = NULL; } else { saved++; } } - it = hashmap_iter_next(&_nameToNum, it); + it = hashmap_iter_next(uidmap->nameToNum, it); } if (fh != NULL) { fsync(fileno(fh)); diff --git a/uidmap.h b/uidmap.h index a126e39..1145175 100644 --- a/uidmap.h +++ b/uidmap.h @@ -1,16 +1,17 @@ #ifndef _UIDMAP_H_ #define _UIDMAP_H_ +#include "types.h" #include struct string; -uint32_t uidmap_getNumberForName(const struct string *name); +uint32_t uidmap_getNumberForName(struct uidmap *uidmap, const struct string *name); -const struct string *uidmap_getNameForNumber(const uint32_t number); +const struct string *uidmap_getNameForNumber(struct uidmap *uidmap, const struct string *number); -void uidmap_init(const char *fileName); +void uidmap_init(struct uidmap *uidmap); -void uidmap_cleanupSave(); +void uidmap_cleanupSave(struct uidmap *uidmap); #endif -- cgit v1.2.3-55-g7522