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