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