1 /*
2  * libmavis.c
3  * (C)1998-2011 by Marc Huber <Marc.Huber@web.de>
4  *
5  * $Id: libmavis.c,v 1.26 2015/03/14 06:11:27 marc Exp $
6  *
7  */
8 
9 #define __MAVIS_MAIN__
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <stdarg.h>
15 #include <dlfcn.h>
16 #include <sysexits.h>
17 #include "misc/io_sched.h"
18 #include "misc/memops.h"
19 #include "misc/mymd5.h"
20 #include "log.h"
21 #include "misc/base64.h"
22 #include "debug.h"
23 #include "mavis.h"
24 #include "misc/version.h"
25 
26 static const char rcsid[] __attribute__ ((used)) = "$Id: libmavis.c,v 1.26 2015/03/14 06:11:27 marc Exp $";
27 
mavis_method_add(mavis_ctx ** mcx,struct io_context * ioctx,char * path,char * id)28 int mavis_method_add(mavis_ctx ** mcx, struct io_context *ioctx, char *path, char *id)
29 {
30     void *handle;
31     void *(*mn) (void *, struct io_context *, char *);
32 
33     Debug((DEBUG_MAVIS, "+ %s(%s)\n", __func__, path));
34 
35     handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
36 
37     if (!handle) {
38 	Debug((DEBUG_MAVIS, "- %s(%s): dlopen failed.\n", __func__, path));
39 	return -1;
40     }
41 
42     if (!(mn = (void *(*)(void *, struct io_context *, char *))
43 	  dlsym(handle, DLSYM_PREFIX "Mavis_new"))) {
44 	Debug((DEBUG_MAVIS, "- %s(%s): dlsym (%s) failed: %s\n", __func__, path, DLSYM_PREFIX "Mavis_new", dlerror()));
45 	return -1;
46     }
47 
48     if (!*mcx)
49 	*mcx = mn(handle, ioctx, id);
50     else
51 	(*mcx)->append(*mcx, mn(handle, ioctx, id));
52 
53     Debug((DEBUG_MAVIS, "- %s (OK)\n", __func__));
54     return 0;
55 }
56 
mavis_init(mavis_ctx * mcx,char * version)57 int mavis_init(mavis_ctx * mcx, char *version)
58 {
59     int result = MAVIS_INIT_OK;
60 
61     DebugIn(DEBUG_MAVIS);
62 
63     mavis_check_version(version);
64 
65     if (!mcx) {
66 	Debug((DEBUG_MAVIS, "- %s: FATAL: no modules configured\n", __func__));
67 	logmsg("Fatal: No modules configured");
68 	exit(EX_USAGE);
69     }
70 
71     result = mcx->init(mcx);
72     Debug((DEBUG_MAVIS, "- %s = %d\n", __func__, result));
73     return result;
74 }
75 
mavis_drop(mavis_ctx * mcx)76 int mavis_drop(mavis_ctx * mcx)
77 {
78     void *handle = NULL;
79 
80     DebugIn(DEBUG_MAVIS);
81 
82     if (mcx)
83 	handle = mcx->drop(mcx);
84     if (handle)
85 	dlclose(handle);
86 
87     DebugOut(DEBUG_MAVIS);
88     return 0;
89 }
90 
mavis_parse(mavis_ctx * mcx,struct sym * sym,char * id)91 int mavis_parse(mavis_ctx * mcx, struct sym *sym, char *id)
92 {
93     int result = MAVIS_CONF_ERR;
94 
95     DebugIn(DEBUG_MAVIS);
96 
97     if (mcx)
98 	result = mcx->parse(mcx, sym, id);
99     DebugOut(DEBUG_MAVIS);
100     return result;
101 }
102 
mavis_sanitycheck(mavis_ctx * mcx,av_ctx * ac)103 static int mavis_sanitycheck(mavis_ctx * mcx, av_ctx * ac)
104 {
105     if (!mcx) {
106 	av_set(ac, AV_A_RESULT, AV_V_RESULT_ERROR);
107 	av_set(ac, AV_A_COMMENT, "no modules installed");
108 	return -1;
109     }
110     if (ac->arr[AV_A_TYPE] && ((!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_FTP) && ac->arr[AV_A_USER]
111 				&& ac->arr[AV_A_PASSWORD] && ac->arr[AV_A_IPADDR])
112 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_WWW)
113 				   && ac->arr[AV_A_USER] && ac->arr[AV_A_PASSWORD])
114 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_RADIUS)
115 				   && ac->arr[AV_A_USER] && ac->arr[AV_A_PASSWORD])
116 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_TACPLUS)
117 				   && ac->arr[AV_A_USER] && ac->arr[AV_A_TACTYPE])
118 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_LOGIN)
119 				   && ac->arr[AV_A_USER] && ac->arr[AV_A_PASSWORD]
120 				   && ac->arr[AV_A_IPADDR])
121 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_POP3)
122 				   && ac->arr[AV_A_TIMESTAMP] && ac->arr[AV_A_USER]
123 				   && (ac->arr[AV_A_DIGEST] || ac->arr[AV_A_PASSWORD])
124 				   && ac->arr[AV_A_IPADDR])
125 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_TRANSPORT)
126 				   && ac->arr[AV_A_USER])
127 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_CANONICAL)
128 				   && ac->arr[AV_A_USER])
129 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_VIRTUAL)
130 				   && ac->arr[AV_A_USER])
131 			       || (!strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_POP3PATH)
132 				   && ac->arr[AV_A_USER])
133 			       || !strcmp(ac->arr[AV_A_TYPE], AV_V_TYPE_LOGSTATS)))
134 	return 0;
135     av_set(ac, AV_A_RESULT, AV_V_RESULT_ERROR);
136     av_set(ac, AV_A_COMMENT, "invalid request");
137     return -1;
138 }
139 
av_addserial(av_ctx * ac)140 char *av_addserial(av_ctx * ac)
141 {
142     if (!ac->arr[AV_A_SERIAL]) {
143 	u_char u[16];
144 	char b[30];
145 	size_t i, len = (int) sizeof(b);
146 	myMD5_CTX m;
147 	MD5Init(&m);
148 	for (i = 0; i < AV_A_ARRAYSIZE; i++)
149 	    if (ac->arr[i])
150 		MD5Update(&m, (u_char *) ac->arr[i], strlen(ac->arr[i]));
151 	MD5Final(u, &m);
152 	base64enc((char *) u, (size_t) 16, b, &len);
153 	av_set(ac, AV_A_SERIAL, b);
154     }
155     return ac->arr[AV_A_SERIAL];
156 }
157 
mavis_send(mavis_ctx * mcx,av_ctx ** ac)158 int mavis_send(mavis_ctx * mcx, av_ctx ** ac)
159 {
160     int result = MAVIS_IGNORE;
161 
162     DebugIn(DEBUG_MAVIS);
163 
164     if (mcx) {
165 	if (!mavis_sanitycheck(mcx, *ac)) {
166 	    av_addserial(*ac);
167 	    if (!strcmp((*ac)->arr[AV_A_TYPE], AV_V_TYPE_LOGSTATS))
168 		av_set(*ac, AV_A_RESULT, AV_V_RESULT_OK);
169 
170 	    result = mcx->send(mcx, ac);
171 
172 	    if (result == MAVIS_FINAL && !(*ac)->arr[AV_A_RESULT])
173 		av_set(*ac, AV_A_RESULT, AV_V_RESULT_NOTFOUND);
174 	}
175     }
176     Debug((DEBUG_MAVIS, "- %s (%d)\n", __func__, result));
177     return result;
178 }
179 
mavis_cancel(mavis_ctx * mcx,void * app_ctx)180 int mavis_cancel(mavis_ctx * mcx, void *app_ctx)
181 {
182     int result = MAVIS_IGNORE;
183 
184     DebugIn(DEBUG_MAVIS);
185 
186     result = mcx->cancel(mcx, app_ctx);
187 
188     Debug((DEBUG_MAVIS, "- %s (%d)\n", __func__, result));
189     return result;
190 }
191 
mavis_recv(mavis_ctx * mcx,av_ctx ** ac,void * app_ctx)192 int mavis_recv(mavis_ctx * mcx, av_ctx ** ac, void *app_ctx)
193 {
194     int result;
195     DebugIn(DEBUG_MAVIS);
196     result = mcx->recv(mcx, ac, app_ctx);
197     DebugOut(DEBUG_MAVIS);
198     return result;
199 }
200 
av_clear(av_ctx * ac)201 void av_clear(av_ctx * ac)
202 {
203     DebugIn(DEBUG_AV);
204     if (ac) {
205 	int i;
206 
207 	for (i = 0; i < AV_A_ARRAYSIZE; i++)
208 	    Xfree(&ac->arr[i]);
209     }
210     DebugOut(DEBUG_AV);
211 }
212 
av_move(av_ctx * ac_out,av_ctx * ac_in)213 void av_move(av_ctx * ac_out, av_ctx * ac_in)
214 {
215     int i;
216 
217     DebugIn(DEBUG_AV);
218     av_clear(ac_out);
219 
220     for (i = 0; i < AV_A_ARRAYSIZE; i++) {
221 	ac_out->arr[i] = ac_in->arr[i];
222 	ac_in->arr[i] = NULL;
223     }
224 
225     DebugOut(DEBUG_AV);
226 }
227 
av_copy(av_ctx * ac_out,av_ctx * ac_in)228 void av_copy(av_ctx * ac_out, av_ctx * ac_in)
229 {
230     int i;
231 
232     DebugIn(DEBUG_AV);
233     av_clear(ac_out);
234 
235     for (i = 0; i < AV_A_ARRAYSIZE; i++) {
236 	Xfree(&ac_out->arr[i]);
237 	if (ac_in->arr[i])
238 	    ac_out->arr[i] = strdup(ac_in->arr[i]);
239     }
240 
241     DebugOut(DEBUG_AV);
242 }
243 
av_merge(av_ctx * ac_out,av_ctx * ac_in)244 void av_merge(av_ctx * ac_out, av_ctx * ac_in)
245 {
246     int i;
247 
248     DebugIn(DEBUG_AV);
249 
250     for (i = 0; i < AV_A_ARRAYSIZE; i++)
251 	if (!ac_out->arr[i] && ac_in->arr[i])
252 	    ac_out->arr[i] = strdup(ac_in->arr[i]);
253 
254     DebugOut(DEBUG_AV);
255 }
256 
av_set(av_ctx * ac,int av_attribute,char * av_value)257 void av_set(av_ctx * ac, int av_attribute, char *av_value)
258 {
259     if (av_attribute < 0 || av_attribute >= AV_A_ARRAYSIZE) {
260 	Debug((DEBUG_AV, "%s(%d) out of bounds\n", __func__, av_attribute));
261 	return;
262     }
263 
264     Debug((DEBUG_AV, " %s(%s) = %-20s\n", __func__, av_char[av_attribute], av_value ? av_value : "(NULL)"));
265 
266     Xfree(&ac->arr[av_attribute]);
267 
268     ac->arr[av_attribute] = av_value ? Xstrdup(av_value) : NULL;
269 }
270 
av_setf(av_ctx * ac,int av_attribute,char * format,...)271 void av_setf(av_ctx * ac, int av_attribute, char *format, ...)
272 {
273     size_t len = 1024, nlen;
274     va_list ap;
275     char *tmpbuf = alloca(len);
276 
277     va_start(ap, format);
278     nlen = vsnprintf(tmpbuf, len, format, ap);
279     va_end(ap);
280     if (len <= nlen) {
281 	tmpbuf = alloca(++nlen);
282 	va_start(ap, format);
283 	vsnprintf(tmpbuf, nlen, format, ap);
284 	va_end(ap);
285     }
286     va_end(ap);
287     av_set(ac, av_attribute, tmpbuf);
288 }
289 
av_get(av_ctx * ac,int av_attribute)290 char *av_get(av_ctx * ac, int av_attribute)
291 {
292     if (av_attribute < 0 || av_attribute > AV_A_ARRAYSIZE) {
293 	Debug((DEBUG_AV, "%s(%d): out of bounds\n", __func__, av_attribute));
294 	return NULL;
295     }
296 #ifdef DEBUG
297     if (ac->arr[av_attribute])
298 	Debug((DEBUG_AV, " %s(%s) = %-20s\n", __func__, av_char[av_attribute], ac->arr[av_attribute] ? ac->arr[av_attribute] : "(NULL)"));
299 #endif
300 
301     return ac->arr[av_attribute];
302 }
303 
av_dump(av_ctx * ac)304 void av_dump(av_ctx * ac)
305 {
306     int i;
307 
308     fprintf(stderr, "attribute-value-pairs:\n");
309     for (i = 0; i < AV_A_ARRAYSIZE; i++)
310 	if (ac->arr[i])
311 	    fprintf(stderr, "%-20s%s\n", av_char[i], ac->arr[i]);
312     fprintf(stderr, "\n");
313 }
314 
av_attribute_to_i(char * s)315 int av_attribute_to_i(char *s)
316 {
317     int i;
318 
319     for (i = 0; i < AV_A_ARRAYSIZE; i++)
320 	if (!strcasecmp(av_char[i], s))
321 	    return i;
322     return -1;
323 }
324 
av_array_to_char(av_ctx * ac,char * buffer,size_t buflen,fd_set * set)325 int av_array_to_char(av_ctx * ac, char *buffer, size_t buflen, fd_set * set)
326 {
327     int i, j;
328     char *u;
329     char *t = buffer;
330 
331     buffer[0] = 0;
332 
333     for (i = 0; i < AV_A_ARRAYSIZE; i++)
334 	if ((!set || FD_ISSET(i, set)) && (u = av_get(ac, i))) {
335 	    j = snprintf(t, (size_t) (buffer + buflen - t), "%d %s\n", i, u);
336 	    if (j >= buffer + buflen - t)
337 		return -1;
338 	    t += j;
339 	}
340 
341     return (int) (t - buffer);
342 }
343 
av_char_to_array(av_ctx * ac,char * buffer,fd_set * set)344 int av_char_to_array(av_ctx * ac, char *buffer, fd_set * set)
345 {
346     char *av_start = buffer;
347     char *av_end = av_start;
348     int av_attribute;
349     char *av_value;
350 
351     while ((av_end = strchr(av_end, '\n'))) {
352 	*av_end = 0;
353 	av_value = strchr(av_start, ' ');
354 	if (av_value) {
355 	    *av_value++ = 0;
356 	    if ((1 == sscanf(av_start, "%d", &av_attribute)) && (!set || FD_ISSET(av_attribute, set)))
357 		av_set(ac, av_attribute, av_value);
358 	    *(av_value - 1) = ' ';
359 	}
360 	*av_end++ = '\n';
361 	av_start = av_end;
362     }
363 
364     return 0;
365 }
366 
av_new(void * cb,void * ctx)367 av_ctx *av_new(void *cb, void *ctx)
368 {
369     av_ctx *a = Xcalloc((size_t) 1, sizeof(av_ctx));
370     a->app_cb = cb;
371     a->app_ctx = ctx;
372     return a;
373 }
374 
av_setcb(av_ctx * a,void * cb,void * ctx)375 void av_setcb(av_ctx * a, void *cb, void *ctx)
376 {
377     a->app_cb = cb;
378     a->app_ctx = ctx;
379 }
380 
av_free(av_ctx * ac)381 void av_free(av_ctx * ac)
382 {
383     if (ac) {
384 	int i;
385 	for (i = 0; i < AV_A_ARRAYSIZE; i++)
386 	    Xfree(&ac->arr[i]);
387 	free(ac);
388     }
389 }
390 
mavis_check_version(char * version)391 int mavis_check_version(char *version)
392 {
393     if (strcmp(version, VERSION)) {
394 	logmsg("Warning: MAVIS library version mismatch (%s vs. %s). Expect trouble.", version, VERSION);
395 	return -1;
396     }
397     return 0;
398 }
399 
mavis_detach(void)400 void mavis_detach(void)
401 {
402     int devnull;
403 
404     setsid();
405     devnull = open("/dev/null", O_RDWR);
406     dup2(devnull, 1);
407     close(devnull);
408     fcntl(0, F_SETFD, fcntl(0, F_GETFD, 0) | FD_CLOEXEC);
409     fcntl(1, F_SETFD, fcntl(1, F_GETFD, 0) | FD_CLOEXEC);
410 }
411