diff options
author | Jeremy Harris <jgh146exb@wizmail.org> | 2020-04-06 00:28:06 +0100 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2020-04-06 13:14:05 +0100 |
commit | 0b4dfe7aa1f12214abdfa1037497d6c49a471612 (patch) | |
tree | 7a033655ce31568e0fd78417b4b98fa3143668cd /src | |
parent | ba74fb8d95d2e9af2122e0a95c4d5334b4f0466c (diff) |
MySQL, pgsql: per-query server options outside the lookup string. Bug 2546
Diffstat (limited to 'src')
-rw-r--r-- | src/src/acl.c | 9 | ||||
-rw-r--r-- | src/src/lookups/lf_functions.h | 5 | ||||
-rw-r--r-- | src/src/lookups/lf_sqlperform.c | 118 | ||||
-rw-r--r-- | src/src/lookups/mysql.c | 31 | ||||
-rw-r--r-- | src/src/lookups/pgsql.c | 5 | ||||
-rw-r--r-- | src/src/lookups/redis.c | 5 | ||||
-rw-r--r-- | src/src/string.c | 4 | ||||
-rw-r--r-- | src/src/verify.c | 7 |
8 files changed, 108 insertions, 76 deletions
diff --git a/src/src/acl.c b/src/src/acl.c index 02251b197..3ea8df1b1 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3477,13 +3477,13 @@ for (; cb; cb = cb->next) { uschar *endcipher = NULL; uschar *cipher = Ustrchr(tls_in.cipher, ':'); - if (cipher == NULL) cipher = tls_in.cipher; else + if (!cipher) cipher = tls_in.cipher; else { endcipher = Ustrchr(++cipher, ':'); - if (endcipher != NULL) *endcipher = 0; + if (endcipher) *endcipher = 0; } rc = match_isinlist(cipher, &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); - if (endcipher != NULL) *endcipher = ':'; + if (endcipher) *endcipher = ':'; } break; @@ -3496,8 +3496,7 @@ for (; cb; cb = cb->next) case ACLC_HOSTS: rc = verify_check_this_host(&arg, sender_host_cache, NULL, - (sender_host_address == NULL)? US"" : sender_host_address, - CUSS &host_data); + sender_host_address ? sender_host_address : US"", CUSS &host_data); if (rc == DEFER) *log_msgptr = search_error_message; if (host_data) host_data = string_copy_perm(host_data, TRUE); break; diff --git a/src/src/lookups/lf_functions.h b/src/src/lookups/lf_functions.h index 4d9ae9595..7e7ac4d24 100644 --- a/src/src/lookups/lf_functions.h +++ b/src/src/lookups/lf_functions.h @@ -12,7 +12,8 @@ extern int lf_check_file(int, const uschar *, int, int, uid_t *, gid_t *, extern gstring *lf_quote(uschar *, uschar *, int, gstring *); extern int lf_sqlperform(const uschar *, const uschar *, const uschar *, const uschar *, uschar **, - uschar **, uint *, int(*)(const uschar *, uschar *, uschar **, - uschar **, BOOL *, uint *)); + uschar **, uint *, const uschar *, + int(*)(const uschar *, uschar *, uschar **, + uschar **, BOOL *, uint *, const uschar *)); /* End of lf_functions.h */ diff --git a/src/src/lookups/lf_sqlperform.c b/src/src/lookups/lf_sqlperform.c index cc894e0ed..e2636bfe9 100644 --- a/src/src/lookups/lf_sqlperform.c +++ b/src/src/lookups/lf_sqlperform.c @@ -36,55 +36,35 @@ Returns: the return from the lookup function, or DEFER int lf_sqlperform(const uschar *name, const uschar *optionname, const uschar *optserverlist, const uschar *query, - uschar **result, uschar **errmsg, uint *do_cache, - int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *)) + uschar **result, uschar **errmsg, uint *do_cache, const uschar * opts, + int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *, const uschar *)) { -int sep, rc; +int rc; uschar *server; -const uschar *serverlist; -uschar buffer[512]; BOOL defer_break = FALSE; -DEBUG(D_lookup) debug_printf_indent("%s query: %s\n", name, query); - -/* Handle queries that do not have server information at the start. */ - -if (Ustrncmp(query, "servers", 7) != 0) - { - sep = 0; - serverlist = optserverlist; - while ((server = string_nextinlist(&serverlist, &sep, buffer, - sizeof(buffer))) != NULL) - { - rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache); - if (rc != DEFER || defer_break) return rc; - } - if (optserverlist == NULL) - *errmsg = string_sprintf("no %s servers defined (%s option)", name, - optionname); - } +DEBUG(D_lookup) debug_printf_indent("%s query: \"%s\" opts '%s'\n", name, query, opts); /* Handle queries that do have server information at the start. */ -else +if (Ustrncmp(query, "servers", 7) == 0) { - int qsep; + int qsep = 0; const uschar *s, *ss; const uschar *qserverlist; uschar *qserver; - uschar qbuffer[512]; s = query + 7; - while (isspace(*s)) s++; + skip_whitespace(&s); if (*s++ != '=') { *errmsg = string_sprintf("missing = after \"servers\" in %s lookup", name); return DEFER; } - while (isspace(*s)) s++; + skip_whitespace(&s); ss = Ustrchr(s, ';'); - if (ss == NULL) + if (!ss) { *errmsg = string_sprintf("missing ; after \"servers=\" in %s lookup", name); @@ -99,27 +79,21 @@ else } qserverlist = string_sprintf("%.*s", (int)(ss - s), s); - qsep = 0; - while ((qserver = string_nextinlist(&qserverlist, &qsep, qbuffer, - sizeof(qbuffer))) != NULL) + while ((qserver = string_nextinlist(&qserverlist, &qsep, NULL, 0))) { - if (Ustrchr(qserver, '/') != NULL) + if (Ustrchr(qserver, '/')) server = qserver; else { int len = Ustrlen(qserver); + const uschar * serverlist = optserverlist; - sep = 0; - serverlist = optserverlist; - while ((server = string_nextinlist(&serverlist, &sep, buffer, - sizeof(buffer))) != NULL) - { + for (int sep = 0; server = string_nextinlist(&serverlist, &sep, NULL, 0);) if (Ustrncmp(server, qserver, len) == 0 && server[len] == '/') break; - } - if (server == NULL) + if (!server) { *errmsg = string_sprintf("%s server \"%s\" not found in %s", name, qserver, optionname); @@ -127,11 +101,73 @@ else } } - rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache); + if (is_tainted(server)) + { + *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server); + return DEFER; + } + + rc = (*fn)(ss+1, server, result, errmsg, &defer_break, do_cache, opts); if (rc != DEFER || defer_break) return rc; } } +/* Handle queries that do not have server information at the start. */ + +else + { + const uschar * serverlist = NULL; + + /* If options are present, scan for a server definition. Default to + the "optserverlist" srgument. */ + + if (opts) + { + uschar * ele; + for (int sep = ','; ele = string_nextinlist(&opts, &sep, NULL, 0); ) + if (Ustrncmp(ele, "servers=", 8) == 0) + { serverlist = ele + 8; break; } + } + + if (!serverlist) + serverlist = optserverlist; + if (!serverlist) + *errmsg = string_sprintf("no %s servers defined (%s option)", name, + optionname); + else + for (int d = 0; (server = string_nextinlist(&serverlist, &d, NULL, 0)); ) + { + /* If not a full spec assume from options; scan main list for matching + hostname */ + + if (!Ustrchr(server, '/')) + { + int len = Ustrlen(server); + const uschar * slist = optserverlist; + uschar * ele; + for (int sep = 0; ele = string_nextinlist(&slist, &sep, NULL, 0); ) + if (Ustrncmp(ele, server, len) == 0 && ele[len] == '/') + break; + if (!ele) + { + *errmsg = string_sprintf("%s server \"%s\" not found in %s", name, + server, optionname); + return DEFER; + } + server = ele; + } + + if (is_tainted(server)) + { + *errmsg = string_sprintf("%s server \"%s\" is tainted", name, server); + return DEFER; + } + + rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts); + if (rc != DEFER || defer_break) return rc; + } + } + return DEFER; } diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c index 7651353a2..220aba1c3 100644 --- a/src/src/lookups/mysql.c +++ b/src/src/lookups/mysql.c @@ -122,6 +122,7 @@ Arguments: errmsg where to point an error message defer_break TRUE if no more servers are to be tried after DEFER do_cache set zero if data is changed + opts options The server string is of the form "host/dbname/user/password". The host can be host:port. This string is in a nextinlist temporary buffer, so can be @@ -132,7 +133,7 @@ Returns: OK, FAIL, or DEFER static int perform_mysql_search(const uschar *query, uschar *server, uschar **resultptr, - uschar **errmsg, BOOL *defer_break, uint *do_cache) + uschar **errmsg, BOOL *defer_break, uint *do_cache, const uschar * opts) { MYSQL *mysql_handle = NULL; /* Keep compilers happy */ MYSQL_RES *mysql_result = NULL; @@ -155,7 +156,7 @@ has the password removed. This copy is also used for debugging output. */ for (int i = 3; i > 0; i--) { uschar *pp = Ustrrchr(server, '/'); - if (pp == NULL) + if (!pp) { *errmsg = string_sprintf("incomplete MySQL server data: %s", (i == 3)? server : server_copy); @@ -172,10 +173,7 @@ sdata[0] = server; /* What's left at the start */ for (cn = mysql_connections; cn; cn = cn->next) if (Ustrcmp(cn->server, server_copy) == 0) - { - mysql_handle = cn->handle; - break; - } + { mysql_handle = cn->handle; break; } /* If no cached connection, we must set one up. Mysql allows for a host name and port to be specified. It also allows the name of a Unix socket to be used. @@ -283,7 +281,7 @@ up. Setting do_cache zero requests this. */ if (!(mysql_result = mysql_use_result(mysql_handle))) { - if ( mysql_field_count(mysql_handle) == 0 ) + if (mysql_field_count(mysql_handle) == 0) { DEBUG(D_lookup) debug_printf_indent("MYSQL: query was not one that returns data\n"); result = string_cat(result, @@ -328,18 +326,15 @@ while ((mysql_row_data = mysql_fetch_row(mysql_result))) we don't expect any more results. */ while((i = mysql_next_result(mysql_handle)) >= 0) - { - if(i == 0) /* Just ignore more results */ + if(i != 0) { - DEBUG(D_lookup) debug_printf_indent("MYSQL: got unexpected more results\n"); - continue; + *errmsg = string_sprintf( + "MYSQL: lookup result error when checking for more results: %s\n", + mysql_error(mysql_handle)); + goto MYSQL_EXIT; } - - *errmsg = string_sprintf( - "MYSQL: lookup result error when checking for more results: %s\n", - mysql_error(mysql_handle)); - goto MYSQL_EXIT; - } + else /* just ignore more results */ + DEBUG(D_lookup) debug_printf_indent("MYSQL: got unexpected more results\n"); /* If result is NULL then no data has been found and so we return FAIL. Otherwise, we must terminate the string which has been built; string_cat() @@ -394,7 +389,7 @@ mysql_find(void * handle, const uschar * filename, const uschar * query, const uschar * opts) { return lf_sqlperform(US"MySQL", US"mysql_servers", mysql_servers, query, - result, errmsg, do_cache, perform_mysql_search); + result, errmsg, do_cache, opts, perform_mysql_search); } diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c index 90dda601a..505f5e5ce 100644 --- a/src/src/lookups/pgsql.c +++ b/src/src/lookups/pgsql.c @@ -113,13 +113,14 @@ Arguments: errmsg where to point an error message defer_break set TRUE if no more servers are to be tried after DEFER do_cache set FALSE if data is changed + opts options list Returns: OK, FAIL, or DEFER */ static int perform_pgsql_search(const uschar *query, uschar *server, uschar **resultptr, - uschar **errmsg, BOOL *defer_break, uint *do_cache) + uschar **errmsg, BOOL *defer_break, uint *do_cache, const uschar * opts) { PGconn *pg_conn = NULL; PGresult *pg_result = NULL; @@ -386,7 +387,7 @@ pgsql_find(void * handle, const uschar * filename, const uschar * query, const uschar * opts) { return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query, - result, errmsg, do_cache, perform_pgsql_search); + result, errmsg, do_cache, opts, perform_pgsql_search); } diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c index 84a2dc6c7..337fdae15 100644 --- a/src/src/lookups/redis.c +++ b/src/src/lookups/redis.c @@ -63,6 +63,7 @@ single server. errmsg where to point an error message defer_break TRUE if no more servers are to be tried after DEFER do_cache set false if data is changed + opts options The server string is of the form "host/dbnumber/password". The host can be host:port. This string is in a nextinlist temporary buffer, so can be @@ -73,7 +74,7 @@ single server. static int perform_redis_search(const uschar *command, uschar *server, uschar **resultptr, - uschar **errmsg, BOOL *defer_break, uint *do_cache) + uschar **errmsg, BOOL *defer_break, uint *do_cache, const uschar * opts) { redisContext *redis_handle = NULL; /* Keep compilers happy */ redisReply *redis_reply = NULL; @@ -380,7 +381,7 @@ redis_find(void * handle __attribute__((unused)), uint * do_cache, const uschar * opts) { return lf_sqlperform(US"Redis", US"redis_servers", redis_servers, command, - result, errmsg, do_cache, perform_redis_search); + result, errmsg, do_cache, opts, perform_redis_search); } diff --git a/src/src/string.c b/src/src/string.c index 80cf49fdf..1192a554e 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -574,18 +574,14 @@ uschar *ss = yield = store_get(Ustrlen(s) + 1, is_tainted(s)); while (*s != 0) { if (*s != '\\') - { *ss++ = *s++; - } else if (isdigit(s[1])) { *ss++ = (s[1] - '0')*100 + (s[2] - '0')*10 + s[3] - '0'; s += 4; } else if (*(++s) != 0) - { *ss++ = *s++; - } } *ss = 0; diff --git a/src/src/verify.c b/src/src/verify.c index cd9df1f71..dda51a5a2 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -2909,7 +2909,6 @@ provided that host name matching is permitted; if it's "@[]" match against the local host's IP addresses. */ if (*ss == '@') - { if (ss[1] == 0) { if (isiponly) return ERROR; @@ -2921,7 +2920,6 @@ if (*ss == '@') if (Ustrcmp(ip->address, cb->host_address) == 0) return OK; return FAIL; } - } /* If the pattern is an IP address, optionally followed by a bitmask count, do a (possibly masked) comparison with the current IP address. */ @@ -2951,6 +2949,11 @@ if (*t == 0 || (*t == '/' && t != ss)) if ((semicolon = Ustrchr(ss, ';'))) endname = (opts = Ustrchr(ss, ',')) ? opts : semicolon; +if (opts) + { + opts++; + opts = string_copyn(opts, semicolon - opts); + } /* If we are doing an IP address only match, then all lookups must be IP address lookups, even if there is no "net-". */ |