1 /* $OpenBSD: table_static.c,v 1.32 2018/12/28 14:21:02 eric Exp $ */
2
3 /*
4 * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
5 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include "includes.h"
21
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/tree.h>
25 #include <sys/socket.h>
26
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29
30 #include <ctype.h>
31 #include <errno.h>
32
33 #include <event.h>
34 #include <fcntl.h>
35 #include <imsg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <limits.h>
39 #include <string.h>
40
41 #include "smtpd.h"
42 #include "log.h"
43
44 struct table_static_priv {
45 int type;
46 struct dict dict;
47 void *iter;
48 };
49
50 /* static backend */
51 static int table_static_config(struct table *);
52 static int table_static_add(struct table *, const char *, const char *);
53 static void table_static_dump(struct table *);
54 static int table_static_update(struct table *);
55 static int table_static_open(struct table *);
56 static int table_static_lookup(struct table *, enum table_service, const char *,
57 char **);
58 static int table_static_fetch(struct table *, enum table_service, char **);
59 static void table_static_close(struct table *);
60
61 struct table_backend table_backend_static = {
62 "static",
63 K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
64 K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
65 K_STRING|K_REGEX,
66 table_static_config,
67 table_static_add,
68 table_static_dump,
69 table_static_open,
70 table_static_update,
71 table_static_close,
72 table_static_lookup,
73 table_static_fetch
74 };
75
76 static struct keycmp {
77 enum table_service service;
78 int (*func)(const char *, const char *);
79 } keycmp[] = {
80 { K_DOMAIN, table_domain_match },
81 { K_NETADDR, table_netaddr_match },
82 { K_MAILADDR, table_mailaddr_match },
83 { K_REGEX, table_regex_match },
84 };
85
86
87 static void
table_static_priv_free(struct table_static_priv * priv)88 table_static_priv_free(struct table_static_priv *priv)
89 {
90 void *p;
91
92 while (dict_poproot(&priv->dict, (void **)&p))
93 if (p != priv)
94 free(p);
95 free(priv);
96 }
97
98 static int
table_static_priv_add(struct table_static_priv * priv,const char * key,const char * val)99 table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
100 {
101 char lkey[1024];
102 void *old, *new = NULL;
103
104 if (!lowercase(lkey, key, sizeof lkey)) {
105 errno = ENAMETOOLONG;
106 return (-1);
107 }
108
109 if (val) {
110 new = strdup(val);
111 if (new == NULL)
112 return (-1);
113 }
114
115 /* use priv if value is null, so we can detect duplicate entries */
116 old = dict_set(&priv->dict, lkey, new ? new : priv);
117 if (old) {
118 if (old != priv)
119 free(old);
120 return (1);
121 }
122
123 return (0);
124 }
125
126 static int
table_static_priv_load(struct table_static_priv * priv,const char * path)127 table_static_priv_load(struct table_static_priv *priv, const char *path)
128 {
129 FILE *fp;
130 char *buf = NULL, *p;
131 int lineno = 0;
132 size_t sz = 0;
133 ssize_t flen;
134 char *keyp;
135 char *valp;
136 int ret = 0;
137
138 if ((fp = fopen(path, "r")) == NULL) {
139 log_warn("%s: fopen", path);
140 return 0;
141 }
142
143 while ((flen = getline(&buf, &sz, fp)) != -1) {
144 lineno++;
145 if (buf[flen - 1] == '\n')
146 buf[--flen] = '\0';
147
148 keyp = buf;
149 while (isspace((unsigned char)*keyp)) {
150 ++keyp;
151 --flen;
152 }
153 if (*keyp == '\0')
154 continue;
155 while (isspace((unsigned char)keyp[flen - 1]))
156 keyp[--flen] = '\0';
157 if (*keyp == '#') {
158 if (priv->type == T_NONE) {
159 keyp++;
160 while (isspace((unsigned char)*keyp))
161 ++keyp;
162 if (!strcmp(keyp, "@list"))
163 priv->type = T_LIST;
164 }
165 continue;
166 }
167
168 if (priv->type == T_NONE) {
169 for (p = keyp; *p; p++) {
170 if (*p == ' ' || *p == '\t' || *p == ':') {
171 priv->type = T_HASH;
172 break;
173 }
174 }
175 if (priv->type == T_NONE)
176 priv->type = T_LIST;
177 }
178
179 if (priv->type == T_LIST) {
180 table_static_priv_add(priv, keyp, NULL);
181 continue;
182 }
183
184 /* T_HASH */
185 valp = keyp;
186 strsep(&valp, " \t:");
187 if (valp) {
188 while (*valp) {
189 if (!isspace((unsigned char)*valp) &&
190 !(*valp == ':' &&
191 isspace((unsigned char)*(valp + 1))))
192 break;
193 ++valp;
194 }
195 if (*valp == '\0')
196 valp = NULL;
197 }
198 if (valp == NULL) {
199 log_warnx("%s: invalid map entry line %d",
200 path, lineno);
201 goto end;
202 }
203
204 table_static_priv_add(priv, keyp, valp);
205 }
206
207 if (ferror(fp)) {
208 log_warn("%s: getline", path);
209 goto end;
210 }
211
212 /* Accept empty alias files; treat them as hashes */
213 if (priv->type == T_NONE)
214 priv->type = T_HASH;
215
216 ret = 1;
217 end:
218 free(buf);
219 fclose(fp);
220 return ret;
221 }
222
223 static int
table_static_config(struct table * t)224 table_static_config(struct table *t)
225 {
226 struct table_static_priv *priv, *old;
227
228 /* already up, and no config file? ok */
229 if (t->t_handle && *t->t_config == '\0')
230 return 1;
231
232 /* new config */
233 priv = calloc(1, sizeof(*priv));
234 if (priv == NULL)
235 return 0;
236 priv->type = t->t_type;
237 dict_init(&priv->dict);
238
239 if (*t->t_config) {
240 /* load the config file */
241 if (table_static_priv_load(priv, t->t_config) == 0) {
242 table_static_priv_free(priv);
243 return 0;
244 }
245 }
246
247 if ((old = t->t_handle))
248 table_static_priv_free(old);
249 t->t_handle = priv;
250 t->t_type = priv->type;
251
252 return 1;
253 }
254
255 static int
table_static_add(struct table * table,const char * key,const char * val)256 table_static_add(struct table *table, const char *key, const char *val)
257 {
258 struct table_static_priv *priv = table->t_handle;
259 int r;
260
261 /* cannot add to a table read from a file */
262 if (*table->t_config)
263 return 0;
264
265 if (table->t_type == T_NONE)
266 table->t_type = val ? T_HASH : T_LIST;
267 else if (table->t_type == T_LIST && val)
268 return 0;
269 else if (table->t_type == T_HASH && val == NULL)
270 return 0;
271
272 if (priv == NULL) {
273 if (table_static_config(table) == 0)
274 return 0;
275 priv = table->t_handle;
276 }
277
278 r = table_static_priv_add(priv, key, val);
279 if (r == -1)
280 return 0;
281 return 1;
282 }
283
284 static void
table_static_dump(struct table * table)285 table_static_dump(struct table *table)
286 {
287 struct table_static_priv *priv = table->t_handle;
288 const char *key;
289 char *value;
290 void *iter;
291
292 iter = NULL;
293 while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
294 if (value && (void*)value != (void*)priv)
295 log_debug(" \"%s\" -> \"%s\"", key, value);
296 else
297 log_debug(" \"%s\"", key);
298 }
299 }
300
301 static int
table_static_update(struct table * table)302 table_static_update(struct table *table)
303 {
304 if (table_static_config(table) == 1) {
305 log_info("info: Table \"%s\" successfully updated", table->t_name);
306 return 1;
307 }
308
309 log_info("info: Failed to update table \"%s\"", table->t_name);
310 return 0;
311 }
312
313 static int
table_static_open(struct table * table)314 table_static_open(struct table *table)
315 {
316 if (table->t_handle == NULL)
317 return table_static_config(table);
318 return 1;
319 }
320
321 static void
table_static_close(struct table * table)322 table_static_close(struct table *table)
323 {
324 struct table_static_priv *priv = table->t_handle;
325
326 if (priv)
327 table_static_priv_free(priv);
328 table->t_handle = NULL;
329 }
330
331 static int
table_static_lookup(struct table * table,enum table_service service,const char * key,char ** dst)332 table_static_lookup(struct table *table, enum table_service service, const char *key,
333 char **dst)
334 {
335 struct table_static_priv *priv = table->t_handle;
336 char *line;
337 int ret;
338 int (*match)(const char *, const char *) = NULL;
339 size_t i;
340 void *iter;
341 const char *k;
342 char *v;
343
344 for (i = 0; i < nitems(keycmp); ++i)
345 if (keycmp[i].service == service)
346 match = keycmp[i].func;
347
348 line = NULL;
349 iter = NULL;
350 ret = 0;
351 while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
352 if (match) {
353 if (match(key, k)) {
354 line = v;
355 ret = 1;
356 }
357 }
358 else {
359 if (strcmp(key, k) == 0) {
360 line = v;
361 ret = 1;
362 }
363 }
364 if (ret)
365 break;
366 }
367
368 if (dst == NULL)
369 return ret ? 1 : 0;
370
371 if (ret == 0)
372 return 0;
373
374 *dst = strdup(line);
375 if (*dst == NULL)
376 return -1;
377
378 return 1;
379 }
380
381 static int
table_static_fetch(struct table * t,enum table_service service,char ** dst)382 table_static_fetch(struct table *t, enum table_service service, char **dst)
383 {
384 struct table_static_priv *priv = t->t_handle;
385 const char *k;
386
387 if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
388 priv->iter = NULL;
389 if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
390 return 0;
391 }
392
393 *dst = strdup(k);
394 if (*dst == NULL)
395 return -1;
396
397 return 1;
398 }
399