Lookups: avoid leaking user/passwd from server spec to log. Bug 3066
[exim.git] / src / src / lookups / lf_sqlperform.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2020 - 2024 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
9
10
11 #include "../exim.h"
12 #include "lf_functions.h"
13
14
15
16 static int
17 server_len_for_logging(const uschar * server)
18 {
19 const uschar * s = Ustrchr(server, '/');
20 if (!s) return 64;
21 if (!(s = Ustrchr(s+1, '/'))) return 64;
22 return (int) (s - server);
23 }
24
25 /*************************************************
26 *    Call SQL server(s) to run an actual query   *
27 *************************************************/
28
29 /* All the SQL lookups are of the same form, with a list of servers to try
30 until one can be accessed. It is now also possible to provide the server data
31 as part of the query. This function manages server selection and looping; each
32 lookup has its own function for actually performing the lookup.
33
34 Arguments:
35   name           the lookup name, e.g. "MySQL"
36   optionname     the name of the servers option, e.g. "mysql_servers"
37   optserverlist  the value of the servers option
38   query          the query
39   result         where to pass back the result
40   errmsg         where to pass back an error message
41   do_cache       to be set zero if data is changed
42   opts           options (which suffixed the lookup name, minus cache-control ones) or NULL
43   func           the lookup function to call
44
45 Returns:         the return from the lookup function, or DEFER
46 */
47
48 int
49 lf_sqlperform(const uschar *name, const uschar *optionname,
50   const uschar *optserverlist, const uschar *query,
51   uschar **result, uschar **errmsg, uint *do_cache, const uschar * opts,
52   int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *, const uschar *))
53 {
54 int rc;
55 uschar * server;
56 BOOL defer_break = FALSE;
57
58 DEBUG(D_lookup) debug_printf_indent("%s query: \"%s\" opts '%s'\n", name, query, opts);
59
60 /* Handle queries that do have server information at the start (old style). */
61
62 if (Ustrncmp(query, "servers", 7) == 0)
63   {
64   int qsep = 0;
65   const uschar * s, * ss, * qserverlist;
66
67   log_write(0, LOG_MAIN|LOG_CONFIG_IN, "WARNING: obsolete syntax used for lookup");
68
69   s = query + 7;
70   skip_whitespace(&s);
71   if (*s++ != '=')
72     {
73     *errmsg = string_sprintf("missing = after \"servers\" in %s lookup", name);
74     return DEFER;
75     }
76   skip_whitespace(&s);
77
78   ss = Ustrchr(s, ';');
79   if (!ss)
80     {
81     *errmsg = string_sprintf("missing ; after \"servers=\" in %s lookup",
82       name);
83     return DEFER;
84     }
85
86   if (ss == s)
87     {
88     *errmsg = string_sprintf("\"servers=\" defines no servers in \"%s\"",
89       query);
90     return DEFER;
91     }
92
93   qserverlist = string_sprintf("%.*s", (int)(ss - s), s);
94   query = ss + 1;
95
96   for (uschar * qsrv; qsrv = string_nextinlist(&qserverlist, &qsep, NULL, 0); )
97     {
98     if (Ustrchr(qsrv, '/'))
99       server = qsrv;                    /* full server spec */
100     else
101       {                                 /* only name; search in option list */
102       int len = Ustrlen(qsrv);
103       const uschar * serverlist = optserverlist;
104
105       for (int sep = 0; server = string_nextinlist(&serverlist, &sep, NULL, 0);)
106         if (Ustrncmp(server, qsrv, len) == 0 && server[len] == '/')
107           break;
108
109       if (!server)
110         {
111         *errmsg = string_sprintf("%s server \"%.*s\" not found in %s",
112           name, server_len_for_logging(qsrv), qsrv, optionname);
113         return DEFER;
114         }
115       }
116
117     if (is_tainted(server))
118       {
119       *errmsg = string_sprintf("%s server \"%.*s\" is tainted",
120         name, server_len_for_logging(server), server);
121       return DEFER;
122       }
123
124     rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
125     if (rc != DEFER || defer_break) return rc;
126     }
127   }
128
129 /* Handle queries that do not have server information at the start. */
130
131 else
132   {
133   const uschar * serverlist = NULL;
134
135   /* If options are present, scan for a server definition.  Default to
136   the "optserverlist" srgument. */
137
138   if (opts)
139     {
140     uschar * ele;
141     for (int sep = ','; ele = string_nextinlist(&opts, &sep, NULL, 0); )
142       if (Ustrncmp(ele, "servers=", 8) == 0)
143         { serverlist = ele + 8; break; }
144     }
145
146   if (!serverlist)
147     serverlist = optserverlist;
148   if (!serverlist)
149     *errmsg = string_sprintf("no %s servers defined (%s option)", name,
150       optionname);
151   else
152     for (int d = 0; server = string_nextinlist(&serverlist, &d, NULL, 0); )
153       {
154       /* If not a full spec assume from options; scan main list for matching
155       hostname */
156
157       if (!Ustrchr(server, '/'))
158         {
159         int len = Ustrlen(server);
160         const uschar * slist = optserverlist;
161         uschar * ele;
162         for (int sep = 0; ele = string_nextinlist(&slist, &sep, NULL, 0); )
163           if (Ustrncmp(ele, server, len) == 0 && ele[len] == '/')
164             break;
165         if (!ele)
166           {
167           *errmsg = string_sprintf("%s server \"%s\" not found in %s", name,
168             server, optionname);
169           return DEFER;
170           }
171         server = ele;
172         }
173
174       if (is_tainted(server))
175         {
176         *errmsg = string_sprintf("%s server \"%.*s\" is tainted",
177           name, server_len_for_logging(server), server);
178         return DEFER;
179         }
180
181       rc = (*fn)(query, server, result, errmsg, &defer_break, do_cache, opts);
182       if (rc != DEFER || defer_break) return rc;
183       }
184   }
185
186 return DEFER;
187 }
188
189 /* End of lf_sqlperform.c */
190 /* vi: aw ai sw=2
191 */