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