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