1 /* $OpenBSD: expand.c,v 1.28 2015/01/20 17:37:54 deraadt 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->parent = expand->parent; 67 68 log_trace(TRACE_EXPAND, "expand: %p: expand_insert() called for %s", 69 expand, expandnode_info(node)); 70 if (node->type == EXPAND_USERNAME && 71 expand->parent && 72 expand->parent->type == EXPAND_USERNAME && 73 !strcmp(expand->parent->u.user, node->u.user)) { 74 log_trace(TRACE_EXPAND, "expand: %p: setting sameuser = 1", 75 expand); 76 node->sameuser = 1; 77 } 78 79 if (expand_lookup(expand, node)) { 80 log_trace(TRACE_EXPAND, "expand: %p: node found, discarding", 81 expand); 82 return; 83 } 84 85 xn = xmemdup(node, sizeof *xn, "expand_insert"); 86 xn->rule = expand->rule; 87 xn->parent = expand->parent; 88 xn->alias = expand->alias; 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->mapping < e2->mapping) 140 return -1; 141 if (e1->mapping > e2->mapping) 142 return 1; 143 if (e1->userbase < e2->userbase) 144 return -1; 145 if (e1->userbase > e2->userbase) 146 return 1; 147 148 r = memcmp(&e1->u, &e2->u, sizeof(e1->u)); 149 if (r) 150 return (r); 151 152 153 if (e1->parent == e2->parent) 154 return (0); 155 156 if (e1->parent == NULL) 157 return (-1); 158 if (e2->parent == NULL) 159 return (1); 160 161 /* 162 * The same node can be expanded in for different dest context. 163 * Wen need to distinguish between those. 164 */ 165 for(p1 = e1->parent; p1->type != EXPAND_ADDRESS; p1 = p1->parent) 166 ; 167 for(p2 = e2->parent; p2->type != EXPAND_ADDRESS; p2 = p2->parent) 168 ; 169 if (p1 < p2) 170 return (-1); 171 if (p1 > p2) 172 return (1); 173 174 if (e1->type != EXPAND_FILENAME && e1->type != EXPAND_FILTER) 175 return (0); 176 177 /* 178 * For external delivery, we need to distinguish between users. 179 * If we can't find a username, we assume it is _smtpd. 180 */ 181 for(p1 = e1->parent; p1 && p1->type != EXPAND_USERNAME; p1 = p1->parent) 182 ; 183 for(p2 = e2->parent; p2 && p2->type != EXPAND_USERNAME; p2 = p2->parent) 184 ; 185 if (p1 < p2) 186 return (-1); 187 if (p1 > p2) 188 return (1); 189 190 return (0); 191 } 192 193 static int 194 expand_line_split(char **line, char **ret) 195 { 196 static char buffer[LINE_MAX]; 197 int esc, dq, sq; 198 size_t i; 199 char *s; 200 201 memset(buffer, 0, sizeof buffer); 202 esc = dq = sq = 0; 203 i = 0; 204 for (s = *line; (*s) && (i < sizeof(buffer)); ++s) { 205 if (esc) { 206 buffer[i++] = *s; 207 esc = 0; 208 continue; 209 } 210 if (*s == '\\') { 211 esc = 1; 212 continue; 213 } 214 if (*s == ',' && !dq && !sq) { 215 *ret = buffer; 216 *line = s+1; 217 return (1); 218 } 219 220 buffer[i++] = *s; 221 esc = 0; 222 223 if (*s == '"' && !sq) 224 dq ^= 1; 225 if (*s == '\'' && !dq) 226 sq ^= 1; 227 } 228 229 if (esc || dq || sq || i == sizeof(buffer)) 230 return (-1); 231 232 *ret = buffer; 233 *line = s; 234 return (i ? 1 : 0); 235 } 236 237 int 238 expand_line(struct expand *expand, const char *s, int do_includes) 239 { 240 struct expandnode xn; 241 char buffer[LINE_MAX]; 242 char *p, *subrcpt; 243 int ret; 244 245 memset(buffer, 0, sizeof buffer); 246 if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) 247 return 0; 248 249 p = buffer; 250 while ((ret = expand_line_split(&p, &subrcpt)) > 0) { 251 subrcpt = strip(subrcpt); 252 if (subrcpt[0] == '\0') 253 continue; 254 if (! text_to_expandnode(&xn, subrcpt)) 255 return 0; 256 if (! do_includes) 257 if (xn.type == EXPAND_INCLUDE) 258 continue; 259 expand_insert(expand, &xn); 260 } 261 262 if (ret >= 0) 263 return 1; 264 265 /* expand_line_split() returned < 0 */ 266 return 0; 267 } 268 269 static const char * 270 expandnode_info(struct expandnode *e) 271 { 272 static char buffer[1024]; 273 const char *type = NULL; 274 const char *value = NULL; 275 char tmp[64]; 276 277 switch (e->type) { 278 case EXPAND_FILTER: 279 type = "filter"; 280 break; 281 case EXPAND_FILENAME: 282 type = "filename"; 283 break; 284 case EXPAND_INCLUDE: 285 type = "include"; 286 break; 287 case EXPAND_USERNAME: 288 type = "username"; 289 break; 290 case EXPAND_ADDRESS: 291 type = "address"; 292 break; 293 case EXPAND_ERROR: 294 type = "error"; 295 break; 296 case EXPAND_INVALID: 297 default: 298 return NULL; 299 } 300 301 if ((value = expandnode_to_text(e)) == NULL) 302 return NULL; 303 304 (void)strlcpy(buffer, type, sizeof buffer); 305 (void)strlcat(buffer, ":", sizeof buffer); 306 if (strlcat(buffer, value, sizeof buffer) >= sizeof buffer) 307 return NULL; 308 309 (void)snprintf(tmp, sizeof(tmp), "[parent=%p", e->parent); 310 if (strlcat(buffer, tmp, sizeof buffer) >= sizeof buffer) 311 return NULL; 312 313 if (e->mapping) { 314 (void)strlcat(buffer, ", mapping=", sizeof buffer); 315 (void)strlcat(buffer, e->mapping->t_name, sizeof buffer); 316 } 317 318 if (e->userbase) { 319 (void)strlcat(buffer, ", userbase=", sizeof buffer); 320 (void)strlcat(buffer, e->userbase->t_name, sizeof buffer); 321 } 322 323 if (strlcat(buffer, "]", sizeof buffer) >= sizeof buffer) 324 return NULL; 325 326 return buffer; 327 } 328 329 RB_GENERATE(expandtree, expandnode, entry, expand_cmp); 330