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