| 1 |  |  | /*	$OpenBSD: expand.c,v 1.29 2015/12/28 22:08:30 jung 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); |