1 /* $OpenBSD: aliases.c,v 1.43 2011/05/16 21:05:51 gilles Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/tree.h> 22 #include <sys/param.h> 23 #include <sys/socket.h> 24 25 #include <ctype.h> 26 #include <event.h> 27 #include <imsg.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <util.h> 32 33 #include "smtpd.h" 34 #include "log.h" 35 36 static int aliases_expand_include(struct expandtree *, char *); 37 static int alias_is_filter(struct expandnode *, char *, size_t); 38 static int alias_is_username(struct expandnode *, char *, size_t); 39 static int alias_is_address(struct expandnode *, char *, size_t); 40 static int alias_is_filename(struct expandnode *, char *, size_t); 41 static int alias_is_include(struct expandnode *, char *, size_t); 42 43 int 44 aliases_exist(objid_t mapid, char *username) 45 { 46 struct map *map; 47 struct map_alias *map_alias; 48 char buf[MAX_LOCALPART_SIZE]; 49 50 map = map_find(mapid); 51 if (map == NULL) 52 return 0; 53 54 lowercase(buf, username, sizeof(buf)); 55 map_alias = map_lookup(mapid, buf, K_ALIAS); 56 if (map_alias == NULL) 57 return 0; 58 59 /* XXX - for now the map API always allocate */ 60 log_debug("aliases_exist: '%s' exists with %zd expansion nodes", 61 username, map_alias->nbnodes); 62 63 expandtree_free_nodes(&map_alias->expandtree); 64 free(map_alias); 65 66 return 1; 67 } 68 69 int 70 aliases_get(objid_t mapid, struct expandtree *expandtree, char *username) 71 { 72 struct map *map; 73 struct map_alias *map_alias; 74 struct expandnode *expnode; 75 char buf[MAX_LOCALPART_SIZE]; 76 size_t nbaliases; 77 78 map = map_find(mapid); 79 if (map == NULL) 80 return 0; 81 82 lowercase(buf, username, sizeof(buf)); 83 map_alias = map_lookup(mapid, buf, K_ALIAS); 84 if (map_alias == NULL) 85 return 0; 86 87 /* foreach node in map_alias expandtree, we merge */ 88 nbaliases = 0; 89 RB_FOREACH(expnode, expandtree, &map_alias->expandtree) { 90 if (expnode->type == EXPAND_INCLUDE) 91 nbaliases += aliases_expand_include(expandtree, expnode->u.buffer); 92 else { 93 expandtree_increment_node(expandtree, expnode); 94 nbaliases++; 95 } 96 } 97 98 expandtree_free_nodes(&map_alias->expandtree); 99 free(map_alias); 100 101 log_debug("aliases_get: returned %zd aliases", nbaliases); 102 return nbaliases; 103 } 104 105 int 106 aliases_vdomain_exists(objid_t mapid, char *hostname) 107 { 108 struct map *map; 109 struct map_virtual *map_virtual; 110 char buf[MAXHOSTNAMELEN]; 111 112 map = map_find(mapid); 113 if (map == NULL) 114 return 0; 115 116 lowercase(buf, hostname, sizeof(buf)); 117 map_virtual = map_lookup(mapid, buf, K_VIRTUAL); 118 if (map_virtual == NULL) 119 return 0; 120 121 /* XXX - for now the map API always allocate */ 122 log_debug("aliases_vdomain_exist: '%s' exists", hostname); 123 expandtree_free_nodes(&map_virtual->expandtree); 124 free(map_virtual); 125 126 127 return 1; 128 } 129 130 int 131 aliases_virtual_exist(objid_t mapid, struct mailaddr *maddr) 132 { 133 struct map *map; 134 struct map_virtual *map_virtual; 135 char buf[MAX_LINE_SIZE]; 136 char *pbuf = buf; 137 138 map = map_find(mapid); 139 if (map == NULL) 140 return 0; 141 142 if (! bsnprintf(buf, sizeof(buf), "%s@%s", maddr->user, 143 maddr->domain)) 144 return 0; 145 lowercase(buf, buf, sizeof(buf)); 146 147 map_virtual = map_lookup(mapid, buf, K_VIRTUAL); 148 if (map_virtual == NULL) { 149 pbuf = strchr(buf, '@'); 150 map_virtual = map_lookup(mapid, pbuf, K_VIRTUAL); 151 } 152 if (map_virtual == NULL) 153 return 0; 154 155 log_debug("aliases_virtual_exist: '%s' exists", pbuf); 156 expandtree_free_nodes(&map_virtual->expandtree); 157 free(map_virtual); 158 159 return 1; 160 } 161 162 int 163 aliases_virtual_get(objid_t mapid, struct expandtree *expandtree, 164 struct mailaddr *maddr) 165 { 166 struct map *map; 167 struct map_virtual *map_virtual; 168 struct expandnode *expnode; 169 char buf[MAX_LINE_SIZE]; 170 char *pbuf = buf; 171 int nbaliases; 172 173 map = map_find(mapid); 174 if (map == NULL) 175 return 0; 176 177 if (! bsnprintf(buf, sizeof(buf), "%s@%s", maddr->user, 178 maddr->domain)) 179 return 0; 180 lowercase(buf, buf, sizeof(buf)); 181 182 map_virtual = map_lookup(mapid, buf, K_VIRTUAL); 183 if (map_virtual == NULL) { 184 pbuf = strchr(buf, '@'); 185 map_virtual = map_lookup(mapid, pbuf, K_VIRTUAL); 186 } 187 if (map_virtual == NULL) 188 return 0; 189 190 /* foreach node in map_virtual expandtree, we merge */ 191 nbaliases = 0; 192 RB_FOREACH(expnode, expandtree, &map_virtual->expandtree) { 193 if (expnode->type == EXPAND_INCLUDE) 194 nbaliases += aliases_expand_include(expandtree, expnode->u.buffer); 195 else { 196 expandtree_increment_node(expandtree, expnode); 197 nbaliases++; 198 } 199 } 200 201 expandtree_free_nodes(&map_virtual->expandtree); 202 free(map_virtual); 203 log_debug("aliases_virtual_get: '%s' resolved to %d nodes", pbuf, nbaliases); 204 205 return nbaliases; 206 } 207 208 int 209 aliases_expand_include(struct expandtree *expandtree, char *filename) 210 { 211 FILE *fp; 212 char *line; 213 size_t len; 214 size_t lineno = 0; 215 char delim[] = { '\\', '#' }; 216 struct expandnode expnode; 217 218 fp = fopen(filename, "r"); 219 if (fp == NULL) { 220 log_warn("failed to open include file \"%s\".", filename); 221 return 0; 222 } 223 224 while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) { 225 if (len == 0) { 226 free(line); 227 continue; 228 } 229 230 bzero(&expnode, sizeof(struct expandnode)); 231 if (! alias_parse(&expnode, line)) { 232 log_warnx("could not parse include entry \"%s\".", line); 233 } 234 235 if (expnode.type == EXPAND_INCLUDE) 236 log_warnx("nested inclusion is not supported."); 237 else 238 expandtree_increment_node(expandtree, &expnode); 239 240 free(line); 241 } 242 243 fclose(fp); 244 return 1; 245 } 246 247 int 248 alias_parse(struct expandnode *alias, char *line) 249 { 250 size_t i; 251 int (*f[])(struct expandnode *, char *, size_t) = { 252 alias_is_include, 253 alias_is_filter, 254 alias_is_filename, 255 alias_is_address, 256 alias_is_username 257 }; 258 char *wsp; 259 260 /* remove ending whitespaces */ 261 wsp = line + strlen(line); 262 while (wsp != line) { 263 if (*wsp != '\0' && !isspace((int)*wsp)) 264 break; 265 *wsp-- = '\0'; 266 } 267 268 for (i = 0; i < sizeof(f) / sizeof(void *); ++i) { 269 bzero(alias, sizeof(struct expandnode)); 270 if (f[i](alias, line, strlen(line))) 271 break; 272 } 273 if (i == sizeof(f) / sizeof(void *)) 274 return 0; 275 276 return 1; 277 } 278 279 280 int 281 alias_is_filter(struct expandnode *alias, char *line, size_t len) 282 { 283 if (*line == '|') { 284 if (strlcpy(alias->u.buffer, line + 1, 285 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 286 return 0; 287 alias->type = EXPAND_FILTER; 288 return 1; 289 } 290 return 0; 291 } 292 293 int 294 alias_is_username(struct expandnode *alias, char *line, size_t len) 295 { 296 if (strlcpy(alias->u.user, line, 297 sizeof(alias->u.user)) >= sizeof(alias->u.user)) 298 return 0; 299 300 while (*line) { 301 if (!isalnum((int)*line) && 302 *line != '_' && *line != '.' && *line != '-') 303 return 0; 304 ++line; 305 } 306 307 alias->type = EXPAND_USERNAME; 308 return 1; 309 } 310 311 int 312 alias_is_address(struct expandnode *alias, char *line, size_t len) 313 { 314 char *domain; 315 316 if (len < 3) /* x@y */ 317 return 0; 318 319 domain = strchr(line, '@'); 320 if (domain == NULL) 321 return 0; 322 323 /* @ cannot start or end an address */ 324 if (domain == line || domain == line + len - 1) 325 return 0; 326 327 /* scan pre @ for disallowed chars */ 328 *domain++ = '\0'; 329 strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user)); 330 strlcpy(alias->u.mailaddr.domain, domain, sizeof(alias->u.mailaddr.domain)); 331 332 while (*line) { 333 char allowedset[] = "!#$%*/?|^{}`~&'+-=_."; 334 if (!isalnum((int)*line) && 335 strchr(allowedset, *line) == NULL) 336 return 0; 337 ++line; 338 } 339 340 while (*domain) { 341 char allowedset[] = "-."; 342 if (!isalnum((int)*domain) && 343 strchr(allowedset, *domain) == NULL) 344 return 0; 345 ++domain; 346 } 347 348 alias->type = EXPAND_ADDRESS; 349 return 1; 350 } 351 352 int 353 alias_is_filename(struct expandnode *alias, char *line, size_t len) 354 { 355 if (*line != '/') 356 return 0; 357 358 if (strlcpy(alias->u.buffer, line, 359 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 360 return 0; 361 alias->type = EXPAND_FILENAME; 362 return 1; 363 } 364 365 int 366 alias_is_include(struct expandnode *alias, char *line, size_t len) 367 { 368 if (strncasecmp(":include:", line, 9) != 0) 369 return 0; 370 371 if (! alias_is_filename(alias, line + 9, len - 9)) 372 return 0; 373 374 alias->type = EXPAND_INCLUDE; 375 return 1; 376 } 377