xref: /freebsd/contrib/sendmail/libmilter/smfi.c (revision acd3428b7d3e94cef0e1881c868cb4b131d4ff41)
1 /*
2  *  Copyright (c) 1999-2005 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  *
5  * By using this file, you agree to the terms and conditions set
6  * forth in the LICENSE file which can be found at the top level of
7  * the sendmail distribution.
8  *
9  */
10 
11 #include <sm/gen.h>
12 SM_RCSID("@(#)$Id: smfi.c,v 8.74 2005/03/30 00:44:07 ca Exp $")
13 #include <sm/varargs.h>
14 #include "libmilter.h"
15 
16 static int smfi_header __P((SMFICTX *, int, int, char *, char *));
17 static int myisenhsc __P((const char *, int));
18 
19 /* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
20 #define MAXREPLYLEN	980	/* max. length of a reply string */
21 #define MAXREPLIES	32	/* max. number of reply strings */
22 
23 /*
24 **  SMFI_HEADER -- send a header to the MTA
25 **
26 **	Parameters:
27 **		ctx -- Opaque context structure
28 **		cmd -- Header modification command
29 **		hdridx -- Header index
30 **		headerf -- Header field name
31 **		headerv -- Header field value
32 **
33 **
34 **	Returns:
35 **		MI_SUCCESS/MI_FAILURE
36 */
37 
38 static int
39 smfi_header(ctx, cmd, hdridx, headerf, headerv)
40 	SMFICTX *ctx;
41 	int cmd;
42 	int hdridx;
43 	char *headerf;
44 	char *headerv;
45 {
46 	size_t len, l1, l2, offset;
47 	int r;
48 	mi_int32 v;
49 	char *buf;
50 	struct timeval timeout;
51 
52 	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
53 		return MI_FAILURE;
54 	timeout.tv_sec = ctx->ctx_timeout;
55 	timeout.tv_usec = 0;
56 	l1 = strlen(headerf) + 1;
57 	l2 = strlen(headerv) + 1;
58 	len = l1 + l2;
59 	if (hdridx >= 0)
60 		len += MILTER_LEN_BYTES;
61 	buf = malloc(len);
62 	if (buf == NULL)
63 		return MI_FAILURE;
64 	offset = 0;
65 	if (hdridx >= 0)
66 	{
67 		v = htonl(hdridx);
68 		(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
69 		offset += MILTER_LEN_BYTES;
70 	}
71 	(void) memcpy(buf + offset, headerf, l1);
72 	(void) memcpy(buf + offset + l1, headerv, l2);
73 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
74 	free(buf);
75 	return r;
76 }
77 
78 /*
79 **  SMFI_ADDHEADER -- send a new header to the MTA
80 **
81 **	Parameters:
82 **		ctx -- Opaque context structure
83 **		headerf -- Header field name
84 **		headerv -- Header field value
85 **
86 **	Returns:
87 **		MI_SUCCESS/MI_FAILURE
88 */
89 
90 int
91 smfi_addheader(ctx, headerf, headerv)
92 	SMFICTX *ctx;
93 	char *headerf;
94 	char *headerv;
95 {
96 	if (!mi_sendok(ctx, SMFIF_ADDHDRS))
97 		return MI_FAILURE;
98 
99 	return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
100 }
101 
102 /*
103 **  SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
104 **
105 **	Parameters:
106 **		ctx -- Opaque context structure
107 **  		hdridx -- index into header list where insertion should occur
108 **		headerf -- Header field name
109 **		headerv -- Header field value
110 **
111 **	Returns:
112 **		MI_SUCCESS/MI_FAILURE
113 */
114 
115 int
116 smfi_insheader(ctx, hdridx, headerf, headerv)
117 	SMFICTX *ctx;
118 	int hdridx;
119 	char *headerf;
120 	char *headerv;
121 {
122 	if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
123 		return MI_FAILURE;
124 
125 	return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
126 }
127 
128 /*
129 **  SMFI_CHGHEADER -- send a changed header to the MTA
130 **
131 **	Parameters:
132 **		ctx -- Opaque context structure
133 **		headerf -- Header field name
134 **		hdridx -- Header index value
135 **		headerv -- Header field value
136 **
137 **	Returns:
138 **		MI_SUCCESS/MI_FAILURE
139 */
140 
141 int
142 smfi_chgheader(ctx, headerf, hdridx, headerv)
143 	SMFICTX *ctx;
144 	char *headerf;
145 	mi_int32 hdridx;
146 	char *headerv;
147 {
148 	if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
149 		return MI_FAILURE;
150 	if (headerv == NULL)
151 		headerv = "";
152 
153 	return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
154 }
155 
156 /*
157 **  SMFI_ADDRCPT -- send an additional recipient to the MTA
158 **
159 **	Parameters:
160 **		ctx -- Opaque context structure
161 **		rcpt -- recipient address
162 **
163 **	Returns:
164 **		MI_SUCCESS/MI_FAILURE
165 */
166 
167 int
168 smfi_addrcpt(ctx, rcpt)
169 	SMFICTX *ctx;
170 	char *rcpt;
171 {
172 	size_t len;
173 	struct timeval timeout;
174 
175 	if (rcpt == NULL || *rcpt == '\0')
176 		return MI_FAILURE;
177 	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
178 		return MI_FAILURE;
179 	timeout.tv_sec = ctx->ctx_timeout;
180 	timeout.tv_usec = 0;
181 	len = strlen(rcpt) + 1;
182 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
183 }
184 
185 /*
186 **  SMFI_DELRCPT -- send a recipient to be removed to the MTA
187 **
188 **	Parameters:
189 **		ctx -- Opaque context structure
190 **		rcpt -- recipient address
191 **
192 **	Returns:
193 **		MI_SUCCESS/MI_FAILURE
194 */
195 
196 int
197 smfi_delrcpt(ctx, rcpt)
198 	SMFICTX *ctx;
199 	char *rcpt;
200 {
201 	size_t len;
202 	struct timeval timeout;
203 
204 	if (rcpt == NULL || *rcpt == '\0')
205 		return MI_FAILURE;
206 	if (!mi_sendok(ctx, SMFIF_DELRCPT))
207 		return MI_FAILURE;
208 	timeout.tv_sec = ctx->ctx_timeout;
209 	timeout.tv_usec = 0;
210 	len = strlen(rcpt) + 1;
211 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
212 }
213 
214 /*
215 **  SMFI_REPLACEBODY -- send a body chunk to the MTA
216 **
217 **	Parameters:
218 **		ctx -- Opaque context structure
219 **		bodyp -- body chunk
220 **		bodylen -- length of body chunk
221 **
222 **	Returns:
223 **		MI_SUCCESS/MI_FAILURE
224 */
225 
226 int
227 smfi_replacebody(ctx, bodyp, bodylen)
228 	SMFICTX *ctx;
229 	unsigned char *bodyp;
230 	int bodylen;
231 {
232 	int len, off, r;
233 	struct timeval timeout;
234 
235 	if (bodylen < 0 ||
236 	    (bodyp == NULL && bodylen > 0))
237 		return MI_FAILURE;
238 	if (!mi_sendok(ctx, SMFIF_CHGBODY))
239 		return MI_FAILURE;
240 	timeout.tv_sec = ctx->ctx_timeout;
241 	timeout.tv_usec = 0;
242 
243 	/* split body chunk if necessary */
244 	off = 0;
245 	do
246 	{
247 		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
248 						       bodylen;
249 		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
250 				(char *) (bodyp + off), len)) != MI_SUCCESS)
251 			return r;
252 		off += len;
253 		bodylen -= len;
254 	} while (bodylen > 0);
255 	return MI_SUCCESS;
256 }
257 
258 /*
259 **  SMFI_QUARANTINE -- quarantine an envelope
260 **
261 **	Parameters:
262 **		ctx -- Opaque context structure
263 **		reason -- why?
264 **
265 **	Returns:
266 **		MI_SUCCESS/MI_FAILURE
267 */
268 
269 int
270 smfi_quarantine(ctx, reason)
271 	SMFICTX *ctx;
272 	char *reason;
273 {
274 	size_t len;
275 	int r;
276 	char *buf;
277 	struct timeval timeout;
278 
279 	if (reason == NULL || *reason == '\0')
280 		return MI_FAILURE;
281 	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
282 		return MI_FAILURE;
283 	timeout.tv_sec = ctx->ctx_timeout;
284 	timeout.tv_usec = 0;
285 	len = strlen(reason) + 1;
286 	buf = malloc(len);
287 	if (buf == NULL)
288 		return MI_FAILURE;
289 	(void) memcpy(buf, reason, len);
290 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
291 	free(buf);
292 	return r;
293 }
294 
295 /*
296 **  MYISENHSC -- check whether a string contains an enhanced status code
297 **
298 **	Parameters:
299 **		s -- string with possible enhanced status code.
300 **		delim -- delim for enhanced status code.
301 **
302 **	Returns:
303 **		0  -- no enhanced status code.
304 **		>4 -- length of enhanced status code.
305 **
306 **	Side Effects:
307 **		none.
308 */
309 
310 static int
311 myisenhsc(s, delim)
312 	const char *s;
313 	int delim;
314 {
315 	int l, h;
316 
317 	if (s == NULL)
318 		return 0;
319 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
320 		return 0;
321 	h = 0;
322 	l = 2;
323 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
324 		++h;
325 	if (h == 0 || s[l + h] != '.')
326 		return 0;
327 	l += h + 1;
328 	h = 0;
329 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
330 		++h;
331 	if (h == 0 || s[l + h] != delim)
332 		return 0;
333 	return l + h;
334 }
335 
336 /*
337 **  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
338 **
339 **	Parameters:
340 **		ctx -- Opaque context structure
341 **		rcode -- The three-digit (RFC 821) SMTP reply code.
342 **		xcode -- The extended (RFC 2034) reply code.
343 **		message -- The text part of the SMTP reply.
344 **
345 **	Returns:
346 **		MI_SUCCESS/MI_FAILURE
347 */
348 
349 int
350 smfi_setreply(ctx, rcode, xcode, message)
351 	SMFICTX *ctx;
352 	char *rcode;
353 	char *xcode;
354 	char *message;
355 {
356 	size_t len;
357 	char *buf;
358 
359 	if (rcode == NULL || ctx == NULL)
360 		return MI_FAILURE;
361 
362 	/* ### <sp> \0 */
363 	len = strlen(rcode) + 2;
364 	if (len != 5)
365 		return MI_FAILURE;
366 	if ((rcode[0] != '4' && rcode[0] != '5') ||
367 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
368 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
369 		return MI_FAILURE;
370 	if (xcode != NULL)
371 	{
372 		if (!myisenhsc(xcode, '\0'))
373 			return MI_FAILURE;
374 		len += strlen(xcode) + 1;
375 	}
376 	if (message != NULL)
377 	{
378 		size_t ml;
379 
380 		/* XXX check also for unprintable chars? */
381 		if (strpbrk(message, "\r\n") != NULL)
382 			return MI_FAILURE;
383 		ml = strlen(message);
384 		if (ml > MAXREPLYLEN)
385 			return MI_FAILURE;
386 		len += ml + 1;
387 	}
388 	buf = malloc(len);
389 	if (buf == NULL)
390 		return MI_FAILURE;		/* oops */
391 	(void) sm_strlcpy(buf, rcode, len);
392 	(void) sm_strlcat(buf, " ", len);
393 	if (xcode != NULL)
394 		(void) sm_strlcat(buf, xcode, len);
395 	if (message != NULL)
396 	{
397 		if (xcode != NULL)
398 			(void) sm_strlcat(buf, " ", len);
399 		(void) sm_strlcat(buf, message, len);
400 	}
401 	if (ctx->ctx_reply != NULL)
402 		free(ctx->ctx_reply);
403 	ctx->ctx_reply = buf;
404 	return MI_SUCCESS;
405 }
406 
407 /*
408 **  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
409 **
410 **	Parameters:
411 **		ctx -- Opaque context structure
412 **		rcode -- The three-digit (RFC 821) SMTP reply code.
413 **		xcode -- The extended (RFC 2034) reply code.
414 **		txt, ... -- The text part of the SMTP reply,
415 **			MUST be terminated with NULL.
416 **
417 **	Returns:
418 **		MI_SUCCESS/MI_FAILURE
419 */
420 
421 int
422 #if SM_VA_STD
423 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
424 #else /* SM_VA_STD */
425 smfi_setmlreply(ctx, rcode, xcode, va_alist)
426 	SMFICTX *ctx;
427 	const char *rcode;
428 	const char *xcode;
429 	va_dcl
430 #endif /* SM_VA_STD */
431 {
432 	size_t len;
433 	size_t rlen;
434 	int args;
435 	char *buf, *txt;
436 	const char *xc;
437 	char repl[16];
438 	SM_VA_LOCAL_DECL
439 
440 	if (rcode == NULL || ctx == NULL)
441 		return MI_FAILURE;
442 
443 	/* ### <sp> */
444 	len = strlen(rcode) + 1;
445 	if (len != 4)
446 		return MI_FAILURE;
447 	if ((rcode[0] != '4' && rcode[0] != '5') ||
448 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
449 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
450 		return MI_FAILURE;
451 	if (xcode != NULL)
452 	{
453 		if (!myisenhsc(xcode, '\0'))
454 			return MI_FAILURE;
455 		xc = xcode;
456 	}
457 	else
458 	{
459 		if (rcode[0] == '4')
460 			xc = "4.0.0";
461 		else
462 			xc = "5.0.0";
463 	}
464 
465 	/* add trailing space */
466 	len += strlen(xc) + 1;
467 	rlen = len;
468 	args = 0;
469 	SM_VA_START(ap, xcode);
470 	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
471 	{
472 		size_t tl;
473 
474 		tl = strlen(txt);
475 		if (tl > MAXREPLYLEN)
476 			break;
477 
478 		/* this text, reply codes, \r\n */
479 		len += tl + 2 + rlen;
480 		if (++args > MAXREPLIES)
481 			break;
482 
483 		/* XXX check also for unprintable chars? */
484 		if (strpbrk(txt, "\r\n") != NULL)
485 			break;
486 	}
487 	SM_VA_END(ap);
488 	if (txt != NULL)
489 		return MI_FAILURE;
490 
491 	/* trailing '\0' */
492 	++len;
493 	buf = malloc(len);
494 	if (buf == NULL)
495 		return MI_FAILURE;		/* oops */
496 	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
497 	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
498 			   xc, " ");
499 	SM_VA_START(ap, xcode);
500 	txt = SM_VA_ARG(ap, char *);
501 	if (txt != NULL)
502 	{
503 		(void) sm_strlcat2(buf, " ", txt, len);
504 		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
505 		{
506 			if (--args <= 1)
507 				repl[3] = ' ';
508 			(void) sm_strlcat2(buf, "\r\n", repl, len);
509 			(void) sm_strlcat(buf, txt, len);
510 		}
511 	}
512 	if (ctx->ctx_reply != NULL)
513 		free(ctx->ctx_reply);
514 	ctx->ctx_reply = buf;
515 	SM_VA_END(ap);
516 	return MI_SUCCESS;
517 }
518 
519 /*
520 **  SMFI_SETPRIV -- set private data
521 **
522 **	Parameters:
523 **		ctx -- Opaque context structure
524 **		privatedata -- pointer to private data
525 **
526 **	Returns:
527 **		MI_SUCCESS/MI_FAILURE
528 */
529 
530 int
531 smfi_setpriv(ctx, privatedata)
532 	SMFICTX *ctx;
533 	void *privatedata;
534 {
535 	if (ctx == NULL)
536 		return MI_FAILURE;
537 	ctx->ctx_privdata = privatedata;
538 	return MI_SUCCESS;
539 }
540 
541 /*
542 **  SMFI_GETPRIV -- get private data
543 **
544 **	Parameters:
545 **		ctx -- Opaque context structure
546 **
547 **	Returns:
548 **		pointer to private data
549 */
550 
551 void *
552 smfi_getpriv(ctx)
553 	SMFICTX *ctx;
554 {
555 	if (ctx == NULL)
556 		return NULL;
557 	return ctx->ctx_privdata;
558 }
559 
560 /*
561 **  SMFI_GETSYMVAL -- get the value of a macro
562 **
563 **	See explanation in mfapi.h about layout of the structures.
564 **
565 **	Parameters:
566 **		ctx -- Opaque context structure
567 **		symname -- name of macro
568 **
569 **	Returns:
570 **		value of macro (NULL in case of failure)
571 */
572 
573 char *
574 smfi_getsymval(ctx, symname)
575 	SMFICTX *ctx;
576 	char *symname;
577 {
578 	int i;
579 	char **s;
580 	char one[2];
581 	char braces[4];
582 
583 	if (ctx == NULL || symname == NULL || *symname == '\0')
584 		return NULL;
585 
586 	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
587 	{
588 		one[0] = symname[1];
589 		one[1] = '\0';
590 	}
591 	else
592 		one[0] = '\0';
593 	if (strlen(symname) == 1)
594 	{
595 		braces[0] = '{';
596 		braces[1] = *symname;
597 		braces[2] = '}';
598 		braces[3] = '\0';
599 	}
600 	else
601 		braces[0] = '\0';
602 
603 	/* search backwards through the macro array */
604 	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
605 	{
606 		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
607 		    ctx->ctx_mac_buf[i] == NULL)
608 			continue;
609 		while (s != NULL && *s != NULL)
610 		{
611 			if (strcmp(*s, symname) == 0)
612 				return *++s;
613 			if (one[0] != '\0' && strcmp(*s, one) == 0)
614 				return *++s;
615 			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
616 				return *++s;
617 			++s;	/* skip over macro value */
618 			++s;	/* points to next macro name */
619 		}
620 	}
621 	return NULL;
622 }
623 
624 /*
625 **  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
626 **		     timeouts during long milter-side operations
627 **
628 **	Parameters:
629 **		ctx -- Opaque context structure
630 **
631 **	Return value:
632 **		MI_SUCCESS/MI_FAILURE
633 */
634 
635 int
636 smfi_progress(ctx)
637 	SMFICTX *ctx;
638 {
639 	struct timeval timeout;
640 
641 	if (ctx == NULL)
642 		return MI_FAILURE;
643 
644 	timeout.tv_sec = ctx->ctx_timeout;
645 	timeout.tv_usec = 0;
646 
647 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
648 }
649