1 /* $OpenBSD: table_db.c,v 1.21 2019/06/28 13:32:51 deraadt Exp $ */
2
3 /*
4 * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "includes.h"
20
21 #include <sys/types.h>
22 #include <sys/stat.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 #ifdef HAVE_DB_H
30 #include <db.h>
31 #elif defined(HAVE_DB1_DB_H)
32 #include <db1/db.h>
33 #elif defined(HAVE_DB_185_H)
34 #include <db_185.h>
35 #endif
36 #include <ctype.h>
37 #include <err.h>
38 #include <event.h>
39 #include <fcntl.h>
40 #include <imsg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include "smtpd.h"
46 #include "log.h"
47
48
49 /* db(3) backend */
50 static int table_db_config(struct table *);
51 static int table_db_update(struct table *);
52 static int table_db_open(struct table *);
53 static void *table_db_open2(struct table *);
54 static int table_db_lookup(struct table *, enum table_service, const char *, char **);
55 static int table_db_fetch(struct table *, enum table_service, char **);
56 static void table_db_close(struct table *);
57 static void table_db_close2(void *);
58
59 static char *table_db_get_entry(void *, const char *, size_t *);
60 static char *table_db_get_entry_match(void *, const char *, size_t *,
61 int(*)(const char *, const char *));
62
63 struct table_backend table_backend_db = {
64 "db",
65 K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP,
66 table_db_config,
67 NULL,
68 NULL,
69 table_db_open,
70 table_db_update,
71 table_db_close,
72 table_db_lookup,
73 table_db_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 };
84
85 struct dbhandle {
86 DB *db;
87 char pathname[PATH_MAX];
88 time_t mtime;
89 int iter;
90 };
91
92 static int
table_db_config(struct table * table)93 table_db_config(struct table *table)
94 {
95 struct dbhandle *handle;
96
97 handle = table_db_open2(table);
98 if (handle == NULL)
99 return 0;
100
101 table_db_close2(handle);
102 return 1;
103 }
104
105 static int
table_db_update(struct table * table)106 table_db_update(struct table *table)
107 {
108 struct dbhandle *handle;
109
110 handle = table_db_open2(table);
111 if (handle == NULL)
112 return 0;
113
114 table_db_close2(table->t_handle);
115 table->t_handle = handle;
116 return 1;
117 }
118
119 static int
table_db_open(struct table * table)120 table_db_open(struct table *table)
121 {
122 table->t_handle = table_db_open2(table);
123 if (table->t_handle == NULL)
124 return 0;
125 return 1;
126 }
127
128 static void
table_db_close(struct table * table)129 table_db_close(struct table *table)
130 {
131 table_db_close2(table->t_handle);
132 table->t_handle = NULL;
133 }
134
135 static void *
table_db_open2(struct table * table)136 table_db_open2(struct table *table)
137 {
138 struct dbhandle *handle;
139 struct stat sb;
140
141 handle = xcalloc(1, sizeof *handle);
142 if (strlcpy(handle->pathname, table->t_config, sizeof handle->pathname)
143 >= sizeof handle->pathname)
144 goto error;
145
146 if (stat(handle->pathname, &sb) == -1)
147 goto error;
148
149 handle->mtime = sb.st_mtime;
150 handle->db = dbopen(table->t_config, O_RDONLY, 0600, DB_HASH, NULL);
151 if (handle->db == NULL)
152 goto error;
153
154 return handle;
155
156 error:
157 if (handle->db)
158 handle->db->close(handle->db);
159 free(handle);
160 return NULL;
161 }
162
163 static void
table_db_close2(void * hdl)164 table_db_close2(void *hdl)
165 {
166 struct dbhandle *handle = hdl;
167 handle->db->close(handle->db);
168 free(handle);
169 }
170
171 static int
table_db_lookup(struct table * table,enum table_service service,const char * key,char ** dst)172 table_db_lookup(struct table *table, enum table_service service, const char *key,
173 char **dst)
174 {
175 struct dbhandle *handle = table->t_handle;
176 char *line;
177 size_t len = 0;
178 int ret;
179 int (*match)(const char *, const char *) = NULL;
180 size_t i;
181 struct stat sb;
182
183 if (stat(handle->pathname, &sb) == -1)
184 return -1;
185
186 /* DB has changed, close and reopen */
187 if (sb.st_mtime != handle->mtime) {
188 table_db_update(table);
189 handle = table->t_handle;
190 }
191
192 for (i = 0; i < nitems(keycmp); ++i)
193 if (keycmp[i].service == service)
194 match = keycmp[i].func;
195
196 if (match == NULL)
197 line = table_db_get_entry(handle, key, &len);
198 else
199 line = table_db_get_entry_match(handle, key, &len, match);
200 if (line == NULL)
201 return 0;
202
203 ret = 1;
204 if (dst)
205 *dst = line;
206 else
207 free(line);
208
209 return ret;
210 }
211
212 static int
table_db_fetch(struct table * table,enum table_service service,char ** dst)213 table_db_fetch(struct table *table, enum table_service service, char **dst)
214 {
215 struct dbhandle *handle = table->t_handle;
216 DBT dbk;
217 DBT dbd;
218 int r;
219
220 if (handle->iter == 0)
221 r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
222 else
223 r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT);
224 handle->iter = 1;
225 if (!r) {
226 r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
227 if (!r)
228 return 0;
229 }
230
231 *dst = strdup(dbk.data);
232 if (*dst == NULL)
233 return -1;
234
235 return 1;
236 }
237
238
239 static char *
table_db_get_entry_match(void * hdl,const char * key,size_t * len,int (* func)(const char *,const char *))240 table_db_get_entry_match(void *hdl, const char *key, size_t *len,
241 int(*func)(const char *, const char *))
242 {
243 struct dbhandle *handle = hdl;
244 DBT dbk;
245 DBT dbd;
246 int r;
247 char *buf = NULL;
248
249 for (r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); !r;
250 r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT)) {
251 buf = xmemdup(dbk.data, dbk.size);
252 if (func(key, buf)) {
253 *len = dbk.size;
254 return buf;
255 }
256 free(buf);
257 }
258 return NULL;
259 }
260
261 static char *
table_db_get_entry(void * hdl,const char * key,size_t * len)262 table_db_get_entry(void *hdl, const char *key, size_t *len)
263 {
264 struct dbhandle *handle = hdl;
265 int ret;
266 DBT dbk;
267 DBT dbv;
268 char pkey[LINE_MAX];
269
270 /* workaround the stupidity of the DB interface */
271 if (strlcpy(pkey, key, sizeof pkey) >= sizeof pkey)
272 errx(1, "table_db_get_entry: key too long");
273 dbk.data = pkey;
274 dbk.size = strlen(pkey) + 1;
275
276 if ((ret = handle->db->get(handle->db, &dbk, &dbv, 0)) != 0)
277 return NULL;
278
279 *len = dbv.size;
280
281 return xmemdup(dbv.data, dbv.size);
282 }
283