1 /* $OpenBSD: table_static.c,v 1.34 2024/02/11 09:24:26 op 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 *line = NULL; 115 int lineno = 0; 116 size_t linesize = 0; 117 char *keyp; 118 char *valp; 119 int malformed, ret = 0; 120 121 if ((fp = fopen(path, "r")) == NULL) { 122 log_warn("%s: fopen", path); 123 return 0; 124 } 125 126 while (parse_table_line(fp, &line, &linesize, &priv->type, 127 &keyp, &valp, &malformed) != -1) { 128 lineno++; 129 if (malformed) { 130 log_warnx("%s:%d invalid map entry", 131 path, lineno); 132 goto end; 133 } 134 if (keyp == NULL) 135 continue; 136 table_static_priv_add(priv, keyp, valp); 137 } 138 139 if (ferror(fp)) { 140 log_warn("%s: getline", path); 141 goto end; 142 } 143 144 /* Accept empty alias files; treat them as hashes */ 145 if (priv->type == T_NONE) 146 priv->type = T_HASH; 147 148 ret = 1; 149 end: 150 free(line); 151 fclose(fp); 152 return ret; 153 } 154 155 static int 156 table_static_config(struct table *t) 157 { 158 struct table_static_priv *priv, *old; 159 160 /* already up, and no config file? ok */ 161 if (t->t_handle && *t->t_config == '\0') 162 return 1; 163 164 /* new config */ 165 priv = calloc(1, sizeof(*priv)); 166 if (priv == NULL) 167 return 0; 168 priv->type = t->t_type; 169 dict_init(&priv->dict); 170 171 if (*t->t_config) { 172 /* load the config file */ 173 if (table_static_priv_load(priv, t->t_config) == 0) { 174 table_static_priv_free(priv); 175 return 0; 176 } 177 } 178 179 if ((old = t->t_handle)) 180 table_static_priv_free(old); 181 t->t_handle = priv; 182 t->t_type = priv->type; 183 184 return 1; 185 } 186 187 static int 188 table_static_add(struct table *table, const char *key, const char *val) 189 { 190 struct table_static_priv *priv = table->t_handle; 191 int r; 192 193 /* cannot add to a table read from a file */ 194 if (*table->t_config) 195 return 0; 196 197 if (table->t_type == T_NONE) 198 table->t_type = val ? T_HASH : T_LIST; 199 else if (table->t_type == T_LIST && val) 200 return 0; 201 else if (table->t_type == T_HASH && val == NULL) 202 return 0; 203 204 if (priv == NULL) { 205 if (table_static_config(table) == 0) 206 return 0; 207 priv = table->t_handle; 208 } 209 210 r = table_static_priv_add(priv, key, val); 211 if (r == -1) 212 return 0; 213 return 1; 214 } 215 216 static void 217 table_static_dump(struct table *table) 218 { 219 struct table_static_priv *priv = table->t_handle; 220 const char *key; 221 char *value; 222 void *iter; 223 224 iter = NULL; 225 while (dict_iter(&priv->dict, &iter, &key, (void**)&value)) { 226 if (value && (void*)value != (void*)priv) 227 log_debug(" \"%s\" -> \"%s\"", key, value); 228 else 229 log_debug(" \"%s\"", key); 230 } 231 } 232 233 static int 234 table_static_update(struct table *table) 235 { 236 if (table_static_config(table) == 1) { 237 log_info("info: Table \"%s\" successfully updated", table->t_name); 238 return 1; 239 } 240 241 log_info("info: Failed to update table \"%s\"", table->t_name); 242 return 0; 243 } 244 245 static int 246 table_static_open(struct table *table) 247 { 248 if (table->t_handle == NULL) 249 return table_static_config(table); 250 return 1; 251 } 252 253 static void 254 table_static_close(struct table *table) 255 { 256 struct table_static_priv *priv = table->t_handle; 257 258 if (priv) 259 table_static_priv_free(priv); 260 table->t_handle = NULL; 261 } 262 263 static int 264 table_static_lookup(struct table *table, enum table_service service, const char *key, 265 char **dst) 266 { 267 struct table_static_priv *priv = table->t_handle; 268 char *line; 269 int ret; 270 int (*match)(const char *, const char *) = NULL; 271 size_t i; 272 void *iter; 273 const char *k; 274 char *v; 275 276 for (i = 0; i < nitems(keycmp); ++i) 277 if (keycmp[i].service == service) 278 match = keycmp[i].func; 279 280 line = NULL; 281 iter = NULL; 282 ret = 0; 283 while (dict_iter(&priv->dict, &iter, &k, (void **)&v)) { 284 if (match) { 285 if (match(key, k)) { 286 line = v; 287 ret = 1; 288 } 289 } 290 else { 291 if (strcmp(key, k) == 0) { 292 line = v; 293 ret = 1; 294 } 295 } 296 if (ret) 297 break; 298 } 299 300 if (dst == NULL) 301 return ret ? 1 : 0; 302 303 if (ret == 0) 304 return 0; 305 306 *dst = strdup(line); 307 if (*dst == NULL) 308 return -1; 309 310 return 1; 311 } 312 313 static int 314 table_static_fetch(struct table *t, enum table_service service, char **dst) 315 { 316 struct table_static_priv *priv = t->t_handle; 317 const char *k; 318 319 if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) { 320 priv->iter = NULL; 321 if (!dict_iter(&priv->dict, &priv->iter, &k, (void **)NULL)) 322 return 0; 323 } 324 325 *dst = strdup(k); 326 if (*dst == NULL) 327 return -1; 328 329 return 1; 330 } 331