1 /* $OpenBSD: expand.c,v 1.32 2021/06/14 17:58:15 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> 5 * Copyright (c) 2012 Eric Faurot <eric@openbsd.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 <stdlib.h> 21 #include <string.h> 22 23 #include "smtpd.h" 24 25 static const char *expandnode_info(struct expandnode *); 26 27 struct expandnode * 28 expand_lookup(struct expand *expand, struct expandnode *key) 29 { 30 return RB_FIND(expandtree, &expand->tree, key); 31 } 32 33 int 34 expand_to_text(struct expand *expand, char *buf, size_t sz) 35 { 36 struct expandnode *xn; 37 38 buf[0] = '\0'; 39 40 RB_FOREACH(xn, expandtree, &expand->tree) { 41 if (buf[0]) 42 (void)strlcat(buf, ", ", sz); 43 if (strlcat(buf, expandnode_to_text(xn), sz) >= sz) 44 return 0; 45 } 46 47 return 1; 48 } 49 50 void 51 expand_insert(struct expand *expand, struct expandnode *node) 52 { 53 struct expandnode *xn; 54 55 node->rule = expand->rule; 56 node->parent = expand->parent; 57 58 log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s", 59 expand, expandnode_info(node)); 60 if (node->type == EXPAND_USERNAME && 61 expand->parent && 62 expand->parent->type == EXPAND_USERNAME && 63 !strcmp(expand->parent->u.user, node->u.user)) { 64 log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1", 65 expand); 66 node->sameuser = 1; 67 } 68 69 if (expand_lookup(expand, node)) { 70 log_trace(TRACE_EXPAND, "expand: %p: node found, discarding", 71 expand); 72 return; 73 } 74 75 xn = xmemdup(node, sizeof *xn); 76 xn->rule = expand->rule; 77 xn->parent = expand->parent; 78 if (xn->parent) 79 xn->depth = xn->parent->depth + 1; 80 else 81 xn->depth = 0; 82 RB_INSERT(expandtree, &expand->tree, xn); 83 if (expand->queue) 84 TAILQ_INSERT_TAIL(expand->queue, xn, tq_entry); 85 expand->nb_nodes++; 86 log_trace(TRACE_EXPAND, "expand: %p: inserted node %p", expand, xn); 87 } 88 89 void 90 expand_clear(struct expand *expand) 91 { 92 struct expandnode *xn; 93 94 log_trace(TRACE_EXPAND, "expand: %p: clearing expand tree", expand); 95 if (expand->queue) 96 while ((xn = TAILQ_FIRST(expand->queue))) 97 TAILQ_REMOVE(expand->queue, xn, tq_entry); 98 99 while ((xn = RB_ROOT(&expand->tree)) != NULL) { 100 RB_REMOVE(expandtree, &expand->tree, xn); 101 free(xn); 102 } 103 } 104 105 void 106 expand_free(struct expand *expand) 107 { 108 expand_clear(expand); 109 110 log_trace(TRACE_EXPAND, "expand: %p: freeing expand tree", expand); 111 free(expand); 112 } 113 114 int 115 expand_cmp(struct expandnode *e1, struct expandnode *e2) 116 { 117 struct expandnode *p1, *p2; 118 int r; 119 120 if (e1->type < e2->type) 121 return -1; 122 if (e1->type > e2->type) 123 return 1; 124 if (e1->sameuser < e2->sameuser) 125 return -1; 126 if (e1->sameuser > e2->sameuser) 127 return 1; 128 if (e1->realuser < e2->realuser) 129 return -1; 130 if (e1->realuser > e2->realuser) 131 return 1; 132 133 r = memcmp(&e1->u, &e2->u, sizeof(e1->u)); 134 if (r) 135 return (r); 136 137 if (e1->parent == e2->parent) 138 return (0); 139 140 if (e1->parent == NULL) 141 return (-1); 142 if (e2->parent == NULL) 143 return (1); 144 145 /* 146 * The same node can be expanded in for different dest context. 147 * Wen need to distinguish between those. 148 */ 149 for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent) 150 ; 151 for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent) 152 ; 153 if (p1 < p2) 154 return (-1); 155 if (p1 > p2) 156 return (1); 157 158 if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER) 159 return (0); 160 161 /* 162 * For external delivery, we need to distinguish between users. 163 * If we can't find a username, we assume it is _smtpd. 164 */ 165 for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent) 166 ; 167 for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent) 168 ; 169 if (p1 < p2) 170 return (-1); 171 if (p1 > p2) 172 return (1); 173 174 return (0); 175 } 176 177 static int 178 expand_line_split(char **line, char **ret) 179 { 180 static char buffer[LINE_MAX]; 181 int esc, dq, sq; 182 size_t i; 183 char *s; 184 185 memset(buffer, 0, sizeof buffer); 186 esc = dq = sq = 0; 187 i = 0; 188 for (s = *line; (*s) && (i < sizeof(buffer)); ++s) { 189 if (esc) { 190 buffer[i++] = *s; 191 esc = 0; 192 continue; 193 } 194 if (*s == '\\') { 195 esc = 1; 196 continue; 197 } 198 if (*s == ',' && !dq && !sq) { 199 *ret = buffer; 200 *line = s+1; 201 return (1); 202 } 203 204 buffer[i++] = *s; 205 esc = 0; 206 207 if (*s == '"' && !sq) 208 dq ^= 1; 209 if (*s == '\'' && !dq) 210 sq ^= 1; 211 } 212 213 if (esc || dq || sq || i == sizeof(buffer)) 214 return (-1); 215 216 *ret = buffer; 217 *line = s; 218 return (i ? 1 : 0); 219 } 220 221 int 222 expand_line(struct expand *expand, const char *s, int do_includes) 223 { 224 struct expandnode xn; 225 char buffer[LINE_MAX]; 226 char *p, *subrcpt; 227 int ret; 228 229 memset(buffer, 0, sizeof buffer); 230 if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) 231 return 0; 232 233 p = buffer; 234 while ((ret = expand_line_split(&p, &subrcpt)) > 0) { 235 subrcpt = strip(subrcpt); 236 if (subrcpt[0] == '\0') 237 continue; 238 if (!text_to_expandnode(&xn, subrcpt)) 239 return 0; 240 if (!do_includes) 241 if (xn.type == EXPAND_INCLUDE) 242 continue; 243 expand_insert(expand, &xn); 244 } 245 246 if (ret >= 0) 247 return 1; 248 249 /* expand_line_split() returned < 0 */ 250 return 0; 251 } 252 253 static const char * 254 expandnode_info(struct expandnode *e) 255 { 256 static char buffer[1024]; 257 const char *type = NULL; 258 const char *value = NULL; 259 char tmp[64]; 260 261 switch (e->type) { 262 case EXPAND_FILTER: 263 type = "filter"; 264 break; 265 case EXPAND_FILENAME: 266 type = "filename"; 267 break; 268 case EXPAND_INCLUDE: 269 type = "include"; 270 break; 271 case EXPAND_USERNAME: 272 type = "username"; 273 break; 274 case EXPAND_ADDRESS: 275 type = "address"; 276 break; 277 case EXPAND_ERROR: 278 type = "error"; 279 break; 280 case EXPAND_INVALID: 281 default: 282 return NULL; 283 } 284 285 if ((value = expandnode_to_text(e)) == NULL) 286 return NULL; 287 288 (void)strlcpy(buffer, type, sizeof buffer); 289 (void)strlcat(buffer, ":", sizeof buffer); 290 if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer) 291 return NULL; 292 293 (void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent); 294 if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) 295 return NULL; 296 297 (void)snprintf(tmp, sizeof(tmp), ", rule=%p", e->rule); 298 if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) 299 return NULL; 300 301 if (e->rule) { 302 (void)snprintf(tmp, sizeof(tmp), ", dispatcher=%p", e->rule->dispatcher); 303 if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) 304 return NULL; 305 } 306 307 if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer) 308 return NULL; 309 310 return buffer; 311 } 312 313 RB_GENERATE(expandtree, expandnode, entry, expand_cmp); 314