static BOOL parse_route_item(const uschar *s, const uschar **domain, const uschar **hostlist, const uschar **options) { while (*s != 0 && isspace(*s)) s++; if (domain != NULL) { if (*s == 0) return FALSE; /* missing data */ *domain = string_dequote(&s); while (*s != 0 && isspace(*s)) s++; } *hostlist = string_dequote(&s); while (*s != 0 && isspace(*s)) s++; *options = s; return TRUE; }
static int control_ldap_search(uschar *ldap_url, int search_type, uschar **res, uschar **errmsg) { BOOL defer_break = FALSE; int timelimit = LDAP_NO_LIMIT; int sizelimit = LDAP_NO_LIMIT; int tcplimit = 0; int sep = 0; int dereference = LDAP_DEREF_NEVER; void* referrals = LDAP_OPT_ON; uschar *url = ldap_url; uschar *p; uschar *user = NULL; uschar *password = NULL; uschar *server, *list; uschar buffer[512]; while (isspace(*url)) url++; /* Until the string begins "ldap", search for the other parameter settings that are recognized. They are of the form NAME=VALUE, with the value being optionally double-quoted. There must still be a space after it, however. No NAME has the value "ldap". */ while (strncmpic(url, US"ldap", 4) != 0) { uschar *name = url; while (*url != 0 && *url != '=') url++; if (*url == '=') { int namelen; uschar *value; namelen = ++url - name; value = string_dequote(&url); if (isspace(*url)) { if (strncmpic(name, US"USER="******"PASS="******"SIZE=", namelen) == 0) sizelimit = Uatoi(value); else if (strncmpic(name, US"TIME=", namelen) == 0) timelimit = Uatoi(value); else if (strncmpic(name, US"CONNECT=", namelen) == 0) tcplimit = Uatoi(value); else if (strncmpic(name, US"NETTIME=", namelen) == 0) tcplimit = Uatoi(value); /* Don't know if all LDAP libraries have LDAP_OPT_DEREF */ #ifdef LDAP_OPT_DEREF else if (strncmpic(name, US"DEREFERENCE=", namelen) == 0) { if (strcmpic(value, US"never") == 0) dereference = LDAP_DEREF_NEVER; else if (strcmpic(value, US"searching") == 0) dereference = LDAP_DEREF_SEARCHING; else if (strcmpic(value, US"finding") == 0) dereference = LDAP_DEREF_FINDING; if (strcmpic(value, US"always") == 0) dereference = LDAP_DEREF_ALWAYS; } #else else if (strncmpic(name, US"DEREFERENCE=", namelen) == 0) { *errmsg = string_sprintf("LDAP_OP_DEREF not defined in this LDAP " "library - cannot use \"dereference\""); DEBUG(D_lookup) debug_printf("%s\n", *errmsg); return DEFER; } #endif #ifdef LDAP_OPT_REFERRALS else if (strncmpic(name, US"REFERRALS=", namelen) == 0) { if (strcmpic(value, US"follow") == 0) referrals = LDAP_OPT_ON; else if (strcmpic(value, US"nofollow") == 0) referrals = LDAP_OPT_OFF; else { *errmsg = string_sprintf("LDAP option REFERRALS is not \"follow\" " "or \"nofollow\""); DEBUG(D_lookup) debug_printf("%s\n", *errmsg); return DEFER; } } #else else if (strncmpic(name, US"REFERRALS=", namelen) == 0) { *errmsg = string_sprintf("LDAP_OP_REFERRALS not defined in this LDAP " "library - cannot use \"referrals\""); DEBUG(D_lookup) debug_printf("%s\n", *errmsg); return DEFER; } #endif else { *errmsg = string_sprintf("unknown parameter \"%.*s\" precedes LDAP URL", namelen, name); DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg); return DEFER; } while (isspace(*url)) url++; continue; } } *errmsg = US"malformed parameter setting precedes LDAP URL"; DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg); return DEFER; } /* If user is set, de-URL-quote it. Some LDAP libraries do this for themselves, but it seems that not all behave like this. The DN for the user is often the result of ${quote_ldap_dn:...} quoting, which does apply URL quoting, because that is needed when the DN is used as a base DN in a query. Sigh. This is all far too complicated. */ if (user != NULL) { uschar *s; uschar *t = user; for (s = user; *s != 0; s++) { int c, d; if (*s == '%' && isxdigit(c=s[1]) && isxdigit(d=s[2])) { c = tolower(c); d = tolower(d); *t++ = (((c >= 'a')? (10 + c - 'a') : c - '0') << 4) | ((d >= 'a')? (10 + d - 'a') : d - '0'); s += 2; } else *t++ = *s; } *t = 0; } DEBUG(D_lookup) debug_printf("LDAP parameters: user=%s pass=%s size=%d time=%d connect=%d " "dereference=%d referrals=%s\n", user, password, sizelimit, timelimit, tcplimit, dereference, (referrals == LDAP_OPT_ON)? "on" : "off"); /* If the request is just to check authentication, some credentials must be given. The password must not be empty because LDAP binds with an empty password are considered anonymous, and will succeed on most installations. */ if (search_type == SEARCH_LDAP_AUTH) { if (user == NULL || password == NULL) { *errmsg = US"ldapauth lookups must specify the username and password"; return DEFER; } if (password[0] == 0) { DEBUG(D_lookup) debug_printf("Empty password: ldapauth returns FAIL\n"); return FAIL; } } /* Check for valid ldap url starters */ p = url + 4; if (tolower(*p) == 's' || tolower(*p) == 'i') p++; if (Ustrncmp(p, "://", 3) != 0) { *errmsg = string_sprintf("LDAP URL does not start with \"ldap://\", " "\"ldaps://\", or \"ldapi://\" (it starts with \"%.16s...\")", url); DEBUG(D_lookup) debug_printf("LDAP query error: %s\n", *errmsg); return DEFER; } /* No default servers, or URL contains a server name: just one attempt */ if (eldap_default_servers == NULL || p[3] != '/') { return perform_ldap_search(url, NULL, 0, search_type, res, errmsg, &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference, referrals); } /* Loop through the default servers until OK or FAIL */ list = eldap_default_servers; while ((server = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) { int rc; int port = 0; uschar *colon = Ustrchr(server, ':'); if (colon != NULL) { *colon = 0; port = Uatoi(colon+1); } rc = perform_ldap_search(url, server, port, search_type, res, errmsg, &defer_break, user, password, sizelimit, timelimit, tcplimit, dereference, referrals); if (rc != DEFER || defer_break) return rc; } return DEFER; }