1 /* $OpenBSD: aliases.c,v 1.79 2021/06/14 17:58:15 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.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 <stdlib.h> 20 #include <string.h> 21 #include <util.h> 22 23 #include "smtpd.h" 24 #include "log.h" 25 26 static int aliases_expand_include(struct expand *, const char *); 27 28 int 29 aliases_get(struct expand *expand, const char *username) 30 { 31 struct expandnode *xn; 32 char buf[SMTPD_MAXLOCALPARTSIZE]; 33 size_t nbaliases; 34 int ret; 35 union lookup lk; 36 struct dispatcher *dsp; 37 struct table *mapping = NULL; 38 char *pbuf; 39 40 dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher); 41 mapping = table_find(env, dsp->u.local.table_alias); 42 43 xlowercase(buf, username, sizeof(buf)); 44 45 /* first, check if entry has a user-part tag */ 46 pbuf = strchr(buf, *env->sc_subaddressing_delim); 47 if (pbuf) { 48 ret = table_lookup(mapping, K_ALIAS, buf, &lk); 49 if (ret < 0) 50 return (-1); 51 if (ret) 52 goto expand; 53 *pbuf = '\0'; 54 } 55 56 /* no user-part tag, try looking up user */ 57 ret = table_lookup(mapping, K_ALIAS, buf, &lk); 58 if (ret <= 0) 59 return ret; 60 61 expand: 62 /* foreach node in table_alias expandtree, we merge */ 63 nbaliases = 0; 64 RB_FOREACH(xn, expandtree, &lk.expand->tree) { 65 if (xn->type == EXPAND_INCLUDE) 66 nbaliases += aliases_expand_include(expand, 67 xn->u.buffer); 68 else { 69 expand_insert(expand, xn); 70 nbaliases++; 71 } 72 } 73 74 expand_free(lk.expand); 75 76 log_debug("debug: aliases_get: returned %zd aliases", nbaliases); 77 return nbaliases; 78 } 79 80 int 81 aliases_virtual_get(struct expand *expand, const struct mailaddr *maddr) 82 { 83 struct expandnode *xn; 84 union lookup lk; 85 char buf[LINE_MAX]; 86 char user[LINE_MAX]; 87 char tag[LINE_MAX]; 88 char domain[LINE_MAX]; 89 char *pbuf; 90 int nbaliases; 91 int ret; 92 struct dispatcher *dsp; 93 struct table *mapping = NULL; 94 95 dsp = dict_xget(env->sc_dispatchers, expand->rule->dispatcher); 96 mapping = table_find(env, dsp->u.local.table_virtual); 97 98 if (!bsnprintf(user, sizeof(user), "%s", maddr->user)) 99 return 0; 100 if (!bsnprintf(domain, sizeof(domain), "%s", maddr->domain)) 101 return 0; 102 xlowercase(user, user, sizeof(user)); 103 xlowercase(domain, domain, sizeof(domain)); 104 105 memset(tag, '\0', sizeof tag); 106 pbuf = strchr(user, *env->sc_subaddressing_delim); 107 if (pbuf) { 108 if (!bsnprintf(tag, sizeof(tag), "%s", pbuf + 1)) 109 return 0; 110 xlowercase(tag, tag, sizeof(tag)); 111 *pbuf = '\0'; 112 } 113 114 /* first, check if entry has a user-part tag */ 115 if (tag[0]) { 116 if (!bsnprintf(buf, sizeof(buf), "%s%c%s@%s", 117 user, *env->sc_subaddressing_delim, tag, domain)) 118 return 0; 119 ret = table_lookup(mapping, K_ALIAS, buf, &lk); 120 if (ret < 0) 121 return (-1); 122 if (ret) 123 goto expand; 124 } 125 126 /* then, check if entry exists without user-part tag */ 127 if (!bsnprintf(buf, sizeof(buf), "%s@%s", user, domain)) 128 return 0; 129 ret = table_lookup(mapping, K_ALIAS, buf, &lk); 130 if (ret < 0) 131 return (-1); 132 if (ret) 133 goto expand; 134 135 if (tag[0]) { 136 /* Failed ? We lookup for username + user-part tag */ 137 if (!bsnprintf(buf, sizeof(buf), "%s%c%s", 138 user, *env->sc_subaddressing_delim, tag)) 139 return 0; 140 ret = table_lookup(mapping, K_ALIAS, buf, &lk); 141 if (ret < 0) 142 return (-1); 143 if (ret) 144 goto expand; 145 } 146 147 /* Failed ? We lookup for username only */ 148 if (!bsnprintf(buf, sizeof(buf), "%s", user)) 149 return 0; 150 ret = table_lookup(mapping, K_ALIAS, buf, &lk); 151 if (ret < 0) 152 return (-1); 153 if (ret) 154 goto expand; 155 156 /* Do not try catch-all entries if there is no domain */ 157 if (domain[0] == '\0') 158 return 0; 159 160 if (!bsnprintf(buf, sizeof(buf), "@%s", domain)) 161 return 0; 162 /* Failed ? We lookup for catch all for virtual domain */ 163 ret = table_lookup(mapping, K_ALIAS, buf, &lk); 164 if (ret < 0) 165 return (-1); 166 if (ret) 167 goto expand; 168 169 /* Failed ? We lookup for a *global* catch all */ 170 ret = table_lookup(mapping, K_ALIAS, "@", &lk); 171 if (ret <= 0) 172 return (ret); 173 174 expand: 175 /* foreach node in table_virtual expand, we merge */ 176 nbaliases = 0; 177 RB_FOREACH(xn, expandtree, &lk.expand->tree) { 178 if (xn->type == EXPAND_INCLUDE) 179 nbaliases += aliases_expand_include(expand, 180 xn->u.buffer); 181 else { 182 expand_insert(expand, xn); 183 nbaliases++; 184 } 185 } 186 187 expand_free(lk.expand); 188 189 log_debug("debug: aliases_virtual_get: '%s' resolved to %d nodes", 190 buf, nbaliases); 191 192 return nbaliases; 193 } 194 195 static int 196 aliases_expand_include(struct expand *expand, const char *filename) 197 { 198 FILE *fp; 199 char *line; 200 size_t len, lineno = 0; 201 char delim[3] = { '\\', '#', '\0' }; 202 203 fp = fopen(filename, "r"); 204 if (fp == NULL) { 205 log_warn("warn: failed to open include file \"%s\".", filename); 206 return 0; 207 } 208 209 while ((line = fparseln(fp, &len, &lineno, delim, 0)) != NULL) { 210 expand_line(expand, line, 0); 211 free(line); 212 } 213 214 fclose(fp); 215 return 1; 216 } 217