1 /* $OpenBSD: aliases.c,v 1.47 2012/04/21 12:45:05 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_alias *map_alias; 47 char buf[MAX_LOCALPART_SIZE]; 48 49 lowercase(buf, username, sizeof(buf)); 50 map_alias = map_lookup(mapid, buf, K_ALIAS); 51 if (map_alias == NULL) 52 return 0; 53 54 /* XXX - for now the map API always allocate */ 55 log_debug("aliases_exist: '%s' exists with %zd expansion nodes", 56 username, map_alias->nbnodes); 57 58 expandtree_free_nodes(&map_alias->expandtree); 59 free(map_alias); 60 61 return 1; 62 } 63 64 int 65 aliases_get(objid_t mapid, struct expandtree *expandtree, char *username) 66 { 67 struct map_alias *map_alias; 68 struct expandnode *expnode; 69 char buf[MAX_LOCALPART_SIZE]; 70 size_t nbaliases; 71 72 lowercase(buf, username, sizeof(buf)); 73 map_alias = map_lookup(mapid, buf, K_ALIAS); 74 if (map_alias == NULL) 75 return 0; 76 77 /* foreach node in map_alias expandtree, we merge */ 78 nbaliases = 0; 79 RB_FOREACH(expnode, expandtree, &map_alias->expandtree) { 80 strlcpy(expnode->as_user, SMTPD_USER, sizeof (expnode->as_user)); 81 if (expnode->type == EXPAND_INCLUDE) 82 nbaliases += aliases_expand_include(expandtree, expnode->u.buffer); 83 else { 84 expandtree_increment_node(expandtree, expnode); 85 nbaliases++; 86 } 87 } 88 89 expandtree_free_nodes(&map_alias->expandtree); 90 free(map_alias); 91 92 log_debug("aliases_get: returned %zd aliases", nbaliases); 93 return nbaliases; 94 } 95 96 int 97 aliases_vdomain_exists(objid_t mapid, char *hostname) 98 { 99 struct map_virtual *map_virtual; 100 char buf[MAXHOSTNAMELEN]; 101 102 lowercase(buf, hostname, sizeof(buf)); 103 map_virtual = map_lookup(mapid, buf, K_VIRTUAL); 104 if (map_virtual == NULL) 105 return 0; 106 107 /* XXX - for now the map API always allocate */ 108 log_debug("aliases_vdomain_exist: '%s' exists", hostname); 109 expandtree_free_nodes(&map_virtual->expandtree); 110 free(map_virtual); 111 112 return 1; 113 } 114 115 int 116 aliases_virtual_exist(objid_t mapid, struct mailaddr *maddr) 117 { 118 struct map_virtual *map_virtual; 119 char buf[MAX_LINE_SIZE]; 120 char *pbuf = buf; 121 122 if (! bsnprintf(buf, sizeof(buf), "%s@%s", maddr->user, 123 maddr->domain)) 124 return 0; 125 lowercase(buf, buf, sizeof(buf)); 126 127 map_virtual = map_lookup(mapid, buf, K_VIRTUAL); 128 if (map_virtual == NULL) { 129 pbuf = strchr(buf, '@'); 130 map_virtual = map_lookup(mapid, pbuf, K_VIRTUAL); 131 } 132 if (map_virtual == NULL) 133 return 0; 134 135 log_debug("aliases_virtual_exist: '%s' exists", pbuf); 136 expandtree_free_nodes(&map_virtual->expandtree); 137 free(map_virtual); 138 139 return 1; 140 } 141 142 int 143 aliases_virtual_get(objid_t mapid, struct expandtree *expandtree, 144 struct mailaddr *maddr) 145 { 146 struct map_virtual *map_virtual; 147 struct expandnode *expnode; 148 char buf[MAX_LINE_SIZE]; 149 char *pbuf = buf; 150 int nbaliases; 151 152 if (! bsnprintf(buf, sizeof(buf), "%s@%s", maddr->user, 153 maddr->domain)) 154 return 0; 155 lowercase(buf, buf, sizeof(buf)); 156 157 map_virtual = map_lookup(mapid, buf, K_VIRTUAL); 158 if (map_virtual == NULL) { 159 pbuf = strchr(buf, '@'); 160 map_virtual = map_lookup(mapid, pbuf, K_VIRTUAL); 161 } 162 if (map_virtual == NULL) 163 return 0; 164 165 /* foreach node in map_virtual expandtree, we merge */ 166 nbaliases = 0; 167 RB_FOREACH(expnode, expandtree, &map_virtual->expandtree) { 168 strlcpy(expnode->as_user, SMTPD_USER, sizeof (expnode->as_user)); 169 if (expnode->type == EXPAND_INCLUDE) 170 nbaliases += aliases_expand_include(expandtree, expnode->u.buffer); 171 else { 172 expandtree_increment_node(expandtree, expnode); 173 nbaliases++; 174 } 175 } 176 177 expandtree_free_nodes(&map_virtual->expandtree); 178 free(map_virtual); 179 log_debug("aliases_virtual_get: '%s' resolved to %d nodes", pbuf, nbaliases); 180 181 return nbaliases; 182 } 183 184 int 185 aliases_expand_include(struct expandtree *expandtree, char *filename) 186 { 187 FILE *fp; 188 char *line; 189 size_t len; 190 size_t lineno = 0; 191 char delim[] = { '\\', '#' }; 192 struct expandnode expnode; 193 194 fp = fopen(filename, "r"); 195 if (fp == NULL) { 196 log_warn("failed to open include file \"%s\".", filename); 197 return 0; 198 } 199 200 while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) { 201 if (len == 0) { 202 free(line); 203 continue; 204 } 205 206 bzero(&expnode, sizeof(struct expandnode)); 207 if (! alias_parse(&expnode, line)) { 208 log_warnx("could not parse include entry \"%s\".", line); 209 } 210 211 if (expnode.type == EXPAND_INCLUDE) 212 log_warnx("nested inclusion is not supported."); 213 else 214 expandtree_increment_node(expandtree, &expnode); 215 216 free(line); 217 } 218 219 fclose(fp); 220 return 1; 221 } 222 223 int 224 alias_parse(struct expandnode *alias, char *line) 225 { 226 size_t i; 227 int (*f[])(struct expandnode *, char *, size_t) = { 228 alias_is_include, 229 alias_is_filter, 230 alias_is_filename, 231 alias_is_address, 232 alias_is_username 233 }; 234 char *wsp; 235 236 /* remove ending whitespaces */ 237 wsp = line + strlen(line); 238 while (wsp != line) { 239 if (*wsp != '\0' && !isspace((int)*wsp)) 240 break; 241 *wsp-- = '\0'; 242 } 243 244 for (i = 0; i < sizeof(f) / sizeof(void *); ++i) { 245 bzero(alias, sizeof(struct expandnode)); 246 if (f[i](alias, line, strlen(line))) 247 break; 248 } 249 if (i == sizeof(f) / sizeof(void *)) 250 return 0; 251 252 return 1; 253 } 254 255 256 int 257 alias_is_filter(struct expandnode *alias, char *line, size_t len) 258 { 259 if (*line == '|') { 260 if (strlcpy(alias->u.buffer, line + 1, 261 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 262 return 0; 263 alias->type = EXPAND_FILTER; 264 return 1; 265 } 266 return 0; 267 } 268 269 int 270 alias_is_username(struct expandnode *alias, char *line, size_t len) 271 { 272 if (strlcpy(alias->u.user, line, 273 sizeof(alias->u.user)) >= sizeof(alias->u.user)) 274 return 0; 275 276 while (*line) { 277 if (!isalnum((int)*line) && 278 *line != '_' && *line != '.' && *line != '-') 279 return 0; 280 ++line; 281 } 282 283 alias->type = EXPAND_USERNAME; 284 return 1; 285 } 286 287 int 288 alias_is_address(struct expandnode *alias, char *line, size_t len) 289 { 290 char *domain; 291 292 if (len < 3) /* x@y */ 293 return 0; 294 295 domain = strchr(line, '@'); 296 if (domain == NULL) 297 return 0; 298 299 /* @ cannot start or end an address */ 300 if (domain == line || domain == line + len - 1) 301 return 0; 302 303 /* scan pre @ for disallowed chars */ 304 *domain++ = '\0'; 305 strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user)); 306 strlcpy(alias->u.mailaddr.domain, domain, sizeof(alias->u.mailaddr.domain)); 307 308 while (*line) { 309 char allowedset[] = "!#$%*/?|^{}`~&'+-=_."; 310 if (!isalnum((int)*line) && 311 strchr(allowedset, *line) == NULL) 312 return 0; 313 ++line; 314 } 315 316 while (*domain) { 317 char allowedset[] = "-."; 318 if (!isalnum((int)*domain) && 319 strchr(allowedset, *domain) == NULL) 320 return 0; 321 ++domain; 322 } 323 324 alias->type = EXPAND_ADDRESS; 325 return 1; 326 } 327 328 int 329 alias_is_filename(struct expandnode *alias, char *line, size_t len) 330 { 331 if (*line != '/') 332 return 0; 333 334 if (strlcpy(alias->u.buffer, line, 335 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 336 return 0; 337 alias->type = EXPAND_FILENAME; 338 return 1; 339 } 340 341 int 342 alias_is_include(struct expandnode *alias, char *line, size_t len) 343 { 344 size_t skip; 345 346 if (strncasecmp(":include:", line, 9) == 0) 347 skip = 9; 348 else if (strncasecmp("include:", line, 8) == 0) 349 skip = 8; 350 else 351 return 0; 352 353 if (! alias_is_filename(alias, line + skip, len - skip)) 354 return 0; 355 356 alias->type = EXPAND_INCLUDE; 357 return 1; 358 } 359