Track tainted data and refuse to expand it
[exim.git] / src / src / lookups / dbmdb.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2018 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 #include "../exim.h"
9 #include "lf_functions.h"
10
11
12 /*************************************************
13 *              Open entry point                  *
14 *************************************************/
15
16 /* See local README for interface description */
17
18 static void *
19 dbmdb_open(uschar *filename, uschar **errmsg)
20 {
21 uschar * dirname = string_copy(filename);
22 uschar * s;
23 EXIM_DB *yield = NULL;
24
25 if ((s = Ustrrchr(dirname, '/'))) *s = '\0';
26 EXIM_DBOPEN(filename, dirname, O_RDONLY, 0, &yield);
27 if (yield == NULL)
28   {
29   int save_errno = errno;
30   *errmsg = string_open_failed(errno, "%s as a %s file", filename, EXIM_DBTYPE);
31   errno = save_errno;
32   }
33 return yield;
34 }
35
36
37
38 /*************************************************
39 *             Check entry point                  *
40 *************************************************/
41
42 /* This needs to know more about the underlying files than is good for it!
43 We need to know what the real file names are in order to check the owners and
44 modes. If USE_DB is set, we know it is Berkeley DB, which uses an unmodified
45 file name. If USE_TDB or USE_GDBM is set, we know it is tdb or gdbm, which do
46 the same. Otherwise, for safety, we have to check for x.db or x.dir and x.pag.
47 */
48
49 static BOOL
50 dbmdb_check(void *handle, uschar *filename, int modemask, uid_t *owners,
51   gid_t *owngroups, uschar **errmsg)
52 {
53 int rc;
54 handle = handle;    /* Keep picky compilers happy */
55
56 #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM)
57 rc = lf_check_file(-1, filename, S_IFREG, modemask, owners, owngroups,
58   "dbm", errmsg);
59 #else
60   {
61   uschar filebuffer[256];
62   (void)sprintf(CS filebuffer, "%.250s.db", filename);
63   rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
64     "dbm", errmsg);
65   if (rc < 0)        /* stat() failed */
66     {
67     (void)sprintf(CS filebuffer, "%.250s.dir", filename);
68     rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
69       "dbm", errmsg);
70     if (rc == 0)     /* x.dir was OK */
71       {
72       (void)sprintf(CS filebuffer, "%.250s.pag", filename);
73       rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups,
74         "dbm", errmsg);
75       }
76     }
77   }
78 #endif
79
80 return rc == 0;
81 }
82
83
84
85 /*************************************************
86 *              Find entry point                  *
87 *************************************************/
88
89 /* See local README for interface description. This function adds 1 to
90 the keylength in order to include the terminating zero. */
91
92 static int
93 dbmdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
94   uschar **result, uschar **errmsg, uint *do_cache)
95 {
96 EXIM_DB *d = (EXIM_DB *)handle;
97 EXIM_DATUM key, data;
98
99 filename = filename;    /* Keep picky compilers happy */
100 errmsg = errmsg;
101 do_cache = do_cache;
102
103 EXIM_DATUM_INIT(key);               /* Some DBM libraries require datums to */
104 EXIM_DATUM_INIT(data);              /* be cleared before use. */
105 EXIM_DATUM_DATA(key) = CS keystring;
106 EXIM_DATUM_SIZE(key) = length + 1;
107
108 if (EXIM_DBGET(d, key, data))
109   {
110   *result = string_copyn(US EXIM_DATUM_DATA(data), EXIM_DATUM_SIZE(data));
111   EXIM_DATUM_FREE(data);            /* Some DBM libraries need a free() call */
112   return OK;
113   }
114 return FAIL;
115 }
116
117
118
119 /*************************************************
120 *      Find entry point - no zero on key         *
121 *************************************************/
122
123 /* See local README for interface description */
124
125 int
126 static dbmnz_find(void *handle, uschar *filename, const uschar *keystring, int length,
127   uschar **result, uschar **errmsg, uint *do_cache)
128 {
129 return dbmdb_find(handle, filename, keystring, length-1, result, errmsg,
130   do_cache);
131 }
132
133
134
135 /*************************************************
136 *     Find entry point - zero-joined list key    *
137 *************************************************/
138
139 /*
140  * The parameter passed as a key is a list in normal Exim list syntax.
141  * The elements of that list are joined together on NUL, with no trailing
142  * NUL, to form the key.
143  */
144
145 static int
146 dbmjz_find(void *handle, uschar *filename, const uschar *keystring, int length,
147   uschar **result, uschar **errmsg, uint *do_cache)
148 {
149 uschar *key_item, *key_buffer, *key_p;
150 const uschar *key_elems = keystring;
151 int buflen, bufleft, key_item_len, sep = 0;
152
153 /* To a first approximation, the size of the lookup key needs to be about,
154 or less than, the length of the delimited list passed in + 1. */
155
156 buflen = length + 3;
157 key_buffer = store_get(buflen, is_tainted(keystring));
158
159 key_buffer[0] = '\0';
160
161 key_p = key_buffer;
162 bufleft = buflen;
163
164 /* In all cases of an empty list item, we can set 1 and advance by 1 and then
165 pick up the trailing NUL from the previous list item, EXCEPT when at the
166 beginning of the output string, in which case we need to supply that NUL
167 ourselves.  */
168 while ((key_item = string_nextinlist(&key_elems, &sep, key_p, bufleft)) != NULL)
169   {
170   key_item_len = Ustrlen(key_item) + 1;
171   if (key_item_len == 1)
172     {
173     key_p[0] = '\0';
174     if (key_p == key_buffer)
175       {
176       key_p[1] = '\0';
177       key_item_len += 1;
178       }
179     }
180
181   bufleft -= key_item_len;
182   if (bufleft <= 0)
183     {
184     /* The string_nextinlist() will stop at buffer size, but we should always
185     have at least 1 character extra, so some assumption has failed. */
186     *errmsg = string_copy(US"Ran out of buffer space for joining elements");
187     return DEFER;
188     }
189   key_p += key_item_len;
190   }
191
192 if (key_p == key_buffer)
193   {
194   *errmsg = string_copy(US"empty list key");
195   return FAIL;
196   }
197
198 /* We do not pass in the final NULL; if needed, the list should include an
199 empty element to put one in. Boundary: key length 1, is a NULL */
200 key_item_len = key_p - key_buffer - 1;
201
202 DEBUG(D_lookup) debug_printf_indent("NUL-joined key length: %d\n", key_item_len);
203
204 /* beware that dbmdb_find() adds 1 to length to get back terminating NUL, so
205 because we've calculated the real length, we need to subtract one more here */
206 return dbmdb_find(handle, filename,
207     key_buffer, key_item_len - 1,
208     result, errmsg, do_cache);
209 }
210
211
212
213 /*************************************************
214 *              Close entry point                 *
215 *************************************************/
216
217 /* See local README for interface description */
218
219 void
220 static dbmdb_close(void *handle)
221 {
222 EXIM_DBCLOSE((EXIM_DB *)handle);
223 }
224
225
226
227 /*************************************************
228 *         Version reporting entry point          *
229 *************************************************/
230
231 /* See local README for interface description. */
232
233 #include "../version.h"
234
235 void
236 dbm_version_report(FILE *f)
237 {
238 #ifdef DYNLOOKUP
239 fprintf(f, "Library version: DBM: Exim version %s\n", EXIM_VERSION_STR);
240 #endif
241 }
242
243
244 lookup_info dbm_lookup_info = {
245   US"dbm",                       /* lookup name */
246   lookup_absfile,                /* uses absolute file name */
247   dbmdb_open,                    /* open function */
248   dbmdb_check,                   /* check function */
249   dbmdb_find,                    /* find function */
250   dbmdb_close,                   /* close function */
251   NULL,                          /* no tidy function */
252   NULL,                          /* no quoting function */
253   dbm_version_report             /* version reporting */
254 };
255
256 lookup_info dbmz_lookup_info = {
257   US"dbmnz",                     /* lookup name */
258   lookup_absfile,                /* uses absolute file name */
259   dbmdb_open,      /* sic */     /* open function */
260   dbmdb_check,     /* sic */     /* check function */
261   dbmnz_find,                    /* find function */
262   dbmdb_close,     /* sic */     /* close function */
263   NULL,                          /* no tidy function */
264   NULL,                          /* no quoting function */
265   NULL                           /* no version reporting (redundant) */
266 };
267
268 lookup_info dbmjz_lookup_info = {
269   US"dbmjz",                     /* lookup name */
270   lookup_absfile,                /* uses absolute file name */
271   dbmdb_open,      /* sic */     /* open function */
272   dbmdb_check,     /* sic */     /* check function */
273   dbmjz_find,                    /* find function */
274   dbmdb_close,     /* sic */     /* close function */
275   NULL,                          /* no tidy function */
276   NULL,                          /* no quoting function */
277   NULL                           /* no version reporting (redundant) */
278 };
279
280 #ifdef DYNLOOKUP
281 #define dbmdb_lookup_module_info _lookup_module_info
282 #endif
283
284 static lookup_info *_lookup_list[] = { &dbm_lookup_info, &dbmz_lookup_info, &dbmjz_lookup_info };
285 lookup_module_info dbmdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 3 };
286
287 /* End of lookups/dbmdb.c */