| File: | src/usr.sbin/smtpd/smtpd/../table.c |
| Warning: | line 549, column 8 Null pointer passed as 2nd argument to string copy function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: table.c,v 1.50 2021/06/14 17:58:16 eric Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> | |||
| 5 | * Copyright (c) 2008 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/stat.h> | |||
| 21 | ||||
| 22 | #include <net/if.h> | |||
| 23 | ||||
| 24 | #include <arpa/inet.h> | |||
| 25 | #include <errno(*__errno()).h> | |||
| 26 | #include <regex.h> | |||
| 27 | #include <stdlib.h> | |||
| 28 | #include <string.h> | |||
| 29 | ||||
| 30 | #include "smtpd.h" | |||
| 31 | #include "log.h" | |||
| 32 | ||||
| 33 | struct table_backend *table_backend_lookup(const char *); | |||
| 34 | ||||
| 35 | extern struct table_backend table_backend_static; | |||
| 36 | extern struct table_backend table_backend_db; | |||
| 37 | extern struct table_backend table_backend_getpwnam; | |||
| 38 | extern struct table_backend table_backend_proc; | |||
| 39 | ||||
| 40 | static const char * table_service_name(enum table_service); | |||
| 41 | static int table_parse_lookup(enum table_service, const char *, const char *, | |||
| 42 | union lookup *); | |||
| 43 | static int parse_sockaddr(struct sockaddr *, int, const char *); | |||
| 44 | ||||
| 45 | static unsigned int last_table_id = 0; | |||
| 46 | ||||
| 47 | static struct table_backend *backends[] = { | |||
| 48 | &table_backend_static, | |||
| 49 | &table_backend_db, | |||
| 50 | &table_backend_getpwnam, | |||
| 51 | &table_backend_proc, | |||
| 52 | NULL((void *)0) | |||
| 53 | }; | |||
| 54 | ||||
| 55 | struct table_backend * | |||
| 56 | table_backend_lookup(const char *backend) | |||
| 57 | { | |||
| 58 | int i; | |||
| 59 | ||||
| 60 | if (!strcmp(backend, "file")) | |||
| 61 | backend = "static"; | |||
| 62 | ||||
| 63 | for (i = 0; backends[i]; i++) | |||
| 64 | if (!strcmp(backends[i]->name, backend)) | |||
| 65 | return (backends[i]); | |||
| 66 | ||||
| 67 | return NULL((void *)0); | |||
| 68 | } | |||
| 69 | ||||
| 70 | static const char * | |||
| 71 | table_service_name(enum table_service s) | |||
| 72 | { | |||
| 73 | switch (s) { | |||
| 74 | case K_NONE: return "NONE"; | |||
| 75 | case K_ALIAS: return "ALIAS"; | |||
| 76 | case K_DOMAIN: return "DOMAIN"; | |||
| 77 | case K_CREDENTIALS: return "CREDENTIALS"; | |||
| 78 | case K_NETADDR: return "NETADDR"; | |||
| 79 | case K_USERINFO: return "USERINFO"; | |||
| 80 | case K_SOURCE: return "SOURCE"; | |||
| 81 | case K_MAILADDR: return "MAILADDR"; | |||
| 82 | case K_ADDRNAME: return "ADDRNAME"; | |||
| 83 | case K_MAILADDRMAP: return "MAILADDRMAP"; | |||
| 84 | case K_RELAYHOST: return "RELAYHOST"; | |||
| 85 | case K_STRING: return "STRING"; | |||
| 86 | case K_REGEX: return "REGEX"; | |||
| 87 | } | |||
| 88 | return "???"; | |||
| 89 | } | |||
| 90 | ||||
| 91 | struct table * | |||
| 92 | table_find(struct smtpd *conf, const char *name) | |||
| 93 | { | |||
| 94 | return dict_get(conf->sc_tables_dict, name); | |||
| 95 | } | |||
| 96 | ||||
| 97 | int | |||
| 98 | table_match(struct table *table, enum table_service kind, const char *key) | |||
| 99 | { | |||
| 100 | return table_lookup(table, kind, key, NULL((void *)0)); | |||
| 101 | } | |||
| 102 | ||||
| 103 | int | |||
| 104 | table_lookup(struct table *table, enum table_service kind, const char *key, | |||
| 105 | union lookup *lk) | |||
| 106 | { | |||
| 107 | char lkey[1024], *buf = NULL((void *)0); | |||
| 108 | int r; | |||
| 109 | ||||
| 110 | r = -1; | |||
| 111 | if (table->t_backend->lookup == NULL((void *)0)) | |||
| 112 | errno(*__errno()) = ENOTSUP91; | |||
| 113 | else if (!lowercase(lkey, key, sizeof lkey)) { | |||
| 114 | log_warnx("warn: lookup key too long: %s", key); | |||
| 115 | errno(*__errno()) = EINVAL22; | |||
| 116 | } | |||
| 117 | else | |||
| 118 | r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL((void *)0)); | |||
| 119 | ||||
| 120 | if (r == 1) { | |||
| 121 | log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
| 122 | lk ? "lookup" : "match",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
| 123 | key,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
| 124 | table_service_name(kind),do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
| 125 | table->t_backend->name,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
| 126 | table->t_name,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
| 127 | lk ? "\"" : "",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
| 128 | lk ? buf : "true",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0) | |||
| 129 | lk ? "\"" : "")do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, lk ? "\"" : "", lk ? buf : "true", lk ? "\"" : ""); } while (0); | |||
| 130 | if (buf) | |||
| 131 | r = table_parse_lookup(kind, lkey, buf, lk); | |||
| 132 | } | |||
| 133 | else | |||
| 134 | log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
| 135 | lk ? "lookup" : "match",do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
| 136 | key,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
| 137 | table_service_name(kind),do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
| 138 | table->t_backend->name,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
| 139 | table->t_name,do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
| 140 | (r == -1) ? "error: " : (lk ? "none" : "false"),do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0) | |||
| 141 | (r == -1) ? strerror(errno) : "")do { if (tracing & (0x0100)) log_trace0("lookup: %s \"%s\" as %s in table %s:%s -> %s%s" , lk ? "lookup" : "match", key, table_service_name(kind), table ->t_backend->name, table->t_name, (r == -1) ? "error: " : (lk ? "none" : "false"), (r == -1) ? strerror((*__errno()) ) : ""); } while (0); | |||
| 142 | ||||
| 143 | free(buf); | |||
| 144 | ||||
| 145 | return (r); | |||
| 146 | } | |||
| 147 | ||||
| 148 | int | |||
| 149 | table_fetch(struct table *table, enum table_service kind, union lookup *lk) | |||
| 150 | { | |||
| 151 | char *buf = NULL((void *)0); | |||
| 152 | int r; | |||
| 153 | ||||
| 154 | r = -1; | |||
| 155 | if (table->t_backend->fetch == NULL((void *)0)) | |||
| ||||
| 156 | errno(*__errno()) = ENOTSUP91; | |||
| 157 | else | |||
| 158 | r = table->t_backend->fetch(table, kind, &buf); | |||
| 159 | ||||
| 160 | if (r == 1) { | |||
| 161 | log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"",do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0) | |||
| 162 | table_service_name(kind),do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0) | |||
| 163 | table->t_backend->name,do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0) | |||
| 164 | table->t_name,do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0) | |||
| 165 | buf)do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> \"%s\"" , table_service_name(kind), table->t_backend->name, table ->t_name, buf); } while (0); | |||
| 166 | r = table_parse_lookup(kind, NULL((void *)0), buf, lk); | |||
| 167 | } | |||
| 168 | else | |||
| 169 | log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s",do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
| 170 | table_service_name(kind),do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
| 171 | table->t_backend->name,do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
| 172 | table->t_name,do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
| 173 | (r == -1) ? "error: " : "none",do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0) | |||
| 174 | (r == -1) ? strerror(errno) : "")do { if (tracing & (0x0100)) log_trace0("lookup: fetch %s from table %s:%s -> %s%s" , table_service_name(kind), table->t_backend->name, table ->t_name, (r == -1) ? "error: " : "none", (r == -1) ? strerror ((*__errno())) : ""); } while (0); | |||
| 175 | ||||
| 176 | free(buf); | |||
| 177 | ||||
| 178 | return (r); | |||
| 179 | } | |||
| 180 | ||||
| 181 | struct table * | |||
| 182 | table_create(struct smtpd *conf, const char *backend, const char *name, | |||
| 183 | const char *config) | |||
| 184 | { | |||
| 185 | struct table *t; | |||
| 186 | struct table_backend *tb; | |||
| 187 | char path[LINE_MAX2048]; | |||
| 188 | size_t n; | |||
| 189 | struct stat sb; | |||
| 190 | ||||
| 191 | if (name && table_find(conf, name)) | |||
| 192 | fatalx("table_create: table \"%s\" already defined", name); | |||
| 193 | ||||
| 194 | if ((tb = table_backend_lookup(backend)) == NULL((void *)0)) { | |||
| 195 | if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/usr/local/libexec/smtpd""/table-%s", | |||
| 196 | backend) >= sizeof(path)) { | |||
| 197 | fatalx("table_create: path too long \"" | |||
| 198 | PATH_LIBEXEC"/usr/local/libexec/smtpd""/table-%s\"", backend); | |||
| 199 | } | |||
| 200 | if (stat(path, &sb) == 0) { | |||
| 201 | tb = table_backend_lookup("proc"); | |||
| 202 | (void)strlcpy(path, backend, sizeof(path)); | |||
| 203 | if (config) { | |||
| 204 | (void)strlcat(path, ":", sizeof(path)); | |||
| 205 | if (strlcat(path, config, sizeof(path)) | |||
| 206 | >= sizeof(path)) | |||
| 207 | fatalx("table_create: config file path too long"); | |||
| 208 | } | |||
| 209 | config = path; | |||
| 210 | } | |||
| 211 | } | |||
| 212 | ||||
| 213 | if (tb == NULL((void *)0)) | |||
| 214 | fatalx("table_create: backend \"%s\" does not exist", backend); | |||
| 215 | ||||
| 216 | t = xcalloc(1, sizeof(*t)); | |||
| 217 | t->t_backend = tb; | |||
| 218 | ||||
| 219 | if (config) { | |||
| 220 | if (strlcpy(t->t_config, config, sizeof t->t_config) | |||
| 221 | >= sizeof t->t_config) | |||
| 222 | fatalx("table_create: table config \"%s\" too large", | |||
| 223 | t->t_config); | |||
| 224 | } | |||
| 225 | ||||
| 226 | if (strcmp(tb->name, "static") != 0) | |||
| 227 | t->t_type = T_DYNAMIC; | |||
| 228 | ||||
| 229 | if (name == NULL((void *)0)) | |||
| 230 | (void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>", | |||
| 231 | last_table_id++); | |||
| 232 | else { | |||
| 233 | n = strlcpy(t->t_name, name, sizeof(t->t_name)); | |||
| 234 | if (n >= sizeof(t->t_name)) | |||
| 235 | fatalx("table_create: table name too long"); | |||
| 236 | } | |||
| 237 | ||||
| 238 | dict_set(conf->sc_tables_dict, t->t_name, t); | |||
| 239 | ||||
| 240 | return (t); | |||
| 241 | } | |||
| 242 | ||||
| 243 | void | |||
| 244 | table_destroy(struct smtpd *conf, struct table *t) | |||
| 245 | { | |||
| 246 | dict_xpop(conf->sc_tables_dict, t->t_name); | |||
| 247 | free(t); | |||
| 248 | } | |||
| 249 | ||||
| 250 | int | |||
| 251 | table_config(struct table *t) | |||
| 252 | { | |||
| 253 | if (t->t_backend->config == NULL((void *)0)) | |||
| 254 | return (1); | |||
| 255 | return (t->t_backend->config(t)); | |||
| 256 | } | |||
| 257 | ||||
| 258 | void | |||
| 259 | table_add(struct table *t, const char *key, const char *val) | |||
| 260 | { | |||
| 261 | if (t->t_backend->add == NULL((void *)0)) | |||
| 262 | fatalx("table_add: cannot add to table"); | |||
| 263 | ||||
| 264 | if (t->t_backend->add(t, key, val) == 0) | |||
| 265 | log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name); | |||
| 266 | } | |||
| 267 | ||||
| 268 | void | |||
| 269 | table_dump(struct table *t) | |||
| 270 | { | |||
| 271 | const char *type; | |||
| 272 | char buf[LINE_MAX2048]; | |||
| 273 | ||||
| 274 | switch(t->t_type) { | |||
| 275 | case T_NONE: | |||
| 276 | type = "NONE"; | |||
| 277 | break; | |||
| 278 | case T_DYNAMIC: | |||
| 279 | type = "DYNAMIC"; | |||
| 280 | break; | |||
| 281 | case T_LIST: | |||
| 282 | type = "LIST"; | |||
| 283 | break; | |||
| 284 | case T_HASH: | |||
| 285 | type = "HASH"; | |||
| 286 | break; | |||
| 287 | default: | |||
| 288 | type = "???"; | |||
| 289 | break; | |||
| 290 | } | |||
| 291 | ||||
| 292 | if (t->t_config[0]) | |||
| 293 | snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config); | |||
| 294 | else | |||
| 295 | buf[0] = '\0'; | |||
| 296 | ||||
| 297 | log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name, | |||
| 298 | t->t_backend->name, type, buf); | |||
| 299 | ||||
| 300 | if (t->t_backend->dump) | |||
| 301 | t->t_backend->dump(t); | |||
| 302 | } | |||
| 303 | ||||
| 304 | int | |||
| 305 | table_check_type(struct table *t, uint32_t mask) | |||
| 306 | { | |||
| 307 | return t->t_type & mask; | |||
| 308 | } | |||
| 309 | ||||
| 310 | int | |||
| 311 | table_check_service(struct table *t, uint32_t mask) | |||
| 312 | { | |||
| 313 | return t->t_backend->services & mask; | |||
| 314 | } | |||
| 315 | ||||
| 316 | int | |||
| 317 | table_check_use(struct table *t, uint32_t tmask, uint32_t smask) | |||
| 318 | { | |||
| 319 | return table_check_type(t, tmask) && table_check_service(t, smask); | |||
| 320 | } | |||
| 321 | ||||
| 322 | int | |||
| 323 | table_open(struct table *t) | |||
| 324 | { | |||
| 325 | if (t->t_backend->open == NULL((void *)0)) | |||
| 326 | return (1); | |||
| 327 | return (t->t_backend->open(t)); | |||
| 328 | } | |||
| 329 | ||||
| 330 | void | |||
| 331 | table_close(struct table *t) | |||
| 332 | { | |||
| 333 | if (t->t_backend->close) | |||
| 334 | t->t_backend->close(t); | |||
| 335 | } | |||
| 336 | ||||
| 337 | int | |||
| 338 | table_update(struct table *t) | |||
| 339 | { | |||
| 340 | if (t->t_backend->update == NULL((void *)0)) | |||
| 341 | return (1); | |||
| 342 | return (t->t_backend->update(t)); | |||
| 343 | } | |||
| 344 | ||||
| 345 | ||||
| 346 | /* | |||
| 347 | * quick reminder: | |||
| 348 | * in *_match() s1 comes from session, s2 comes from table | |||
| 349 | */ | |||
| 350 | ||||
| 351 | int | |||
| 352 | table_domain_match(const char *s1, const char *s2) | |||
| 353 | { | |||
| 354 | return hostname_match(s1, s2); | |||
| 355 | } | |||
| 356 | ||||
| 357 | int | |||
| 358 | table_mailaddr_match(const char *s1, const char *s2) | |||
| 359 | { | |||
| 360 | struct mailaddr m1; | |||
| 361 | struct mailaddr m2; | |||
| 362 | ||||
| 363 | if (!text_to_mailaddr(&m1, s1)) | |||
| 364 | return 0; | |||
| 365 | if (!text_to_mailaddr(&m2, s2)) | |||
| 366 | return 0; | |||
| 367 | return mailaddr_match(&m1, &m2); | |||
| 368 | } | |||
| 369 | ||||
| 370 | static int table_match_mask(struct sockaddr_storage *, struct netaddr *); | |||
| 371 | static int table_inet4_match(struct sockaddr_in *, struct netaddr *); | |||
| 372 | static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *); | |||
| 373 | ||||
| 374 | int | |||
| 375 | table_netaddr_match(const char *s1, const char *s2) | |||
| 376 | { | |||
| 377 | struct netaddr n1; | |||
| 378 | struct netaddr n2; | |||
| 379 | ||||
| 380 | if (strcasecmp(s1, s2) == 0) | |||
| 381 | return 1; | |||
| 382 | if (!text_to_netaddr(&n1, s1)) | |||
| 383 | return 0; | |||
| 384 | if (!text_to_netaddr(&n2, s2)) | |||
| 385 | return 0; | |||
| 386 | if (n1.ss.ss_family != n2.ss.ss_family) | |||
| 387 | return 0; | |||
| 388 | if (n1.ss.ss_len != n2.ss.ss_len) | |||
| 389 | return 0; | |||
| 390 | return table_match_mask(&n1.ss, &n2); | |||
| 391 | } | |||
| 392 | ||||
| 393 | static int | |||
| 394 | table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask) | |||
| 395 | { | |||
| 396 | if (ss->ss_family == AF_INET2) | |||
| 397 | return table_inet4_match((struct sockaddr_in *)ss, ssmask); | |||
| 398 | ||||
| 399 | if (ss->ss_family == AF_INET624) | |||
| 400 | return table_inet6_match((struct sockaddr_in6 *)ss, ssmask); | |||
| 401 | ||||
| 402 | return (0); | |||
| 403 | } | |||
| 404 | ||||
| 405 | static int | |||
| 406 | table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask) | |||
| 407 | { | |||
| 408 | in_addr_t mask; | |||
| 409 | int i; | |||
| 410 | ||||
| 411 | /* a.b.c.d/8 -> htonl(0xff000000) */ | |||
| 412 | mask = 0; | |||
| 413 | for (i = 0; i < ssmask->bits; ++i) | |||
| 414 | mask = (mask >> 1) | 0x80000000; | |||
| 415 | mask = htonl(mask)(__uint32_t)(__builtin_constant_p(mask) ? (__uint32_t)(((__uint32_t )(mask) & 0xff) << 24 | ((__uint32_t)(mask) & 0xff00 ) << 8 | ((__uint32_t)(mask) & 0xff0000) >> 8 | ((__uint32_t)(mask) & 0xff000000) >> 24) : __swap32md (mask)); | |||
| 416 | ||||
| 417 | /* (addr & mask) == (net & mask) */ | |||
| 418 | if ((ss->sin_addr.s_addr & mask) == | |||
| 419 | (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask)) | |||
| 420 | return 1; | |||
| 421 | ||||
| 422 | return 0; | |||
| 423 | } | |||
| 424 | ||||
| 425 | static int | |||
| 426 | table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask) | |||
| 427 | { | |||
| 428 | struct in6_addr *in; | |||
| 429 | struct in6_addr *inmask; | |||
| 430 | struct in6_addr mask; | |||
| 431 | int i; | |||
| 432 | ||||
| 433 | memset(&mask, 0, sizeof(mask)); | |||
| 434 | for (i = 0; i < ssmask->bits / 8; i++) | |||
| 435 | mask.s6_addr__u6_addr.__u6_addr8[i] = 0xff; | |||
| 436 | i = ssmask->bits % 8; | |||
| 437 | if (i) | |||
| 438 | mask.s6_addr__u6_addr.__u6_addr8[ssmask->bits / 8] = 0xff00 >> i; | |||
| 439 | ||||
| 440 | in = &ss->sin6_addr; | |||
| 441 | inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr; | |||
| 442 | ||||
| 443 | for (i = 0; i < 16; i++) { | |||
| 444 | if ((in->s6_addr__u6_addr.__u6_addr8[i] & mask.s6_addr__u6_addr.__u6_addr8[i]) != | |||
| 445 | (inmask->s6_addr__u6_addr.__u6_addr8[i] & mask.s6_addr__u6_addr.__u6_addr8[i])) | |||
| 446 | return (0); | |||
| 447 | } | |||
| 448 | ||||
| 449 | return (1); | |||
| 450 | } | |||
| 451 | ||||
| 452 | int | |||
| 453 | table_regex_match(const char *string, const char *pattern) | |||
| 454 | { | |||
| 455 | regex_t preg; | |||
| 456 | int cflags = REG_EXTENDED0001|REG_NOSUB0004; | |||
| 457 | int ret; | |||
| 458 | ||||
| 459 | if (strncmp(pattern, "(?i)", 4) == 0) { | |||
| 460 | cflags |= REG_ICASE0002; | |||
| 461 | pattern += 4; | |||
| 462 | } | |||
| 463 | ||||
| 464 | if (regcomp(&preg, pattern, cflags) != 0) | |||
| 465 | return (0); | |||
| 466 | ||||
| 467 | ret = regexec(&preg, string, 0, NULL((void *)0), 0); | |||
| 468 | ||||
| 469 | regfree(&preg); | |||
| 470 | ||||
| 471 | if (ret != 0) | |||
| 472 | return (0); | |||
| 473 | ||||
| 474 | return (1); | |||
| 475 | } | |||
| 476 | ||||
| 477 | void | |||
| 478 | table_dump_all(struct smtpd *conf) | |||
| 479 | { | |||
| 480 | struct table *t; | |||
| 481 | void *iter; | |||
| 482 | ||||
| 483 | iter = NULL((void *)0); | |||
| 484 | while (dict_iter(conf->sc_tables_dict, &iter, NULL((void *)0), (void **)&t)) | |||
| 485 | table_dump(t); | |||
| 486 | } | |||
| 487 | ||||
| 488 | void | |||
| 489 | table_open_all(struct smtpd *conf) | |||
| 490 | { | |||
| 491 | struct table *t; | |||
| 492 | void *iter; | |||
| 493 | ||||
| 494 | iter = NULL((void *)0); | |||
| 495 | while (dict_iter(conf->sc_tables_dict, &iter, NULL((void *)0), (void **)&t)) | |||
| 496 | if (!table_open(t)) | |||
| 497 | fatalx("failed to open table %s", t->t_name); | |||
| 498 | } | |||
| 499 | ||||
| 500 | void | |||
| 501 | table_close_all(struct smtpd *conf) | |||
| 502 | { | |||
| 503 | struct table *t; | |||
| 504 | void *iter; | |||
| 505 | ||||
| 506 | iter = NULL((void *)0); | |||
| 507 | while (dict_iter(conf->sc_tables_dict, &iter, NULL((void *)0), (void **)&t)) | |||
| 508 | table_close(t); | |||
| 509 | } | |||
| 510 | ||||
| 511 | static int | |||
| 512 | table_parse_lookup(enum table_service service, const char *key, | |||
| 513 | const char *line, union lookup *lk) | |||
| 514 | { | |||
| 515 | char buffer[LINE_MAX2048], *p; | |||
| 516 | size_t len; | |||
| 517 | ||||
| 518 | len = strlen(line); | |||
| 519 | ||||
| 520 | switch (service) { | |||
| 521 | case K_ALIAS: | |||
| 522 | lk->expand = calloc(1, sizeof(*lk->expand)); | |||
| 523 | if (lk->expand == NULL((void *)0)) | |||
| 524 | return (-1); | |||
| 525 | if (!expand_line(lk->expand, line, 1)) { | |||
| 526 | expand_free(lk->expand); | |||
| 527 | return (-1); | |||
| 528 | } | |||
| 529 | return (1); | |||
| 530 | ||||
| 531 | case K_DOMAIN: | |||
| 532 | if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name)) | |||
| 533 | >= sizeof(lk->domain.name)) | |||
| 534 | return (-1); | |||
| 535 | return (1); | |||
| 536 | ||||
| 537 | case K_CREDENTIALS: | |||
| 538 | ||||
| 539 | /* credentials are stored as user:password */ | |||
| 540 | if (len < 3) | |||
| 541 | return (-1); | |||
| 542 | ||||
| 543 | /* too big to fit in a smtp session line */ | |||
| 544 | if (len >= LINE_MAX2048) | |||
| 545 | return (-1); | |||
| 546 | ||||
| 547 | p = strchr(line, ':'); | |||
| 548 | if (p == NULL((void *)0)) { | |||
| 549 | if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username)) | |||
| ||||
| 550 | >= sizeof (lk->creds.username)) | |||
| 551 | return (-1); | |||
| 552 | if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password)) | |||
| 553 | >= sizeof(lk->creds.password)) | |||
| 554 | return (-1); | |||
| 555 | return (1); | |||
| 556 | } | |||
| 557 | ||||
| 558 | if (p == line || p == line + len - 1) | |||
| 559 | return (-1); | |||
| 560 | ||||
| 561 | memmove(lk->creds.username, line, p - line); | |||
| 562 | lk->creds.username[p - line] = '\0'; | |||
| 563 | ||||
| 564 | if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password)) | |||
| 565 | >= sizeof(lk->creds.password)) | |||
| 566 | return (-1); | |||
| 567 | ||||
| 568 | return (1); | |||
| 569 | ||||
| 570 | case K_NETADDR: | |||
| 571 | if (!text_to_netaddr(&lk->netaddr, line)) | |||
| 572 | return (-1); | |||
| 573 | return (1); | |||
| 574 | ||||
| 575 | case K_USERINFO: | |||
| 576 | if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line)) | |||
| 577 | return (-1); | |||
| 578 | if (!text_to_userinfo(&lk->userinfo, buffer)) | |||
| 579 | return (-1); | |||
| 580 | return (1); | |||
| 581 | ||||
| 582 | case K_SOURCE: | |||
| 583 | if (parse_sockaddr((struct sockaddr *)&lk->source.addr, | |||
| 584 | PF_UNSPEC0, line) == -1) | |||
| 585 | return (-1); | |||
| 586 | return (1); | |||
| 587 | ||||
| 588 | case K_MAILADDR: | |||
| 589 | if (!text_to_mailaddr(&lk->mailaddr, line)) | |||
| 590 | return (-1); | |||
| 591 | return (1); | |||
| 592 | ||||
| 593 | case K_MAILADDRMAP: | |||
| 594 | lk->maddrmap = calloc(1, sizeof(*lk->maddrmap)); | |||
| 595 | if (lk->maddrmap == NULL((void *)0)) | |||
| 596 | return (-1); | |||
| 597 | maddrmap_init(lk->maddrmap); | |||
| 598 | if (!mailaddr_line(lk->maddrmap, line)) { | |||
| 599 | maddrmap_free(lk->maddrmap); | |||
| 600 | return (-1); | |||
| 601 | } | |||
| 602 | return (1); | |||
| 603 | ||||
| 604 | case K_ADDRNAME: | |||
| 605 | if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr, | |||
| 606 | PF_UNSPEC0, key) == -1) | |||
| 607 | return (-1); | |||
| 608 | if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name)) | |||
| 609 | >= sizeof(lk->addrname.name)) | |||
| 610 | return (-1); | |||
| 611 | return (1); | |||
| 612 | ||||
| 613 | case K_RELAYHOST: | |||
| 614 | if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost)) | |||
| 615 | >= sizeof(lk->relayhost)) | |||
| 616 | return (-1); | |||
| 617 | return (1); | |||
| 618 | ||||
| 619 | default: | |||
| 620 | return (-1); | |||
| 621 | } | |||
| 622 | } | |||
| 623 | ||||
| 624 | static int | |||
| 625 | parse_sockaddr(struct sockaddr *sa, int family, const char *str) | |||
| 626 | { | |||
| 627 | struct in_addr ina; | |||
| 628 | struct in6_addr in6a; | |||
| 629 | struct sockaddr_in *sin; | |||
| 630 | struct sockaddr_in6 *sin6; | |||
| 631 | char *cp, *str2; | |||
| 632 | const char *errstr; | |||
| 633 | ||||
| 634 | switch (family) { | |||
| 635 | case PF_UNSPEC0: | |||
| 636 | if (parse_sockaddr(sa, PF_INET2, str) == 0) | |||
| 637 | return (0); | |||
| 638 | return parse_sockaddr(sa, PF_INET624, str); | |||
| 639 | ||||
| 640 | case PF_INET2: | |||
| 641 | if (inet_pton(PF_INET2, str, &ina) != 1) | |||
| 642 | return (-1); | |||
| 643 | ||||
| 644 | sin = (struct sockaddr_in *)sa; | |||
| 645 | memset(sin, 0, sizeof *sin); | |||
| 646 | sin->sin_len = sizeof(struct sockaddr_in); | |||
| 647 | sin->sin_family = PF_INET2; | |||
| 648 | sin->sin_addr.s_addr = ina.s_addr; | |||
| 649 | return (0); | |||
| 650 | ||||
| 651 | case PF_INET624: | |||
| 652 | if (strncasecmp("ipv6:", str, 5) == 0) | |||
| 653 | str += 5; | |||
| 654 | cp = strchr(str, SCOPE_DELIMITER'%'); | |||
| 655 | if (cp) { | |||
| 656 | str2 = strdup(str); | |||
| 657 | if (str2 == NULL((void *)0)) | |||
| 658 | return (-1); | |||
| 659 | str2[cp - str] = '\0'; | |||
| 660 | if (inet_pton(PF_INET624, str2, &in6a) != 1) { | |||
| 661 | free(str2); | |||
| 662 | return (-1); | |||
| 663 | } | |||
| 664 | cp++; | |||
| 665 | free(str2); | |||
| 666 | } else if (inet_pton(PF_INET624, str, &in6a) != 1) | |||
| 667 | return (-1); | |||
| 668 | ||||
| 669 | sin6 = (struct sockaddr_in6 *)sa; | |||
| 670 | memset(sin6, 0, sizeof *sin6); | |||
| 671 | sin6->sin6_len = sizeof(struct sockaddr_in6); | |||
| 672 | sin6->sin6_family = PF_INET624; | |||
| 673 | sin6->sin6_addr = in6a; | |||
| 674 | ||||
| 675 | if (cp == NULL((void *)0)) | |||
| 676 | return (0); | |||
| 677 | ||||
| 678 | if (IN6_IS_ADDR_LINKLOCAL(&in6a)(((&in6a)->__u6_addr.__u6_addr8[0] == 0xfe) && (((&in6a)->__u6_addr.__u6_addr8[1] & 0xc0) == 0x80 )) || | |||
| 679 | IN6_IS_ADDR_MC_LINKLOCAL(&in6a)(((&in6a)->__u6_addr.__u6_addr8[0] == 0xff) && (((&in6a)->__u6_addr.__u6_addr8[1] & 0x0f) == 0x02 )) || | |||
| 680 | IN6_IS_ADDR_MC_INTFACELOCAL(&in6a)(((&in6a)->__u6_addr.__u6_addr8[0] == 0xff) && (((&in6a)->__u6_addr.__u6_addr8[1] & 0x0f) == 0x01 ))) | |||
| 681 | if ((sin6->sin6_scope_id = if_nametoindex(cp))) | |||
| 682 | return (0); | |||
| 683 | ||||
| 684 | sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX0xffffffffU, &errstr); | |||
| 685 | if (errstr) | |||
| 686 | return (-1); | |||
| 687 | return (0); | |||
| 688 | ||||
| 689 | default: | |||
| 690 | break; | |||
| 691 | } | |||
| 692 | ||||
| 693 | return (-1); | |||
| 694 | } |