xref: /openbsd-src/usr.sbin/smtpd/table_db.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /*	$OpenBSD: table_db.c,v 1.22 2021/01/23 16:11:11 rob 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 <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/queue.h>
22 #include <sys/tree.h>
23 #include <sys/socket.h>
24 
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 
28 #include <db.h>
29 #include <ctype.h>
30 #include <err.h>
31 #include <event.h>
32 #include <fcntl.h>
33 #include <imsg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "smtpd.h"
39 #include "log.h"
40 
41 
42 /* db(3) backend */
43 static int table_db_config(struct table *);
44 static int table_db_update(struct table *);
45 static int table_db_open(struct table *);
46 static void *table_db_open2(struct table *);
47 static int table_db_lookup(struct table *, enum table_service, const char *, char **);
48 static int table_db_fetch(struct table *, enum table_service, char **);
49 static void table_db_close(struct table *);
50 static void table_db_close2(void *);
51 
52 static char *table_db_get_entry(void *, const char *, size_t *);
53 static char *table_db_get_entry_match(void *, const char *, size_t *,
54     int(*)(const char *, const char *));
55 
56 struct table_backend table_backend_db = {
57 	"db",
58 	K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP,
59 	table_db_config,
60 	NULL,
61 	NULL,
62 	table_db_open,
63 	table_db_update,
64 	table_db_close,
65 	table_db_lookup,
66 	table_db_fetch,
67 };
68 
69 static struct keycmp {
70 	enum table_service	service;
71 	int		       (*func)(const char *, const char *);
72 } keycmp[] = {
73 	{ K_DOMAIN, table_domain_match },
74 	{ K_NETADDR, table_netaddr_match },
75 	{ K_MAILADDR, table_mailaddr_match }
76 };
77 
78 struct dbhandle {
79 	DB		*db;
80 	char		 pathname[PATH_MAX];
81 	time_t		 mtime;
82 	int		 iter;
83 };
84 
85 static int
86 table_db_config(struct table *table)
87 {
88 	struct dbhandle	       *handle;
89 
90 	handle = table_db_open2(table);
91 	if (handle == NULL)
92 		return 0;
93 
94 	table_db_close2(handle);
95 	return 1;
96 }
97 
98 static int
99 table_db_update(struct table *table)
100 {
101 	struct dbhandle	*handle;
102 
103 	handle = table_db_open2(table);
104 	if (handle == NULL)
105 		return 0;
106 
107 	table_db_close2(table->t_handle);
108 	table->t_handle = handle;
109 	return 1;
110 }
111 
112 static int
113 table_db_open(struct table *table)
114 {
115 	table->t_handle = table_db_open2(table);
116 	if (table->t_handle == NULL)
117 		return 0;
118 	return 1;
119 }
120 
121 static void
122 table_db_close(struct table *table)
123 {
124 	table_db_close2(table->t_handle);
125 	table->t_handle = NULL;
126 }
127 
128 static void *
129 table_db_open2(struct table *table)
130 {
131 	struct dbhandle	       *handle;
132 	struct stat		sb;
133 
134 	handle = xcalloc(1, sizeof *handle);
135 	if (strlcpy(handle->pathname, table->t_config, sizeof handle->pathname)
136 	    >= sizeof handle->pathname)
137 		goto error;
138 
139 	if (stat(handle->pathname, &sb) == -1)
140 		goto error;
141 
142 	handle->mtime = sb.st_mtime;
143 	handle->db = dbopen(table->t_config, O_RDONLY, 0600, DB_HASH, NULL);
144 	if (handle->db == NULL)
145 		goto error;
146 
147 	return handle;
148 
149 error:
150 	if (handle->db)
151 		handle->db->close(handle->db);
152 	free(handle);
153 	return NULL;
154 }
155 
156 static void
157 table_db_close2(void *hdl)
158 {
159 	struct dbhandle	*handle = hdl;
160 	handle->db->close(handle->db);
161 	free(handle);
162 }
163 
164 static int
165 table_db_lookup(struct table *table, enum table_service service, const char *key,
166     char **dst)
167 {
168 	struct dbhandle	*handle = table->t_handle;
169 	char	       *line;
170 	size_t		len = 0;
171 	int		ret;
172 	int	       (*match)(const char *, const char *) = NULL;
173 	size_t		i;
174 	struct stat	sb;
175 
176 	if (stat(handle->pathname, &sb) == -1)
177 		return -1;
178 
179 	/* DB has changed, close and reopen */
180 	if (sb.st_mtime != handle->mtime) {
181 		table_db_update(table);
182 		handle = table->t_handle;
183 	}
184 
185 	for (i = 0; i < nitems(keycmp); ++i)
186 		if (keycmp[i].service == service)
187 			match = keycmp[i].func;
188 
189 	if (match == NULL)
190 		line = table_db_get_entry(handle, key, &len);
191 	else
192 		line = table_db_get_entry_match(handle, key, &len, match);
193 	if (line == NULL)
194 		return 0;
195 
196 	ret = 1;
197 	if (dst)
198 		*dst = line;
199 	else
200 		free(line);
201 
202 	return ret;
203 }
204 
205 static int
206 table_db_fetch(struct table *table, enum table_service service, char **dst)
207 {
208 	struct dbhandle	*handle = table->t_handle;
209 	DBT dbk;
210 	DBT dbd;
211 	int r;
212 
213 	if (handle->iter == 0)
214 		r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
215 	else
216 		r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT);
217 	handle->iter = 1;
218 	if (!r) {
219 		r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
220 		if (!r)
221 			return 0;
222 	}
223 
224 	*dst = strdup(dbk.data);
225 	if (*dst == NULL)
226 		return -1;
227 
228 	return 1;
229 }
230 
231 
232 static char *
233 table_db_get_entry_match(void *hdl, const char *key, size_t *len,
234     int(*func)(const char *, const char *))
235 {
236 	struct dbhandle	*handle = hdl;
237 	DBT dbk;
238 	DBT dbd;
239 	int r;
240 	char *buf = NULL;
241 
242 	for (r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); !r;
243 	     r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT)) {
244 		buf = xmemdup(dbk.data, dbk.size);
245 		if (func(key, buf)) {
246 			*len = dbk.size;
247 			return buf;
248 		}
249 		free(buf);
250 	}
251 	return NULL;
252 }
253 
254 static char *
255 table_db_get_entry(void *hdl, const char *key, size_t *len)
256 {
257 	struct dbhandle	*handle = hdl;
258 	DBT dbk;
259 	DBT dbv;
260 	char pkey[LINE_MAX];
261 
262 	/* workaround the stupidity of the DB interface */
263 	if (strlcpy(pkey, key, sizeof pkey) >= sizeof pkey)
264 		errx(1, "table_db_get_entry: key too long");
265 	dbk.data = pkey;
266 	dbk.size = strlen(pkey) + 1;
267 
268 	if (handle->db->get(handle->db, &dbk, &dbv, 0) != 0)
269 		return NULL;
270 
271 	*len = dbv.size;
272 
273 	return xmemdup(dbv.data, dbv.size);
274 }
275