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