1 /* $Id: macro.c,v 1.10 2013/01/19 16:01:15 manu Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Emmanuel Dreyfus
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *        This product includes software developed by Emmanuel Dreyfus
18  *
19  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
20  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "config.h"
33 
34 #ifdef HAVE_SYS_CDEFS_H
35 #include <sys/cdefs.h>
36 #ifdef __RCSID
37 __RCSID("$Id: macro.c,v 1.10 2013/01/19 16:01:15 manu Exp $");
38 #endif
39 #endif
40 
41 #include <sys/types.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <syslog.h>
46 #include <errno.h>
47 #include <sysexits.h>
48 #include <regex.h>
49 
50 #if defined(HAVE_OLD_QUEUE_H) || !defined(HAVE_SYS_QUEUE_H)
51 #include "queue.h"
52 #else
53 #include <sys/queue.h>
54 #endif
55 
56 #include "milter-greylist.h"
57 #include "pending.h"
58 #include "conf.h"
59 #include "spf.h"
60 #include "macro.h"
61 
62 #ifdef USE_DMALLOC
63 #include <dmalloc.h>
64 #endif
65 
66 /*
67  * locking is done through the same lock as acllist: both are static
68  * configuration, which are readen or changed at the same times.
69  */
70 struct macrolist macro_head;
71 
72 void
macro_init(void)73 macro_init(void) {
74 	LIST_INIT(&macro_head);
75 	return;
76 }
77 
78 int
macro_check(ad,stage,ap,priv)79 macro_check(ad, stage, ap, priv)
80 	acl_data_t *ad;
81 	acl_stage_t stage;
82 	struct acl_param *ap;
83 	struct mlfi_priv *priv;
84 {
85 	SMFICTX *ctx;
86 	struct macro_entry *me;
87 	char *value;
88 	int retval = 0;
89 
90 	ctx = priv->priv_ctx;
91 	me = ad->macro;
92 
93 	value = smfi_getsymval(ctx, me->m_macro);
94 
95 	switch (me->m_type) {
96 	case M_UNSET:
97 		if (value == NULL)
98 			retval = 1;
99 		break;
100 	case M_STRING:
101 		if (value != NULL && strcmp(value, me->m_string) == 0)
102 			retval = 1;
103 		break;
104 	case M_REGEX:
105 		if (value != NULL &&
106 		    regexec(me->m_regex, value, 0, NULL, 0) == 0)
107 			retval = 1;
108 		break;
109 	default:
110 		mg_log(LOG_ERR, "unexpecte me->m_type = %d", me->m_type);
111 		exit(EX_SOFTWARE);
112 		break;
113 	}
114 
115 	if (conf.c_debug) {
116 		mg_log(LOG_DEBUG, "sm_macro \"%s\" %s=%s %s", me->m_name,
117 		    me->m_macro, value ? value : "(null)",
118 		    retval ? "match" : "nomatch");
119 	}
120 
121 	return retval;
122 }
123 
124 
125 void
macro_add_unset(name,macro)126 macro_add_unset(name, macro)
127 	char *name;
128 	char *macro;
129 {
130 	struct macro_entry *me;
131 
132 	if (macro_byname(name) != NULL) {
133 		mg_log(LOG_ERR, "macro \"%s\" defined twice at line %d",
134 		    name, conf_line - 1);
135 		exit(EX_DATAERR);
136 	}
137 
138 	if ((me = malloc(sizeof(*me))) == NULL) {
139 		mg_log(LOG_ERR, "malloc failed: %s", strerror(errno));
140 		exit(EX_OSERR);
141 	}
142 
143 	me->m_type = M_UNSET;
144 	if ((me->m_name = strdup(name)) == NULL) {
145 		mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
146 		exit(EX_OSERR);
147 	}
148 	if ((me->m_macro = strdup(macro)) == NULL) {
149 		mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
150 		exit(EX_OSERR);
151 	}
152 
153 	me->m_string = NULL;
154 
155 	LIST_INSERT_HEAD(&macro_head, me, m_list);
156 
157 	if (conf.c_debug || conf.c_acldebug) {
158 		mg_log(LOG_DEBUG, "load sm_macro \"%s\" \"%s\" unset",
159 		    me->m_name, me->m_macro);
160 	}
161 
162 	return;
163 }
164 
165 void
macro_add_string(name,macro,string)166 macro_add_string(name, macro, string)
167 	char *name;
168 	char *macro;
169 	char *string;
170 {
171 	struct macro_entry *me;
172 
173 	if ((me = malloc(sizeof(*me))) == NULL) {
174 		mg_log(LOG_ERR, "malloc failed: %s", strerror(errno));
175 		exit(EX_OSERR);
176 	}
177 
178 	me->m_type = M_STRING;
179 	if ((me->m_name = strdup(name)) == NULL) {
180 		mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
181 		exit(EX_OSERR);
182 	}
183 	if ((me->m_macro = strdup(macro)) == NULL) {
184 		mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
185 		exit(EX_OSERR);
186 	}
187 
188 	if ((me->m_string = strdup(string)) == NULL) {
189 		mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
190 		exit(EX_OSERR);
191 	}
192 
193 	LIST_INSERT_HEAD(&macro_head, me, m_list);
194 
195 	if (conf.c_debug || conf.c_acldebug) {
196 		mg_log(LOG_DEBUG, "load sm_macro \"%s\" \"%s\" \"%s\"",
197 		    me->m_name, me->m_macro, me->m_string);
198 	}
199 
200 	return;
201 }
202 
203 #define ERRLEN 1024
204 void
macro_add_regex(name,macro,regex)205 macro_add_regex(name, macro, regex)
206 	char *name;
207 	char *macro;
208 	char *regex;
209 {
210 	struct macro_entry *me;
211 	char errstr[ERRLEN + 1];
212 	int error;
213 	size_t len;
214 
215 	/* Strip slashes */
216 	len = strlen(regex);
217 	if (len > 0)
218 		regex[len - 1] = '\0';
219 	regex++;
220 
221 	if ((me = malloc(sizeof(*me))) == NULL) {
222 		mg_log(LOG_ERR, "malloc failed: %s", strerror(errno));
223 		exit(EX_OSERR);
224 	}
225 
226 	me->m_type = M_REGEX;
227 	if ((me->m_name = strdup(name)) == NULL) {
228 		mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
229 		exit(EX_OSERR);
230 	}
231 	if ((me->m_macro = strdup(macro)) == NULL) {
232 		mg_log(LOG_ERR, "strdup failed: %s", strerror(errno));
233 		exit(EX_OSERR);
234 	}
235 
236 	if ((me->m_regex = malloc(sizeof(*me->m_regex))) == NULL) {
237 		mg_log(LOG_ERR, "malloc failed: %s", strerror(errno));
238 		exit(EX_OSERR);
239 	}
240 
241         if ((error = regcomp(me->m_regex, regex,
242 	    (conf.c_extendedregex ? REG_EXTENDED : 0) | REG_ICASE)) != 0) {
243 		regerror(error, me->m_regex, errstr, ERRLEN);
244 		mg_log(LOG_ERR, "bad regular expression \"%s\": %s",
245 		    regex, errstr);
246 		exit(EX_OSERR);
247 	}
248 
249 	LIST_INSERT_HEAD(&macro_head, me, m_list);
250 
251 	if (conf.c_debug || conf.c_acldebug) {
252 		mg_log(LOG_DEBUG, "load sm_macro \"%s\" \"%s\" /%s/",
253 		    me->m_name, me->m_macro, regex);
254 	}
255 	return;
256 }
257 
258 struct macro_entry *
macro_byname(macro)259 macro_byname(macro)	/* acllist must be read locked */
260 	char *macro;
261 {
262 	struct macro_entry *me;
263 
264 	LIST_FOREACH(me, &macro_head, m_list) {
265 		if (strcmp(me->m_name, macro) == 0)
266 			break;
267 	}
268 
269 	return me;
270 }
271 
272 void
macro_clear(void)273 macro_clear(void)	/* acllist must be write locked */
274 {
275 	struct macro_entry *me;
276 
277 	while(!LIST_EMPTY(&macro_head)) {
278 		me = LIST_FIRST(&macro_head);
279 
280 		LIST_REMOVE(me, m_list);
281 
282 		free(me->m_name);
283 		free(me->m_macro);
284 
285 		switch (me->m_type) {
286 		case M_UNSET:
287 			break;
288 		case M_STRING:
289 			free(me->m_string);
290 			break;
291 		case M_REGEX:
292 			regfree(me->m_regex);
293 			free(me->m_regex);
294 			break;
295 		default:
296 			mg_log(LOG_ERR,
297 			    "unexpecte me->m_type = %d", me->m_type);
298 			exit(EX_SOFTWARE);
299 		}
300 	}
301 
302 	macro_init();
303 	return;
304 }
305 
306