1 /* $OpenBSD: table_static.c,v 1.32 2018/12/28 14:21:02 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> 5 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/types.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 <ctype.h> 29 #include <errno.h> 30 31 #include <event.h> 32 #include <fcntl.h> 33 #include <imsg.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <limits.h> 37 #include <string.h> 38 39 #include "smtpd.h" 40 #include "log.h" 41 42 struct table_static_priv { 43 int type; 44 struct dict dict; 45 void *iter; 46 }; 47 48 /* static backend */ 49 static int table_static_config(struct table *); 50 static int table_static_add(struct table *, const char *, const char *); 51 static void table_static_dump(struct table *); 52 static int table_static_update(struct table *); 53 static int table_static_open(struct table *); 54 static int table_static_lookup(struct table *, enum table_service, const char *, 55 char **); 56 static int table_static_fetch(struct table *, enum table_service, char **); 57 static void table_static_close(struct table *); 58 59 struct table_backend table_backend_static = { 60 "static", 61 K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO| 62 K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST| 63 K_STRING|K_REGEX, 64 table_static_config, 65 table_static_add, 66 table_static_dump, 67 table_static_open, 68 table_static_update, 69 table_static_close, 70 table_static_lookup, 71 table_static_fetch 72 }; 73 74 static struct keycmp { 75 enum table_service service; 76 int (*func)(const char *, const char *); 77 } keycmp[] = { 78 { K_DOMAIN, table_domain_match }, 79 { K_NETADDR, table_netaddr_match }, 80 { K_MAILADDR, table_mailaddr_match }, 81 { K_REGEX, table_regex_match }, 82 }; 83 84 85 static void 86 table_static_priv_free(struct table_static_priv *priv) 87 { 88 void *p; 89 90 while (dict_poproot(&priv->dict, (void **)&p)) 91 if (p != priv) 92 free(p); 93 free(priv); 94 } 95 96 static int 97 table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val) 98 { 99 char lkey[1024]; 100 void *old, *new = NULL; 101 102 if (!lowercase(lkey, key, sizeof lkey)) { 103 errno = ENAMETOOLONG; 104 return (-1); 105 } 106 107 if (val) { 108 new = strdup(val); 109 if (new == NULL) 110 return (-1); 111 } 112 113 /* use priv if value is null, so we can detect duplicate entries */ 114 old = dict_set(&priv->dict, lkey, new ? new : priv); 115 if (old) { 116 if (old != priv) 117 free(old); 118 return (1); 119 } 120 121 return (0); 122 } 123 124 static int 125 table_static_priv_load(struct table_static_priv *priv, const char *path) 126 { 127 FILE *fp; 128 char *buf = NULL, *p; 129 int lineno = 0; 130 size_t sz = 0; 131 ssize_t flen; 132 char *keyp; 133 char *valp; 134 int ret = 0; 135 136 if ((fp = fopen(path, "r")) == NULL) { 137 log_warn("%s: fopen", path); 138 return 0; 139 } 140 141 while ((flen = getline(&buf, &sz, fp)) != -1) { 142 lineno++; 143 if (buf[flen - 1] == '\n') 144 buf[--flen] = '\0'; 145 146 keyp = buf; 147 while (isspace((unsigned char)*keyp)) { 148 ++keyp; 149 --flen; 150 } 151 if (*keyp == '\0') 152 continue; 153 while (isspace((unsigned char)keyp[flen - 1])) 154 keyp[--flen] = '\0'; 155 if (*keyp == '#') { 156 if (priv->type == T_NONE) { 157 keyp++; 158 while (isspace((unsigned char)*keyp)) 159 ++keyp; 160 if (!strcmp(keyp, "@list")) 161 priv->type = T_LIST; 162 } 163 continue; 164 } 165 166 if (priv->type == T_NONE) { 167 for (p = keyp; *p; p++) { 168 if (*p == ' ' || *p == '\t' || *p == ':') { 169 priv->type = T_HASH; 170 break; 171 } 172 } 173 if (priv->type == T_NONE) 174 priv->type = T_LIST; 175 } 176 177 if (priv->type == T_LIST) { 178 table_static_priv_add(priv, keyp, NULL); 179 continue; 180 } 181 182 /* T_HASH */ 183 valp = keyp; 184 strsep(&valp, " \t:"); 185 if (valp) { 186 while (*valp) { 187 if (!isspace((unsigned char)*valp) && 188 !(*valp == ':' && 189 isspace((unsigned char)*(valp + 1)))) 190 break; 191 ++valp; 192 } 193 if (*valp == '\0') 194 valp = NULL; 195 } 196 if (valp == NULL) { 197 log_warnx("%s: invalid map entry line %d", 198 path, lineno); 199 goto end; 200 } 201 202 table_static_priv_add(priv, keyp, valp); 203 } 204 205 if (ferror(fp)) { 206 log_warn("%s: getline", path); 207 goto end; 208 } 209 210 /* Accept empty alias files; treat them as hashes */ 211 if (priv->type == T_NONE) 212 priv->type = T_HASH; 213 214 ret = 1; 215 end: 216 free(buf); 217 fclose(fp); 218 return ret; 219 } 220 221 static int 222 table_static_config(struct table *t) 223 { 224 struct table_static_priv *priv, *old; 225 226 /* already up, and no config file? ok */ 227 if (t->t_handle && *t->t_config == '\0') 228 return 1; 229 230 /* new config */ 231 priv = calloc(1, sizeof(*priv)); 232 if (priv == NULL) 233 return 0; 234 priv->type = t->t_type; 235 dict_init(&priv->dict); 236 237 if (*t->t_config) { 238 /* load the config file */ 239 if (table_static_priv_load(priv, t->t_config) == 0) { 240 table_static_priv_free(priv); 241 return 0; 242 } 243 } 244 245 if ((old = t->t_handle)) 246 table_static_priv_free(old); 247 t->t_handle = priv; 248 t->t_type = priv->type; 249 250 return 1; 251 } 252 253 static int 254 table_static_add(struct table *table, const char *key, const char *val) 255 { 256 struct table_static_priv *priv = table->t_handle; 257 int r; 258 259 /* cannot add to a table read from a file */ 260 if (*table->t_config) 261 return 0; 262 263 if (table->t_type == T_NONE) 264 table->t_type = val ? T_HASH : T_LIST; 265 else if (table->t_type == T_LIST && val) 266 return 0; 267 else if (table->t_type == T_HASH && val == NULL) 268 return 0; 269 270 if (priv == NULL) { 271 if (table_static_config(table) == 0) 272 return 0; 273 priv = table->t_handle; 274 } 275 276 r = table_static_priv_add(priv, key, val); 277 if (r == -1) 278 return 0; 279 return 1; 280 } 281 282 static void 283 table_static_dump(struct table *table) 284 { 285 struct table_static_priv *priv = table->t_handle; 286 const char *key; 287 char *value; 288 void *iter; 289 290 iter = NULL; 291 while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) { 292 if (value && (void*)value != (void*)priv) 293 log_debug(" \"%s\" -> \"%s\"", key, value); 294 else 295 log_debug(" \"%s\"", key); 296 } 297 } 298 299 static int 300 table_static_update(struct table *table) 301 { 302 if (table_static_config(table) == 1) { 303 log_info("info: Table \"%s\" successfully updated", table->t_name); 304 return 1; 305 } 306 307 log_info("info: Failed to update table \"%s\"", table->t_name); 308 return 0; 309 } 310 311 static int 312 table_static_open(struct table *table) 313 { 314 if (table->t_handle == NULL) 315 return table_static_config(table); 316 return 1; 317 } 318 319 static void 320 table_static_close(struct table *table) 321 { 322 struct table_static_priv *priv = table->t_handle; 323 324 if (priv) 325 table_static_priv_free(priv); 326 table->t_handle = NULL; 327 } 328 329 static int 330 table_static_lookup(struct table *table, enum table_service service, const char *key, 331 char **dst) 332 { 333 struct table_static_priv *priv = table->t_handle; 334 char *line; 335 int ret; 336 int (*match)(const char *, const char *) = NULL; 337 size_t i; 338 void *iter; 339 const char *k; 340 char *v; 341 342 for (i = 0; i < nitems(keycmp); ++i) 343 if (keycmp[i].service == service) 344 match = keycmp[i].func; 345 346 line = NULL; 347 iter = NULL; 348 ret = 0; 349 while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) { 350 if (match) { 351 if (match(key, k)) { 352 line = v; 353 ret = 1; 354 } 355 } 356 else { 357 if (strcmp(key, k) == 0) { 358 line = v; 359 ret = 1; 360 } 361 } 362 if (ret) 363 break; 364 } 365 366 if (dst == NULL) 367 return ret ? 1 : 0; 368 369 if (ret == 0) 370 return 0; 371 372 *dst = strdup(line); 373 if (*dst == NULL) 374 return -1; 375 376 return 1; 377 } 378 379 static int 380 table_static_fetch(struct table *t, enum table_service service, char **dst) 381 { 382 struct table_static_priv *priv = t->t_handle; 383 const char *k; 384 385 if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) { 386 priv->iter = NULL; 387 if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) 388 return 0; 389 } 390 391 *dst = strdup(k); 392 if (*dst == NULL) 393 return -1; 394 395 return 1; 396 } 397