1*18236237Seric /* $OpenBSD: table_static.c,v 1.30 2018/12/28 10:42:18 eric Exp $ */ 265c4fdfbSgilles 365c4fdfbSgilles /* 4299c4efeSeric * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> 565c4fdfbSgilles * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 665c4fdfbSgilles * 765c4fdfbSgilles * Permission to use, copy, modify, and distribute this software for any 865c4fdfbSgilles * purpose with or without fee is hereby granted, provided that the above 965c4fdfbSgilles * copyright notice and this permission notice appear in all copies. 1065c4fdfbSgilles * 1165c4fdfbSgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1265c4fdfbSgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1365c4fdfbSgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1465c4fdfbSgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1565c4fdfbSgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1665c4fdfbSgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1765c4fdfbSgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1865c4fdfbSgilles */ 1965c4fdfbSgilles 2065c4fdfbSgilles #include <sys/types.h> 2165c4fdfbSgilles #include <sys/queue.h> 2265c4fdfbSgilles #include <sys/tree.h> 2365c4fdfbSgilles #include <sys/socket.h> 2465c4fdfbSgilles 2565c4fdfbSgilles #include <netinet/in.h> 2665c4fdfbSgilles #include <arpa/inet.h> 2765c4fdfbSgilles 2865c4fdfbSgilles #include <ctype.h> 29*18236237Seric #include <errno.h> 30*18236237Seric 3165c4fdfbSgilles #include <event.h> 3265c4fdfbSgilles #include <fcntl.h> 3365c4fdfbSgilles #include <imsg.h> 3465c4fdfbSgilles #include <stdio.h> 3565c4fdfbSgilles #include <stdlib.h> 36953aae25Sderaadt #include <limits.h> 3765c4fdfbSgilles #include <string.h> 3865c4fdfbSgilles 3965c4fdfbSgilles #include "smtpd.h" 4065c4fdfbSgilles #include "log.h" 4165c4fdfbSgilles 42*18236237Seric struct table_static_priv { 43*18236237Seric int type; 44*18236237Seric struct dict dict; 45*18236237Seric void *iter; 46*18236237Seric }; 47*18236237Seric 4865c4fdfbSgilles /* static backend */ 49299c4efeSeric static int table_static_config(struct table *); 505f0e36ccSeric static int table_static_add(struct table *, const char *, const char *); 515f0e36ccSeric static void table_static_dump(struct table *); 5265c4fdfbSgilles static int table_static_update(struct table *); 53b700b1d8Seric static int table_static_open(struct table *); 549a2428acSeric static int table_static_lookup(struct table *, enum table_service, const char *, 55aa0d26b5Seric char **); 569a2428acSeric static int table_static_fetch(struct table *, enum table_service, char **); 57a4c00f8aSeric static void table_static_close(struct table *); 5865c4fdfbSgilles 5965c4fdfbSgilles struct table_backend table_backend_static = { 604f15e081Seric "static", 6193262a01Ssunil K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO| 62a8e22235Sgilles K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST| 6381c7fb0aSgilles K_STRING|K_REGEX, 6465c4fdfbSgilles table_static_config, 655f0e36ccSeric table_static_add, 665f0e36ccSeric table_static_dump, 6765c4fdfbSgilles table_static_open, 6865c4fdfbSgilles table_static_update, 6965c4fdfbSgilles table_static_close, 7065c4fdfbSgilles table_static_lookup, 7165c4fdfbSgilles table_static_fetch 7265c4fdfbSgilles }; 7365c4fdfbSgilles 7465c4fdfbSgilles static struct keycmp { 7565c4fdfbSgilles enum table_service service; 7665c4fdfbSgilles int (*func)(const char *, const char *); 7765c4fdfbSgilles } keycmp[] = { 7865c4fdfbSgilles { K_DOMAIN, table_domain_match }, 7965c4fdfbSgilles { K_NETADDR, table_netaddr_match }, 8081c7fb0aSgilles { K_MAILADDR, table_mailaddr_match }, 8181c7fb0aSgilles { K_REGEX, table_regex_match }, 8265c4fdfbSgilles }; 8365c4fdfbSgilles 8465c4fdfbSgilles 85*18236237Seric static void 86*18236237Seric table_static_priv_free(struct table_static_priv *priv) 87*18236237Seric { 88*18236237Seric void *p; 89*18236237Seric 90*18236237Seric while (dict_poproot(&priv->dict, (void **)&p)) 91*18236237Seric if (p != priv) 92*18236237Seric free(p); 93*18236237Seric free(priv); 94*18236237Seric } 95*18236237Seric 9665c4fdfbSgilles static int 97*18236237Seric table_static_priv_add(struct table_static_priv *priv, const char *key, const char *val) 98*18236237Seric { 99*18236237Seric char lkey[1024]; 100*18236237Seric void *old, *new = NULL; 101*18236237Seric 102*18236237Seric if (!lowercase(lkey, key, sizeof lkey)) { 103*18236237Seric errno = ENAMETOOLONG; 104*18236237Seric return (-1); 105*18236237Seric } 106*18236237Seric 107*18236237Seric if (val) { 108*18236237Seric new = strdup(val); 109*18236237Seric if (new == NULL) 110*18236237Seric return (-1); 111*18236237Seric } 112*18236237Seric 113*18236237Seric /* use priv if value is null, so we can detect duplicate entries */ 114*18236237Seric old = dict_set(&priv->dict, lkey, new ? new : priv); 115*18236237Seric if (old) { 116*18236237Seric if (old != priv) 117*18236237Seric free(old); 118*18236237Seric return (1); 119*18236237Seric } 120*18236237Seric 121*18236237Seric return (0); 122*18236237Seric } 123*18236237Seric 124*18236237Seric static int 125*18236237Seric table_static_priv_load(struct table_static_priv *priv, const char *path) 126299c4efeSeric { 127299c4efeSeric FILE *fp; 128b7b75d53Seric char *buf = NULL, *p; 129b7b75d53Seric int lineno = 0; 130c17c2b51Ssunil size_t sz = 0; 131c17c2b51Ssunil ssize_t flen; 132299c4efeSeric char *keyp; 133299c4efeSeric char *valp; 134*18236237Seric int ret = 0; 135299c4efeSeric 136*18236237Seric if ((fp = fopen(path, "r")) == NULL) { 137*18236237Seric log_warn("%s: fopen", path); 138299c4efeSeric return 0; 13927c7fbfbSgilles } 140299c4efeSeric 141c17c2b51Ssunil while ((flen = getline(&buf, &sz, fp)) != -1) { 142b7b75d53Seric lineno++; 143299c4efeSeric if (buf[flen - 1] == '\n') 144b7b75d53Seric buf[--flen] = '\0'; 145299c4efeSeric 146299c4efeSeric keyp = buf; 147b7b75d53Seric while (isspace((unsigned char)*keyp)) { 148b7b75d53Seric ++keyp; 149b7b75d53Seric --flen; 150b7b75d53Seric } 151b7b75d53Seric if (*keyp == '\0') 152b7b75d53Seric continue; 153b7b75d53Seric while (isspace((unsigned char)keyp[flen - 1])) 154b7b75d53Seric keyp[--flen] = '\0'; 155b7b75d53Seric if (*keyp == '#') { 156*18236237Seric if (priv->type == T_NONE) { 157b7b75d53Seric keyp++; 158fc3a8311Seric while (isspace((unsigned char)*keyp)) 159299c4efeSeric ++keyp; 160b7b75d53Seric if (!strcmp(keyp, "@list")) 161*18236237Seric priv->type = T_LIST; 162b7b75d53Seric } 163299c4efeSeric continue; 164b7b75d53Seric } 165b7b75d53Seric 166*18236237Seric if (priv->type == T_NONE) { 167b7b75d53Seric for (p = keyp; *p; p++) { 168b7b75d53Seric if (*p == ' ' || *p == '\t' || *p == ':') { 169*18236237Seric priv->type = T_HASH; 170b7b75d53Seric break; 171b7b75d53Seric } 172b7b75d53Seric } 173*18236237Seric if (priv->type == T_NONE) 174*18236237Seric priv->type = T_LIST; 175b7b75d53Seric } 176b7b75d53Seric 177*18236237Seric if (priv->type == T_LIST) { 178*18236237Seric table_static_priv_add(priv, keyp, NULL); 179b7b75d53Seric continue; 180b7b75d53Seric } 181b7b75d53Seric 182b7b75d53Seric /* T_HASH */ 183299c4efeSeric valp = keyp; 184299c4efeSeric strsep(&valp, " \t:"); 185299c4efeSeric if (valp) { 186299c4efeSeric while (*valp) { 187fc3a8311Seric if (!isspace((unsigned char)*valp) && 18893262a01Ssunil !(*valp == ':' && 18993262a01Ssunil isspace((unsigned char)*(valp + 1)))) 190299c4efeSeric break; 191299c4efeSeric ++valp; 192299c4efeSeric } 193299c4efeSeric if (*valp == '\0') 194299c4efeSeric valp = NULL; 195299c4efeSeric } 196b7b75d53Seric if (valp == NULL) { 197*18236237Seric log_warnx("%s: invalid map entry line %d", 198*18236237Seric path, lineno); 199299c4efeSeric goto end; 200299c4efeSeric } 201b7b75d53Seric 202*18236237Seric table_static_priv_add(priv, keyp, valp); 203b7b75d53Seric } 204b7b75d53Seric 205b7b75d53Seric if (ferror(fp)) { 206*18236237Seric log_warn("%s: getline", path); 207b7b75d53Seric goto end; 208b7b75d53Seric } 209b7b75d53Seric 2103f95a974Seric /* Accept empty alias files; treat them as hashes */ 211*18236237Seric if (priv->type == T_NONE) 212*18236237Seric priv->type = T_HASH; 2133f95a974Seric 214299c4efeSeric ret = 1; 215299c4efeSeric end: 216c17c2b51Ssunil free(buf); 217299c4efeSeric fclose(fp); 218299c4efeSeric return ret; 21965c4fdfbSgilles } 22065c4fdfbSgilles 22165c4fdfbSgilles static int 222*18236237Seric table_static_config(struct table *t) 2235f0e36ccSeric { 224*18236237Seric struct table_static_priv *priv, *old; 2255f0e36ccSeric 226*18236237Seric /* already up, and no config file? ok */ 227*18236237Seric if (t->t_handle && *t->t_config == '\0') 228*18236237Seric return 1; 229*18236237Seric 230*18236237Seric /* new config */ 231*18236237Seric priv = calloc(1, sizeof(*priv)); 232*18236237Seric if (priv == NULL) 233*18236237Seric return 0; 234*18236237Seric priv->type = t->t_type; 235*18236237Seric dict_init(&priv->dict); 236*18236237Seric 237*18236237Seric if (*t->t_config) { 238*18236237Seric /* load the config file */ 239*18236237Seric if (table_static_priv_load(priv, t->t_config) == 0) { 240*18236237Seric table_static_priv_free(priv); 2415f0e36ccSeric return 0; 2425f0e36ccSeric } 2435f0e36ccSeric } 2445f0e36ccSeric 245*18236237Seric if ((old = t->t_handle)) 246*18236237Seric table_static_priv_free(old); 247*18236237Seric t->t_handle = priv; 248*18236237Seric t->t_type = priv->type; 249*18236237Seric 250*18236237Seric return 1; 251*18236237Seric } 252*18236237Seric 253*18236237Seric static int 254*18236237Seric table_static_add(struct table *table, const char *key, const char *val) 255*18236237Seric { 256*18236237Seric struct table_static_priv *priv = table->t_handle; 257*18236237Seric int r; 258*18236237Seric 259*18236237Seric /* cannot add to a table read from a file */ 260*18236237Seric if (*table->t_config) 261*18236237Seric return 0; 262*18236237Seric 263*18236237Seric if (priv == NULL) { 264*18236237Seric if (table_static_config(table) == 0) 265*18236237Seric return 0; 266*18236237Seric priv = table->t_handle; 267*18236237Seric } 268*18236237Seric 269*18236237Seric r = table_static_priv_add(priv, key, val); 270*18236237Seric if (r == -1) 271*18236237Seric return 0; 2725f0e36ccSeric return 1; 2735f0e36ccSeric } 2745f0e36ccSeric 2755f0e36ccSeric static void 2765f0e36ccSeric table_static_dump(struct table *table) 2775f0e36ccSeric { 278*18236237Seric struct table_static_priv *priv = table->t_handle; 2795f0e36ccSeric const char *key; 2805f0e36ccSeric char *value; 2815f0e36ccSeric void *iter; 2825f0e36ccSeric 2835f0e36ccSeric iter = NULL; 284*18236237Seric while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) { 2855f0e36ccSeric if (value) 2865f0e36ccSeric log_debug(" \"%s\" -> \"%s\"", key, value); 2875f0e36ccSeric else 2885f0e36ccSeric log_debug(" \"%s\"", key); 2895f0e36ccSeric } 2905f0e36ccSeric } 2915f0e36ccSeric 2925f0e36ccSeric static int 29365c4fdfbSgilles table_static_update(struct table *table) 29465c4fdfbSgilles { 295*18236237Seric if (table_static_config(table) == 1) { 296dd047c3bSgilles log_info("info: Table \"%s\" successfully updated", table->t_name); 29765c4fdfbSgilles return 1; 298*18236237Seric } 29965c4fdfbSgilles 300dd047c3bSgilles log_info("info: Failed to update table \"%s\"", table->t_name); 30165c4fdfbSgilles return 0; 30265c4fdfbSgilles } 30365c4fdfbSgilles 304b700b1d8Seric static int 30565c4fdfbSgilles table_static_open(struct table *table) 30665c4fdfbSgilles { 307*18236237Seric if (table->t_handle == NULL) 308*18236237Seric return table_static_config(table); 309b700b1d8Seric return 1; 31065c4fdfbSgilles } 31165c4fdfbSgilles 31265c4fdfbSgilles static void 313a4c00f8aSeric table_static_close(struct table *table) 31465c4fdfbSgilles { 315*18236237Seric struct table_static_priv *priv = table->t_handle; 316*18236237Seric 317*18236237Seric if (priv) 318*18236237Seric table_static_priv_free(priv); 319a4c00f8aSeric table->t_handle = NULL; 32065c4fdfbSgilles } 32165c4fdfbSgilles 32265c4fdfbSgilles static int 323*18236237Seric table_static_lookup(struct table *table, enum table_service service, const char *key, 32493cc0b04Seric char **dst) 32565c4fdfbSgilles { 326*18236237Seric struct table_static_priv *priv = table->t_handle; 32765c4fdfbSgilles char *line; 32865c4fdfbSgilles int ret; 32965c4fdfbSgilles int (*match)(const char *, const char *) = NULL; 33065c4fdfbSgilles size_t i; 33165c4fdfbSgilles void *iter; 33265c4fdfbSgilles const char *k; 33365c4fdfbSgilles char *v; 33465c4fdfbSgilles 33565c4fdfbSgilles for (i = 0; i < nitems(keycmp); ++i) 33665c4fdfbSgilles if (keycmp[i].service == service) 33765c4fdfbSgilles match = keycmp[i].func; 33865c4fdfbSgilles 33965c4fdfbSgilles line = NULL; 34065c4fdfbSgilles iter = NULL; 34165c4fdfbSgilles ret = 0; 342*18236237Seric while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) { 34365c4fdfbSgilles if (match) { 34465c4fdfbSgilles if (match(key, k)) { 34565c4fdfbSgilles line = v; 34665c4fdfbSgilles ret = 1; 34765c4fdfbSgilles } 34865c4fdfbSgilles } 34965c4fdfbSgilles else { 35065c4fdfbSgilles if (strcmp(key, k) == 0) { 35165c4fdfbSgilles line = v; 35265c4fdfbSgilles ret = 1; 35365c4fdfbSgilles } 35465c4fdfbSgilles } 35565c4fdfbSgilles if (ret) 35665c4fdfbSgilles break; 35765c4fdfbSgilles } 35865c4fdfbSgilles 359aa0d26b5Seric if (dst == NULL) 36065c4fdfbSgilles return ret ? 1 : 0; 36165c4fdfbSgilles 362299c4efeSeric if (ret == 0) 36365c4fdfbSgilles return 0; 36465c4fdfbSgilles 365aa0d26b5Seric *dst = strdup(line); 366aa0d26b5Seric if (*dst == NULL) 367aa0d26b5Seric return -1; 368aa0d26b5Seric 369aa0d26b5Seric return 1; 37065c4fdfbSgilles } 37165c4fdfbSgilles 37265c4fdfbSgilles static int 3739a2428acSeric table_static_fetch(struct table *t, enum table_service service, char **dst) 37465c4fdfbSgilles { 375*18236237Seric struct table_static_priv *priv = t->t_handle; 37665c4fdfbSgilles const char *k; 37765c4fdfbSgilles 378*18236237Seric if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) { 379*18236237Seric priv->iter = NULL; 380*18236237Seric if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) 38165c4fdfbSgilles return 0; 38265c4fdfbSgilles } 38365c4fdfbSgilles 384aa0d26b5Seric *dst = strdup(k); 385aa0d26b5Seric if (*dst == NULL) 386aa0d26b5Seric return -1; 387aa0d26b5Seric 388aa0d26b5Seric return 1; 38965c4fdfbSgilles } 390