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