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(¯o_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(¯o_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(¯o_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(¯o_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, ¯o_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(¯o_head)) {
278 me = LIST_FIRST(¯o_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