From bf9f3a690ead4aa59f5dbae744503e90793f770f Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Mon, 29 May 2017 15:47:03 +0200 Subject: Support specifying custom attribute names for schema --- ldadp.c | 2 + proxy.c | 341 +++++++++++++++++++++++++-------------------------------------- proxy.h | 2 + server.c | 81 ++++++++++++--- server.h | 2 + types.h | 15 ++- 6 files changed, 217 insertions(+), 226 deletions(-) diff --git a/ldadp.c b/ldadp.c index cad87ea..741df5d 100644 --- a/ldadp.c +++ b/ldadp.c @@ -140,6 +140,8 @@ static int loadConfig_handler(void *stuff, const char *section, const char *key, server_setPort(section, value); } else if (strcmp(key, "plainldap") == 0) { server_setPlainLdap(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); } diff --git a/proxy.c b/proxy.c index 07c1b61..f3a5421 100644 --- a/proxy.c +++ b/proxy.c @@ -45,8 +45,8 @@ static struct string s_uid, s_sAMAccountName, s_objectSid, s_homeMount, s_member static struct string s_objectClass, s_homeDirectory, s_gidNumber, s_gecos, s_cn, s_dn; static struct string s_loginShell, s_uidNumber, s_mail, s_objectCategory, s_memberOf, s_distinguishedName; // Some again in lowercase -static struct string s_homemount, s_memberuid, s_realaccount, s_objectclass, s_homedirectory, s_gidnumber; -static struct string s_uidnumber, s_memberof, s_distinguishedname, s_loginshell; +static struct string s_samaccountname, s_objectsid, s_homemount, s_memberuid, s_realaccount, s_objectclass; +static struct string s_homedirectory, s_gidnumber, s_uidnumber, s_memberof, s_distinguishedname, s_loginshell; // Values static struct string s_shadowAccount, s_posixAccount, s_posixGroup, s_3, s_1001, s_user, s_member; // Feature query @@ -138,6 +138,8 @@ void proxy_init() SETSTR(highestCommittedUSN); SETSTR(1001); SETSTR(3); + SETSTR(samaccountname); + SETSTR(objectsid); SETSTR(homemount); SETSTR(memberuid); SETSTR(realaccount); @@ -154,6 +156,26 @@ void proxy_init() } #undef SETSTR +void proxy_initDefaultMap(server_t *server) +{ + attr_map_t *m = &server->map; + if (server->plainLdap) { + if (m->homemount.l == 0) m->homemount = s_homemount; + if (m->localhome.l == 0) m->localhome = s_homedirectory; + if (m->posixAccount.l == 0) m->posixAccount = s_posixAccount; + if (m->shadowAccount.l == 0) m->shadowAccount = s_shadowAccount; + if (m->uid.l == 0) m->uid = s_uid; + if (m->uidnumber.l == 0) m->uidnumber = s_uidnumber; + } else { + if (m->homemount.l == 0) m->homemount = s_homedirectory; + if (m->localhome.l == 0) m->localhome = s_supportedLDAPVersion; // Unused, use something long and unlikely + if (m->posixAccount.l == 0) m->posixAccount = s_user; + if (m->shadowAccount.l == 0) m->shadowAccount = s_user; + if (m->uid.l == 0) m->uid = s_samaccountname; + if (m->uidnumber.l == 0) m->uidnumber = s_objectsid; + } +} + BOOL proxy_fromClient(epoll_client_t *client, const size_t maxLen) { unsigned long messageId, op; @@ -283,6 +305,7 @@ static void pref(int spaces, char prefix) static inline int equals(struct string *a, struct string *b) { if (a->l != b->l) return 0; + if (a->s == b->s) return 1; return strncmp(a->s, b->s, a->l) == 0; } @@ -292,6 +315,7 @@ static inline int equals(struct string *a, struct string *b) static inline int iequals(const struct string * const a, const struct string * const b) { if (a->l != b->l) return 0; + if (a->s == b->s) return 1; for (size_t i = 0; i < a->l; ++i) { if (tolower(a->s[i]) != b->s[i]) return 0; } @@ -307,9 +331,11 @@ static BOOL request_getGroupFilter(struct Filter *filter, struct string *wantedG static void request_replaceFilter(server_t *server, struct Filter **filter); static void request_replaceAdl(server_t *server, struct AttributeDescriptionList **adl, attr_t *attr); static BOOL request_replaceAttribute(server_t *server, struct string *attribute, struct string *value, attr_t *attr); -static void request_replaceAdlLdap(server_t *server, struct AttributeDescriptionList **adl, attr_t *attr); -static BOOL request_replaceAttributeLdap(server_t *server, struct string *attribute, struct string *value, attr_t *attr); +/** + * Checks whether the filter contains attributes that indicate this is a search for a user. + * Expects pre-replacement attribute names. + */ static BOOL request_isUserFilter(struct Filter *filter) { for (; filter != NULL; filter = filter->next) { @@ -436,14 +462,19 @@ static void request_replaceFilter(server_t *server, struct Filter **filter) #define elifSETATTR(MATCH,TOSET) else if (iequals(&(*adl)->a, &s_ ## MATCH)) attr->TOSET = TRUE, next = (*adl)->next, free(*adl), *adl = next static void request_replaceAdl(server_t *server, struct AttributeDescriptionList **adl, attr_t *attr) { - if (server->plainLdap) { - request_replaceAdlLdap(server, adl, attr); - return; - } while (*adl != NULL) { struct AttributeDescriptionList *next = NULL; if (attr == NULL) { } - elifSETATTR(homedirectory, homeDirectory); + else if (iequals(&(*adl)->a, &s_homedirectory)) { + attr->homeDirectory = TRUE; + if (server->plainLdap) { + (*adl)->a = server->map.localhome; + } else { + next = (*adl)->next; + free(*adl); + *adl = next; + } + } elifSETATTR(gidnumber, gidNumber); elifSETATTR(gecos, gecos); elifSETATTR(realaccount, realAccount); @@ -455,87 +486,51 @@ static void request_replaceAdl(server_t *server, struct AttributeDescriptionList if (!attr->hasUser) { if (attr->homeDirectory || attr->gecos || attr->homeMount) { struct AttributeDescriptionList *user = calloc(1, sizeof(struct AttributeDescriptionList)); - user->a = s_sAMAccountName; + user->a = server->map.uid; user->next = *adl; *adl = user; } } } +/** + * @return FALSE = delete attribute, TRUE = keep + */ static BOOL request_replaceAttribute(server_t *server, struct string *attribute, struct string *value, attr_t *attr) { - if (server->plainLdap) { - return request_replaceAttributeLdap(server, attribute, value, attr); - } if (iequals(attribute, &s_uid)) { - *attribute = s_sAMAccountName; + *attribute = server->map.uid; if (attr) attr->hasUser = TRUE; // If uid is of format s[0-9]+, we assume that it's a numeric account name in AD, as a workaround if (value == NULL) return FALSE; fixUnNumeric(value); ////// ################### } else if (iequals(attribute, &s_homemount)) { - if (server->homeAttrLower.s == NULL) { - *attribute = s_homeDirectory; - } else { - *attribute = server->homeAttr; - } + *attribute = server->map.homemount; if (attr != NULL) attr->homeMount = TRUE; } else if (iequals(attribute, &s_objectclass)) { if (value == NULL) return TRUE; - if (equals(value, &s_shadowAccount)) *value = s_user; - else if (equals(value, &s_posixAccount)) *value = s_user; + if (equals(value, &s_shadowAccount)) *value = server->map.shadowAccount; + else if (equals(value, &s_posixAccount)) *value = server->map.posixAccount; } else if (iequals(attribute, &s_uidnumber)) { - *attribute = s_objectSid; + *attribute = server->map.uidnumber; if (value == NULL) return TRUE; - if (value != NULL && value->l == 1 && value->s[0] == '0') return FALSE; - 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; - } - return TRUE; -} - -// ----------------- LDAP to LDAP handling - -static void request_replaceAdlLdap(server_t *server, struct AttributeDescriptionList **adl, attr_t *attr) -{ - while (*adl != NULL) { - struct AttributeDescriptionList *next = NULL; - if (attr == NULL) { } - else if (iequals(&(*adl)->a, &s_homedirectory)) attr->homeDirectory = TRUE; - elifSETATTR(gidnumber, gidNumber); - elifSETATTR(gecos, gecos); - elifSETATTR(realaccount, realAccount); - elifSETATTR(loginshell, loginShell); - else request_replaceAttributeLdap(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_uid; - user->next = *adl; - *adl = user; + if (value != NULL && value->l == 1 && value->s[0] == '0') { + // Query for user with uidNumber == 0 - root; replace with something that + // should never return anything + *value = s_uid; + } + if (!server->plainLdap) { + 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; } - } -} - -static BOOL request_replaceAttributeLdap(server_t *server, struct string *attribute, struct string *value, attr_t *attr) -{ - if (iequals(attribute, &s_uid)) { - if (attr) attr->hasUser = TRUE; - // If uid is of format s[0-9]+, we assume that it's a numeric account name, as a workaround - if (value == NULL) return FALSE; - fixUnNumeric(value); - } else if (iequals(attribute, &s_homemount)) { - if (attr != NULL) attr->homeMount = TRUE; } return TRUE; } + #undef elifSETATTR // ----- Sanitize requested attributes by whitelist @@ -548,6 +543,11 @@ static void prependAdl(struct AttributeDescriptionList **adl, struct string *str *adl = item; } +/** + * Add all the default attributes we're interested in in our context to the + * given ADL from a client. Pre-relace/map, with names corresponding to + * our expected names. + */ static void request_addDefaultAttributes(struct AttributeDescriptionList **adl) { prependAdl(adl, &s_uid); @@ -563,6 +563,11 @@ static void request_addDefaultAttributes(struct AttributeDescriptionList **adl) } #define S_EQ(wat) iequals(str, &s_ ## wat) +/** + * Filter all requested attributes which are not on our whitelist. + * Attribute names are pre-replacement, directly what the client sends us, before + * mapping to scheme expected by server. + */ static void request_filterRequestedAttributes(struct AttributeDescriptionList **adl) { while (*adl != NULL) { @@ -585,50 +590,52 @@ static void request_filterRequestedAttributes(struct AttributeDescriptionList ** 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 void response_replacePalLdap(server_t *server, struct PartialAttributeList **pal, attr_t *attr); -static void response_replaceAdlLdap(server_t *server, struct string *type, struct AttributeDescriptionList **adl, attr_t *attr); -static void response_replaceAttributeLdap(server_t *server, struct string *attribute, struct string *value); +static void response_replaceAttribute(server_t *server, struct string * const attribute, struct string * const value); static BOOL response_filterHomeDir(struct PartialAttributeList *pal); static BOOL response_filterLocalHomeDir(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(MATCH,FIELD) else if (iequals(&(*pal)->type, &s_ ## MATCH)) next = (*pal)->next, del = TRUE, attr->FIELD = TRUE -#define elifDEL(MATCH) else if (iequals(&(*pal)->type, &s_ ## MATCH)) next = (*pal)->next, del = TRUE +#define elifDELATTR(MATCH,FIELD,...) else if (__VA_ARGS__ (iequals(&(*pal)->type, &s_ ## MATCH)) ) del = TRUE, attr->FIELD = TRUE +#define elifDEL(MATCH) else if (iequals(&(*pal)->type, &s_ ## MATCH)) del = TRUE static void response_replacePal(server_t *server, struct PartialAttributeList **pal, attr_t *attr) { - if (server->plainLdap) { - return response_replacePalLdap(server, pal, attr); - } struct string *username = NULL; - struct AttributeDescriptionList *lastObjectClass = NULL; - struct PartialAttributeList *next = NULL; BOOL wasNumeric = FALSE; while (*pal != NULL) { BOOL del = FALSE; - if (0) { } // Remove fields we don't want from AD + if (0) { } // Remove fields we don't want from AD/LDAP elifDELATTR(gidnumber, gidNumber); elifDELATTR(gecos, gecos); elifDELATTR(loginshell, loginShell); - elifDELATTR(uidnumber, uidNumber); + elifDELATTR(uidnumber, uidNumber, !server->plainLdap &&); elifDEL(mail); elifDELATTR(cn, cn); elifDEL(memberof); - else if ( (server->homeAttrLower.s == NULL && iequals(&(*pal)->type, &s_homedirectory)) - || (server->homeAttrLower.s != NULL && iequals(&(*pal)->type, &server->homeAttrLower)) ) { + 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 if (!response_filterHomeDir(*pal)) { + // Not UNC, ignore and generate later if possible del = TRUE; attr->homeMount = TRUE; - next = (*pal)->next; } else { attr->homeMount = FALSE; + (*pal)->type = s_homeMount; + } + } + else if (iequals(&(*pal)->type, &server->map.localhome)) { + // homeDirectory is set in LDAP - use if it's a local path + if(response_filterLocalHomeDir(*pal)) { + attr->homeDirectory = FALSE; + (*pal)->type = s_homeDirectory; + } else { + del = TRUE; } } // Entry should be removed, free structs if (del) { + struct PartialAttributeList *next = (*pal)->next; free_ldapadl((*pal)->values); free(*pal); *pal = next; @@ -640,45 +647,33 @@ static void response_replacePal(server_t *server, struct PartialAttributeList ** username = &(*pal)->values->a; if (username->l > 1 && username->s[0] == 's' && isInt(username, 1)) wasNumeric = TRUE; } - // 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); + if (attr->homeDirectory) { + ADDATTR(homeDirectory, "/home/%s", user); + } ADDATTR(gecos, "%s,,,", user); ADDATTR(cn, "%s", user); - if (wasNumeric) user++; // From here on, user is the real AD username, no leading 's' + if (wasNumeric) user++; // From here on, user is the real AD/ldap username, no leading 's' if (attr->homeMount && server->homeTemplate[0] != '\0') { ADDATTR(homeMount, server->homeTemplate, user, user, user, user, user, user); } // Do this here so user++ will have been executed ADDATTR(realAccount, "%s", 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; - } + ADDATTR(loginShell, "/bin/bash"); + ADDATTR(gidNumber, "1001"); } -static void response_replaceAdl(server_t *server, struct string *type, struct AttributeDescriptionList **adl, attr_t *attr) +static void response_replaceAdl(server_t *server, struct string *attribute, struct AttributeDescriptionList **adl, attr_t *attr) { - if (server->plainLdap) { - return response_replaceAdlLdap(server, type, adl, attr); + if (iequals(attribute, &server->map.uid)) { + *attribute = s_uid; + } else if (iequals(attribute, &server->map.uidnumber)) { + *attribute = s_uidNumber; } while (*adl != NULL) { struct AttributeDescriptionList *next = NULL; @@ -688,34 +683,43 @@ static void response_replaceAdl(server_t *server, struct string *type, struct At *adl = next; continue; } - response_replaceAttribute(server, type, &(*adl)->a); + response_replaceAttribute(server, attribute, &(*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) +static void response_replaceAttribute(server_t *server, struct string * const attribute, struct string * const value) { - if (server->plainLdap) { - return response_replaceAttributeLdap(server, attribute, value); - } - if (equals(attribute, &s_sAMAccountName)) { + if (iequals(attribute, &server->map.uid)) { *attribute = s_uid; if (value != NULL) fixNumeric(value); - } else if (equals(attribute, &s_objectSid)) { + } else if (iequals(attribute, &server->map.uidnumber)) { *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); + if (!server->plainLdap) { + // If this is AD, this must have been objectSid before - convert SID to number + 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); + } + } else if (iequals(attribute, &s_objectclass)) { + if (equals(value, &server->map.posixAccount)) { + *value = s_posixAccount; + } else if (equals(value, &server->map.shadowAccount)) { + *value = s_shadowAccount; } - 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); } } +/** + * Remove every attribute except one that looks like a local home directory path. + */ static BOOL response_filterHomeDir(struct PartialAttributeList *pal) { for (struct AttributeDescriptionList *adl = pal->values; adl != NULL; adl = pal->values /* sic */) { @@ -736,6 +740,9 @@ static BOOL response_filterHomeDir(struct PartialAttributeList *pal) return FALSE; } +/** + * Remove every attribute except one that looks like a UNC path. + */ static BOOL response_filterLocalHomeDir(struct PartialAttributeList *pal) { for (struct AttributeDescriptionList *adl = pal->values; adl != NULL; adl = pal->values /* sic */) { @@ -743,6 +750,7 @@ static BOOL response_filterLocalHomeDir(struct PartialAttributeList *pal) if (adl->a.l > 0 && adl->a.s[0] == '/') { free_ldapadl(adl->next); adl->next = NULL; + pal->type = s_homeDirectory; return TRUE; } } @@ -756,99 +764,16 @@ static struct PartialAttributeList* response_addPal(struct PartialAttributeList { 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)); + va_start(args, format); tmpbuffer_formatva(&next->values->a, format, args); - next->values->next = NULL; va_end(args); + next->values->next = NULL; return next; } -// ---- replace response for LDAP to LDAP - -static void response_replacePalLdap(server_t *server, struct PartialAttributeList **pal, attr_t *attr) -{ - struct string *username = NULL; - struct PartialAttributeList *next = NULL; - BOOL wasNumeric = FALSE; - while (*pal != NULL) { - BOOL del = FALSE; - if (0) { } // Remove fields we don't want from AD - elifDELATTR(gidnumber, gidNumber); - elifDELATTR(gecos, gecos); - elifDELATTR(loginshell, loginShell); - elifDEL(mail); - elifDELATTR(cn, cn); - elifDEL(memberOf); - else if (iequals(&(*pal)->type, &s_homedirectory)) { - // homeDirectory is set in LDAP - use if it's a local path - if(response_filterLocalHomeDir(*pal)) { - attr->homeDirectory = FALSE; - } else { - next = (*pal)->next; - del = TRUE; - } - } - // Entry should be removed, free structs - if (del) { - free_ldapadl((*pal)->values); - free(*pal); - *pal = next; - continue; - } - response_replaceAdlLdap(server, &(*pal)->type, &(*pal)->values, attr); - // Fetch user name so we can add our fake fields later - if (username == NULL && iequals(&(*pal)->type, &s_uid)) { - username = &(*pal)->values->a; - if (username->l > 1 && username->s[0] == 's' && isInt(username, 1)) wasNumeric = TRUE; - } - pal = &(*pal)->next; - } - if (username != NULL) { - char *user = tmpbuffer_get(); - snprintf(user, TMPLEN, "%.*s", (int)username->l, username->s); - if (attr->homeDirectory) { - ADDATTR(homeDirectory, "/home/%s", user); - } - ADDATTR(gecos, "%s,,,", user); - ADDATTR(cn, "%s", user); - if (wasNumeric) user++; // From here on, user is the real ldap username, no leading 's' - if (attr->homeMount && server->homeTemplate[0] != '\0') { - ADDATTR(homeMount, server->homeTemplate, user, user, user, user, user, user); - } - // Do this here so user++ will have been executed - ADDATTR(realAccount, "%s", user); - } - ADDATTR(loginShell, "/bin/bash"); - ADDATTR(gidNumber, "1001"); -} -#undef ADDATTR -#undef elifDELATTR - -static void response_replaceAdlLdap(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_replaceAttributeLdap(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_replaceAttributeLdap(server_t *server, struct string *attribute, struct string *value) -{ - if (iequals(attribute, &s_uid)) { - if (value != NULL) fixNumeric(value); - } -} - // ----- static BOOL proxy_clientSearchRequest(epoll_client_t *client, const unsigned long messageId, const size_t offset, const size_t maxLen) @@ -897,9 +822,7 @@ static BOOL proxy_clientSearchRequest(epoll_client_t *client, const unsigned lon if (req.attributes == NULL) { if (client->fixedServer == NULL) { request_addDefaultAttributes(&req.attributes); - if (!server->plainLdap) { - request_replaceAdl(server, &req.attributes, &pending->attr); - } + request_replaceAdl(server, &req.attributes, &pending->attr); } memset(&pending->attr, -1, sizeof(pending->attr)); } diff --git a/proxy.h b/proxy.h index f9a781e..44d971d 100644 --- a/proxy.h +++ b/proxy.h @@ -13,4 +13,6 @@ void proxy_removeServer(epoll_server_t * const server); BOOL proxy_fromServer(epoll_server_t *server, const size_t maxLen); +void proxy_initDefaultMap(server_t *server); + #endif diff --git a/server.c b/server.c index 22f6085..ae6a4ae 100644 --- a/server.c +++ b/server.c @@ -63,6 +63,52 @@ void server_setPlainLdap(const char *server, const char *enabledStr) || strcmp(enabledStr, "True") == 0 || strcmp(enabledStr, "TRUE") == 0; } +static void strtolower(char *str) +{ + while (*str != '\0') { + *str = tolower(*str); + ++str; + } +} + +void server_setMap(const char *server, const char *attribute, const char *value) +{ + if (value == NULL || *value == '\0') { + printf("Warning: Ignoring empty mapping option '%s'\n", attribute); + return; + } + server_t *entry = server_create(server); + if (entry == NULL) return; + struct string *s = NULL; + BOOL lower = TRUE; + if (strcmp(attribute, "homemount") == 0) { + s = &entry->map.homemount; + } else if (strcmp(attribute, "localhome") == 0) { + s = &entry->map.localhome; + } else if (strcmp(attribute, "posixAccount") == 0) { + s = &entry->map.posixAccount; + lower = FALSE; + } else if (strcmp(attribute, "shadowAccount") == 0) { + s = &entry->map.shadowAccount; + lower = FALSE; + } else if (strcmp(attribute, "uid") == 0) { + s = &entry->map.uid; + } else if (strcmp(attribute, "uidnumber") == 0) { + s = &entry->map.uidnumber; + } + if (s == NULL) { + printf("Warning: Invalid mapping option: '%s'\n", attribute); + return; + } + free((void*)s->s); + char *tmp = strdup(value); + if (lower) { + strtolower(tmp); + } + s->s = tmp; + s->l = strlen(value); +} + void server_setBind(const char *server, const char *bind) { server_t *entry = server_create(server); @@ -126,16 +172,9 @@ void server_setHomeAttribute(const char *server, const char *homeattribute) { server_t *entry = server_create(server); if (entry == NULL || entry->sslContext != NULL) return; - free((void*)entry->homeAttr.s); - free((void*)entry->homeAttrLower.s); - entry->homeAttr.l = strlen(homeattribute); - entry->homeAttrLower.l = entry->homeAttr.l; - entry->homeAttr.s = strdup(homeattribute); - char *tmp = strdup(homeattribute); - for (size_t i = 0; i < entry->homeAttrLower.l; ++i) { - tmp[i] = tolower(tmp[i]); - } - entry->homeAttrLower.s = tmp; + free((void*)entry->map.homemount.s); + entry->map.homemount.s = strdup(homeattribute); + entry->map.homemount.l = strlen(homeattribute); } void server_setFingerprint(const char *server, const char *fingerprint) @@ -183,12 +222,24 @@ BOOL server_initServers() int i; printf("%d servers configured.\n", serverCount); for (i = 0; i < serverCount; ++i) { - if (servers[i].cabundle[0] != '\0' || memcmp(servers[i].fingerprint, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) != 0) { - servers[i].sslContext = ssl_newClientCtx(servers[i].cabundle); + server_t *server = &servers[i]; + if (server->cabundle[0] != '\0' || memcmp(server->fingerprint, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 20) != 0) { + // Have cabundle or fingerprint - use SSL to talk to server + server->sslContext = ssl_newClientCtx(server->cabundle); + } + printf("%s:\n Bind: %s\n Base: %s\n", server->addr, server->bind, server->base); + printf("Plain LDAP-LDAP: %d\n", (int)server->plainLdap); + // Fixup & print attribute map + proxy_initDefaultMap(server); + printf("uid: '%.*s'\n", (int)server->map.uid.l, server->map.uid.s); + printf("uidnumber: '%.*s'\n", (int)server->map.uidnumber.l, server->map.uidnumber.s); + printf("homemount: '%.*s'\n", (int)server->map.homemount.l, server->map.homemount.s); + if (server->plainLdap) { + printf("localhome: '%.*s'\n", (int)server->map.localhome.l, server->map.localhome.s); } - printf("%s:\n Bind: %s\n Base: %s\n", servers[i].addr, servers[i].bind, servers[i].base); - printf("Plain LDAP-LDAP: %d\n", (int)servers[i].plainLdap); - if (!server_ensureConnected(&servers[i])) + printf("objectClass posixAccount: '%.*s'\n", (int)server->map.posixAccount.l, server->map.posixAccount.s); + printf("objectClass shadowAccount: '%.*s'\n", (int)server->map.shadowAccount.l, server->map.shadowAccount.s); + if (!server_ensureConnected(server)) return FALSE; } connectionInitDone = TRUE; diff --git a/server.h b/server.h index 3d9d73b..6e5ac88 100644 --- a/server.h +++ b/server.h @@ -10,6 +10,8 @@ void server_setPort(const char *server, const char *portStr); void server_setPlainLdap(const char *server, const char *enabledStr); +void server_setMap(const char *server, const char *attribute, const char *value); + void server_setBind(const char *server, const char *bind); void server_setPassword(const char *server, const char *password); diff --git a/types.h b/types.h index 57f790f..19477fb 100644 --- a/types.h +++ b/types.h @@ -92,6 +92,18 @@ struct _epoll_server_t_ { server_t *serverData; }; +/** + * Struct for mapping attribute names/values + */ +typedef struct { + struct string uid; // AD: sAMAccountName + struct string homemount; // AD: homeDirectory + struct string localhome; // AD: none, LDAP: homeDirectory + struct string posixAccount; // AD: user + struct string shadowAccount; // AD: user + struct string uidnumber; // AD: objectSid +} attr_map_t; + /** * Configuration data for an ADS we're proxying. */ @@ -105,14 +117,13 @@ struct _server_t_ { char base[BASELEN]; char sid[SIDLEN]; char homeTemplate[MOUNTLEN]; - struct string homeAttr; - struct string homeAttrLower; unsigned char fingerprint[FINGERPRINTLEN]; char cabundle[MAXPATH]; BOOL plainLdap; uint16_t port; SSL_CTX *sslContext; epoll_server_t con; + attr_map_t map; }; #endif -- cgit v1.2.3-55-g7522