1 /* $OpenBSD: table_static.c,v 1.35 2024/05/14 13:28:08 op 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 <ctype.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "smtpd.h"
26 #include "log.h"
27
28 struct table_static_priv {
29 int type;
30 struct dict dict;
31 void *iter;
32 };
33
34 /* static backend */
35 static int table_static_config(struct table *);
36 static int table_static_add(struct table *, const char *, const char *);
37 static void table_static_dump(struct table *);
38 static int table_static_update(struct table *);
39 static int table_static_open(struct table *);
40 static int table_static_lookup(struct table *, enum table_service, const char *,
41 char **);
42 static int table_static_fetch(struct table *, enum table_service, char **);
43 static void table_static_close(struct table *);
44
45 struct table_backend table_backend_static = {
46 .name = "static",
47 .services = K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
48 K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
49 K_STRING|K_REGEX,
50 .config = table_static_config,
51 .add = table_static_add,
52 .dump = table_static_dump,
53 .open = table_static_open,
54 .update = table_static_update,
55 .close = table_static_close,
56 .lookup = table_static_lookup,
57 .fetch = table_static_fetch
58 };
59
60 static struct keycmp {
61 enum table_service service;
62 int (*func)(const char *, const char *);
63 } keycmp[] = {
64 { K_DOMAIN, table_domain_match },
65 { K_NETADDR, table_netaddr_match },
66 { K_MAILADDR, table_mailaddr_match },
67 { K_REGEX, table_regex_match },
68 };
69
70
71 static void
table_static_priv_free(struct table_static_priv * priv)72 table_static_priv_free(struct table_static_priv *priv)
73 {
74 void *p;
75
76 while (dict_poproot(&priv->dict, (void **)&p))
77 if (p != priv)
78 free(p);
79 free(priv);
80 }
81
82 static int
table_static_priv_add(struct table_static_priv * priv,const char * key,const char * val)83 table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val)
84 {
85 char lkey[1024];
86 void *old, *new = NULL;
87
88 if (!lowercase(lkey, key, sizeof lkey)) {
89 errno = ENAMETOOLONG;
90 return (-1);
91 }
92
93 if (val) {
94 new = strdup(val);
95 if (new == NULL)
96 return (-1);
97 }
98
99 /* use priv if value is null, so we can detect duplicate entries */
100 old = dict_set(&priv->dict, lkey, new ? new : priv);
101 if (old) {
102 if (old != priv)
103 free(old);
104 return (1);
105 }
106
107 return (0);
108 }
109
110 static int
table_static_priv_load(struct table_static_priv * priv,const char * path)111 table_static_priv_load(struct table_static_priv *priv, const char *path)
112 {
113 FILE *fp;
114 char *line = NULL;
115 int lineno = 0;
116 size_t linesize = 0;
117 char *keyp;
118 char *valp;
119 int malformed, ret = 0;
120
121 if ((fp = fopen(path, "r")) == NULL) {
122 log_warn("%s: fopen", path);
123 return 0;
124 }
125
126 while (parse_table_line(fp, &line, &linesize, &priv->type,
127 &keyp, &valp, &malformed) != -1) {
128 lineno++;
129 if (malformed) {
130 log_warnx("%s:%d invalid map entry",
131 path, lineno);
132 goto end;
133 }
134 if (keyp == NULL)
135 continue;
136 table_static_priv_add(priv, keyp, valp);
137 }
138
139 if (ferror(fp)) {
140 log_warn("%s: getline", path);
141 goto end;
142 }
143
144 /* Accept empty alias files; treat them as hashes */
145 if (priv->type == T_NONE)
146 priv->type = T_HASH;
147
148 ret = 1;
149 end:
150 free(line);
151 fclose(fp);
152 return ret;
153 }
154
155 static int
table_static_config(struct table * t)156 table_static_config(struct table *t)
157 {
158 struct table_static_priv *priv, *old;
159
160 /* already up, and no config file? ok */
161 if (t->t_handle && *t->t_config == '\0')
162 return 1;
163
164 /* new config */
165 priv = calloc(1, sizeof(*priv));
166 if (priv == NULL)
167 return 0;
168 priv->type = t->t_type;
169 dict_init(&priv->dict);
170
171 if (*t->t_config) {
172 /* load the config file */
173 if (table_static_priv_load(priv, t->t_config) == 0) {
174 table_static_priv_free(priv);
175 return 0;
176 }
177 }
178
179 if ((old = t->t_handle))
180 table_static_priv_free(old);
181 t->t_handle = priv;
182 t->t_type = priv->type;
183
184 return 1;
185 }
186
187 static int
table_static_add(struct table * table,const char * key,const char * val)188 table_static_add(struct table *table, const char *key, const char *val)
189 {
190 struct table_static_priv *priv = table->t_handle;
191 int r;
192
193 /* cannot add to a table read from a file */
194 if (*table->t_config)
195 return 0;
196
197 if (table->t_type == T_NONE)
198 table->t_type = val ? T_HASH : T_LIST;
199 else if (table->t_type == T_LIST && val)
200 return 0;
201 else if (table->t_type == T_HASH && val == NULL)
202 return 0;
203
204 if (priv == NULL) {
205 if (table_static_config(table) == 0)
206 return 0;
207 priv = table->t_handle;
208 }
209
210 r = table_static_priv_add(priv, key, val);
211 if (r == -1)
212 return 0;
213 return 1;
214 }
215
216 static void
table_static_dump(struct table * table)217 table_static_dump(struct table *table)
218 {
219 struct table_static_priv *priv = table->t_handle;
220 const char *key;
221 char *value;
222 void *iter;
223
224 iter = NULL;
225 while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) {
226 if (value && (void*)value != (void*)priv)
227 log_debug(" \"%s\" -> \"%s\"", key, value);
228 else
229 log_debug(" \"%s\"", key);
230 }
231 }
232
233 static int
table_static_update(struct table * table)234 table_static_update(struct table *table)
235 {
236 if (table_static_config(table) == 1) {
237 log_info("info: Table \"%s\" successfully updated", table->t_name);
238 return 1;
239 }
240
241 log_info("info: Failed to update table \"%s\"", table->t_name);
242 return 0;
243 }
244
245 static int
table_static_open(struct table * table)246 table_static_open(struct table *table)
247 {
248 if (table->t_handle == NULL)
249 return table_static_config(table);
250 return 1;
251 }
252
253 static void
table_static_close(struct table * table)254 table_static_close(struct table *table)
255 {
256 struct table_static_priv *priv = table->t_handle;
257
258 if (priv)
259 table_static_priv_free(priv);
260 table->t_handle = NULL;
261 }
262
263 static int
table_static_lookup(struct table * table,enum table_service service,const char * key,char ** dst)264 table_static_lookup(struct table *table, enum table_service service, const char *key,
265 char **dst)
266 {
267 struct table_static_priv *priv = table->t_handle;
268 char *line;
269 int ret;
270 int (*match)(const char *, const char *) = NULL;
271 size_t i;
272 void *iter;
273 const char *k;
274 char *v;
275
276 for (i = 0; i < nitems(keycmp); ++i)
277 if (keycmp[i].service == service)
278 match = keycmp[i].func;
279
280 line = NULL;
281 iter = NULL;
282 ret = 0;
283 while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) {
284 if (match) {
285 if (match(key, k)) {
286 line = v;
287 ret = 1;
288 }
289 }
290 else {
291 if (strcmp(key, k) == 0) {
292 line = v;
293 ret = 1;
294 }
295 }
296 if (ret)
297 break;
298 }
299
300 if (dst == NULL)
301 return ret ? 1 : 0;
302
303 if (ret == 0)
304 return 0;
305
306 *dst = strdup(line);
307 if (*dst == NULL)
308 return -1;
309
310 return 1;
311 }
312
313 static int
table_static_fetch(struct table * t,enum table_service service,char ** dst)314 table_static_fetch(struct table *t, enum table_service service, char **dst)
315 {
316 struct table_static_priv *priv = t->t_handle;
317 const char *k;
318
319 if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) {
320 priv->iter = NULL;
321 if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL))
322 return 0;
323 }
324
325 *dst = strdup(k);
326 if (*dst == NULL)
327 return -1;
328
329 return 1;
330 }
331