1 /*
2 ** Copyright 1998 - 2009 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5 
6 #include	"lexer.h"
7 #include	"recipe.h"
8 #include	"varlist.h"
9 #include	"funcs.h"
10 #include	"tempfile.h"
11 #include	"message.h"
12 #include	"messageinfo.h"
13 #include	"xconfig.h"
14 #include	"exittrap.h"
15 #include	"maildrop.h"
16 #include	"config.h"
17 #include	"setgroupid.h"
18 #include	<sys/types.h>
19 
20 #if	HAVE_LOCALE_H
21 #include	<locale.h>
22 #endif
23 
24 #if HAVE_SYS_STAT_H
25 #include	<sys/stat.h>
26 #endif
27 #include	<sysexits.h>
28 #include	<string.h>
29 #include	<stdio.h>
30 #include	<stdlib.h>
31 #include	<ctype.h>
32 #include	<pwd.h>
33 #include	<grp.h>
34 #include	"../dbobj.h"
35 
36 /*
37 ** This switch can later be moved to config.h file with appropriate
38 ** configure option like --with-dovecotauth or something similar
39 */
40 #if DOVECOTAUTH
41 #include	"dovecotauth.h"
42 #endif
43 
44 #if AUTHLIB
45 #include	<courierauth.h>
46 #endif
47 
48 #if	HAS_GETHOSTNAME
49 #else
50 extern "C" int gethostname(const char *, size_t);
51 #endif
52 
rfc2045_error(const char * p)53 void rfc2045_error(const char *p)
54 {
55 	fprintf(stderr, "%s\n", p);
56 	fflush(stderr);
57 	exit(1);
58 }
59 
60 extern void setprocgroup();
61 
62 static Message m1, m2;
63 extern char **environ;
64 static int errexit=EX_TEMPFAIL;
65 int quota_warn_percent = -1;
66 const char *quota_warn_message=0;
67 
68 static const char *defaults_vars[]={"LOCKEXT","LOCKSLEEP","LOCKTIMEOUT",
69 					"LOCKREFRESH", "PATH", "SENDMAIL",
70 					"MAILDIRQUOTA"};
71 static const char *defaults_vals[]={LOCKEXT_DEF,LOCKSLEEP_DEF,LOCKTIMEOUT_DEF,
72 					LOCKREFRESH_DEF, DEFAULT_PATH,
73 					SENDMAIL_DEF, ""};
74 
75 Maildrop maildrop;
76 
Maildrop()77 Maildrop::Maildrop()
78 {
79 	verbose_level=0;
80 	isdelivery=0;
81 	sigfpe=0;
82 	includelevel=0;
83 	embedded_mode=0;
84 	msgptr= &m1;
85 	savemsgptr= &m2;
86 #if AUTHLIB_TEMPREJECT
87 	authlib_essential=1;
88 #else
89 	authlib_essential=0;
90 #endif
91 }
92 
help()93 static void help()
94 {
95 	mout << "Usage: maildrop [options] [-d user] [arg] [arg] ...\n";
96 	mout << "       maildrop [options] [filterfile [arg] [arg] ...\n";
97 }
98 
bad()99 static void bad()
100 {
101 	errexit=EX_TEMPFAIL;
102 	throw "Bad command line arguments, -h for help.";
103 }
104 
nouser()105 static void nouser()
106 {
107 	merr << "Invalid user specified.\n";
108 	exit(EX_NOUSER);
109 }
110 
nochangeuidgid()111 static void nochangeuidgid()
112 {
113 	errexit=EX_TEMPFAIL;
114 	throw "Cannot set my user or group id.";
115 }
116 
trusted_user(uid_t uid)117 static int trusted_user(uid_t uid)
118 {
119 static char trusted_users[]=TRUSTED_USERS;
120 static char buf[ sizeof(trusted_users) ];
121 char	*p;
122 
123 	strcpy(buf, trusted_users);
124 	for (p=buf; (p=strtok(p, " ")) != 0; p=0)
125 	{
126 	struct	passwd *q=getpwnam(p);
127 
128 		if (q && q->pw_uid == uid)
129 			return (1);
130 	}
131 	return (0);
132 }
133 
trusted_group(gid_t gid)134 static int trusted_group(gid_t gid)
135 {
136 static char trusted_groups[]=TRUSTED_GROUPS;
137 static char buf[ sizeof(trusted_groups) ];
138 char	*p;
139 
140 	strcpy(buf, trusted_groups);
141 	for (p=buf; (p=strtok(p, " ")) != 0; p=0)
142 	{
143 	struct	group *q=getgrnam(p);
144 
145 		if (q && (gid_t)q->gr_gid == gid)
146 			return (1);
147 	}
148 	return (0);
149 }
150 
trusted_uidgid(uid_t uid,gid_t gid,gid_t gid2)151 static int trusted_uidgid(uid_t uid, gid_t gid, gid_t gid2)
152 {
153 	if (trusted_user(uid) || trusted_group(gid) ||
154 		trusted_group(gid2))
155 		return (1);
156 	return (0);
157 }
158 
sethostname(Buffer & buf)159 static void sethostname(Buffer &buf)
160 {
161 char    hostname[256];
162 
163         hostname[0]=0;
164         gethostname(hostname, 256);
165         hostname[sizeof(hostname)-1]=0;
166 
167 	buf=hostname;
168 }
169 
170 
copyright()171 static void copyright()
172 {
173 static const char msg[]="maildrop " VERSION " Copyright 1998-2018 Double Precision, Inc."
174 
175 #if CRLF_TERM
176 	"\r\n"
177 #else
178 	"\n"
179 #endif
180 #if HAVE_COURIER
181 	"Courier-specific maildrop build. This version of maildrop should only be used"
182 #if CRLF_TERM
183 	"\r\n"
184 #else
185 	"\n"
186 #endif
187 	"with Courier, and not any other mail server."
188 #if CRLF_TERM
189 	"\r\n"
190 #else
191 	"\n"
192 #endif
193 #endif
194 #ifdef DbObj
195 	"GDBM/DB extensions enabled."
196 #if CRLF_TERM
197 	"\r\n"
198 #else
199 	"\n"
200 #endif
201 #endif
202 #if DOVECOTAUTH
203 	"Dovecot Authentication extension enabled."
204 #if CRLF_TERM
205 	"\r\n"
206 #else
207 	"\n"
208 #endif
209 #endif
210 #if AUTHLIB
211 	"Courier Authentication Library extension enabled."
212 #if CRLF_TERM
213 	"\r\n"
214 #else
215 	"\n"
216 #endif
217 #endif
218 	"Maildir quota extension are now always enabled."
219 #if CRLF_TERM
220 	"\r\n"
221 #else
222 	"\n"
223 #endif
224 	"This program is distributed under the terms of the GNU General Public"
225 #if CRLF_TERM
226 	"\r\n"
227 #else
228 	"\n"
229 #endif
230         "License. See COPYING for additional information."
231 #if CRLF_TERM
232 	"\r\n"
233 #else
234 	"\n"
235 #endif
236 
237 		;
238 
239 	mout << msg;
240 	mout.flush();
241 }
242 
reset_vars()243 void Maildrop::reset_vars()
244 {
245 int	i;
246 Buffer	name, value;
247 
248 	for (i=0; i<(int)(sizeof(defaults_vars)/sizeof(defaults_vars[0])); i++)
249 	{
250 		name=defaults_vars[i];
251 		value=defaults_vals[i];
252 		SetVar(name, value);
253 	}
254 	name="HOME";
255 	SetVar(name, maildrop.init_home);
256 	name="LOGNAME";
257 	SetVar(name, maildrop.init_logname);
258 	name="SHELL";
259 	SetVar(name, maildrop.init_shell);
260 	name="DEFAULT";
261 	SetVar(name, maildrop.init_default);
262 
263 	name="UMASK";
264 	value="077";
265 	SetVar(name, value);
266 
267 	if (maildrop.init_quota.Length() > 0)
268 	{
269 		name="MAILDIRQUOTA";
270 		SetVar(name, maildrop.init_quota);
271 	}
272 }
273 
274 #if AUTHLIB
275 // Authlib lookup
276 
callback_authlib(struct authinfo * auth,void * void_arg)277 static int callback_authlib(struct authinfo *auth,
278 			    void *void_arg)
279 {
280 	Maildrop &maildrop=*(Maildrop *)void_arg;
281 
282 	if (auth_mkhomedir(auth))
283 	{
284 		perror(auth->homedir);
285 		exit(1);
286 	}
287 
288 	if (VerboseLevel() > 1)
289 	{
290 		Buffer b;
291 
292 		b.set(auth->sysgroupid);
293 		b.push(0);
294 
295 		merr << "maildrop: authlib: groupid="
296 		     << b << "\n";
297 	}
298 
299 	if (setgroupid(auth->sysgroupid) < 0)
300 	{
301 		perror("setgid");
302 		exit(1);
303 	}
304 
305 	uid_t u;
306 	if (auth->sysusername)
307 	{
308 		struct	passwd *q=getpwnam(auth->sysusername);
309 
310 		if (q == NULL)
311 		{
312 			merr << "Cannot find system user "
313 			     << auth->sysusername
314 			     << "\n";
315 
316 			nochangeuidgid();
317 		}
318 
319 		u=q->pw_uid;
320 	}
321 	else
322 		u=*auth->sysuserid;
323 
324 	if (VerboseLevel() > 1)
325 	{
326 		Buffer b;
327 
328 		b.set(u);
329 		b.push(0);
330 
331 		merr << "maildrop: authlib: userid="
332 		     << b << "\n";
333 	}
334 
335 	if (setuid(u) < 0 ||
336 	    getuid() != u)
337 		nochangeuidgid();
338 
339 	if (VerboseLevel() > 1)
340 	{
341 		merr << "maildrop: authlib: logname="
342 		     << auth->address
343 		     << ", home="
344 		     << auth->homedir
345 		     << ", mail="
346 		     << (auth->maildir ? auth->maildir:"(default)")
347 		     << "\n";
348 	}
349 
350 	maildrop.init_home=auth->homedir;
351 	maildrop.init_logname=auth->address;
352 	maildrop.init_shell="/bin/sh";
353 	maildrop.init_default=auth->maildir ? auth->maildir:
354 		GetDefaultMailbox(auth->address);
355 
356 	if ( auth->quota )
357 		maildrop.init_quota=auth->quota;
358 
359 	return 0;
360 }
361 
find_in_authlib(Maildrop * maildrop,const char * user)362 int find_in_authlib(Maildrop *maildrop, const char* user)
363 {
364 	struct auth_meta meta;
365 
366 	memset(&meta, 0, sizeof(meta));
367 
368 	int rc=auth_getuserinfo_meta(&meta, "login",
369 				     user, callback_authlib, maildrop);
370 
371 	if (rc == 0)
372 		return 1;
373 
374 	if ((rc > 0) && (maildrop->authlib_essential == 1))
375 	{
376 		errexit=EX_TEMPFAIL;
377 		throw "Temporary authentication failure.";
378 	}
379 
380 	return 0;
381 }
382 #else
find_in_authlib(Maildrop * maildrop,const char * user)383 int find_in_authlib(Maildrop *maildrop, const char* user)
384 {
385 	return 0;
386 }
387 #endif
388 
389 #if DOVECOTAUTH
callback_dovecotauth(struct dovecotauthinfo * auth,void * void_arg)390 static int callback_dovecotauth(struct dovecotauthinfo *auth,
391 			    void *void_arg)
392 {
393 	Maildrop &maildrop=*(Maildrop *)void_arg;
394 
395 	if (VerboseLevel() > 1)
396 	{
397 		Buffer b;
398 
399 		b.set(auth->sysgroupid);
400 		b.push(0);
401 
402 		merr << "maildrop: dovecotauth: groupid="
403 		     << b << "\n";
404 	}
405 
406 	setgroupid(auth->sysgroupid);
407 
408 	uid_t u;
409 	if (auth->sysusername)
410 	{
411 		struct	passwd *q=getpwnam(auth->sysusername);
412 
413 		if (q == NULL)
414 		{
415 			merr << "Cannot find system user "
416 			     << auth->sysusername
417 			     << "\n";
418 
419 			nochangeuidgid();
420 		}
421 
422 		u=q->pw_uid;
423 	}
424 	else
425 		u=*auth->sysuserid;
426 
427 	if (VerboseLevel() > 1)
428 	{
429 		Buffer b;
430 
431 		b.set(u);
432 		b.push(0);
433 
434 		merr << "maildrop: dovecotauth: userid="
435 		     << b << "\n";
436 	}
437 
438 	setuid(u);
439 
440 	if ( getuid() != u)
441 		nochangeuidgid();
442 
443 	if (VerboseLevel() > 1)
444 	{
445 		merr << "maildrop: dovecotauth: logname="
446 		     << auth->address
447 		     << ", home="
448 		     << auth->homedir
449 		     << ", mail="
450 		     << (auth->maildir ? auth->maildir:"(default)")
451 		     << "\n";
452 	}
453 
454 	maildrop.init_home=auth->homedir;
455 	maildrop.init_logname=auth->address;
456 	maildrop.init_shell="/bin/sh";
457 	maildrop.init_default=auth->maildir ? auth->maildir:
458 		GetDefaultMailbox(auth->address);
459 
460 	return 0;
461 }
462 
find_in_dovecotauth(const char * addr,Maildrop * maildrop,const char * user)463 int find_in_dovecotauth(const char *addr, Maildrop *maildrop, const char* user)
464 {
465 	int rc=dovecotauth_getuserinfo(addr,
466 				user, callback_dovecotauth, maildrop);
467 
468 	if (rc == 0)
469 		return 1;
470 
471 	if (rc > 0)
472 	{
473 		errexit=EX_TEMPFAIL;
474 		throw "Temporary authentication failure.";
475 	}
476 
477 	return 0;
478 }
479 #endif
480 
tempfail(const char * msg)481 static void tempfail(const char *msg)
482 {
483 	errexit = EX_TEMPFAIL;
484 	throw msg;
485 }
486 
run(int argc,char ** argv)487 static int run(int argc, char **argv)
488 {
489 int	argn;
490 const	char *deliverymode=0;
491 char *embedded_filter=0;
492 const	char *from=0;
493 int     explicit_from=0;
494 Buffer	recipe;
495 uid_t	orig_uid;
496 gid_t	orig_gid, orig_gid2;
497 Buffer	extra_headers;
498 struct passwd *my_pw;
499 int	found;
500 #if	HAVE_COURIER
501 #if	RESTRICT_TRUSTED
502 const	char *numuidgid=0;
503 #endif
504 #endif
505 #if DOVECOTAUTH
506 const	char *dovecotauth_addr=0;
507 #endif
508 
509 
510 	umask( 0007 );
511 	for (argn=1; argn < argc; )
512 	{
513 		if (argv[argn][0] != '-')	break;
514 		if (strcmp(argv[argn], "--") == 0)	{ ++argn; break; }
515 
516 	char	optc=argv[argn][1];
517 	const char *optarg=argv[argn]+2;
518 
519 		++argn;
520 		switch (optc)	{
521 
522 #if	HAVE_COURIER
523 #if	RESTRICT_TRUSTED
524 		case 'D':
525 			if (!*optarg && argn < argc)	optarg=argv[argn++];
526 			numuidgid=optarg;
527 			break;
528 #endif
529 #endif
530 		case 'd':
531 			if (!*optarg && argn < argc)	optarg=argv[argn++];
532 			deliverymode=optarg;
533 			break;
534 		case 'V':
535 			if (!*optarg && argn < argc)	optarg=argv[argn++];
536 			maildrop.verbose_level=atoi(optarg);
537 			break;
538 		case 'v':
539 			copyright();
540 			return (0);
541 		case 'm':
542 			maildrop.embedded_mode=1;
543 			break;
544 		case 'M':
545 			if (!*optarg && argn < argc)	optarg=argv[argn++];
546 			maildrop.embedded_mode=1;
547 			if (!deliverymode)	deliverymode="";
548 			if (!*optarg)
549 			{
550 				help();
551 				return (EX_TEMPFAIL);
552 			}
553 			embedded_filter=(char *)malloc(strlen(optarg)+1);
554 			if (!embedded_filter)	outofmem();
555 			strcpy(embedded_filter, optarg);
556 			{
557 			char *p;
558 
559 				for (p=embedded_filter; *p; p++)
560 					if (*p == SLASH_CHAR || *p == '.')
561 						*p=':';
562 			}
563 			break;
564 		case 'A':
565 			if (!*optarg && argn < argc)	optarg=argv[argn++];
566 			if (*optarg)
567 			{
568 				extra_headers += optarg;
569 				extra_headers += '\n';
570 			}
571 			break;
572 		case 'f':
573 			if (!*optarg && argn < argc)	optarg=argv[argn++];
574 			if (*optarg)
575 			{
576 				from=optarg;
577 				explicit_from=1;
578 			}
579 			break;
580 		case 'w':
581 			if (!*optarg && argn < argc)	optarg=argv[argn++];
582 			if (*optarg)
583 				quota_warn_percent=atoi(optarg);
584 			break;
585 		case 'W':
586 			if (!*optarg && argn < argc)	optarg=argv[argn++];
587 			if (*optarg)
588 				quota_warn_message=optarg;
589 			break;
590 		case 'a':
591 			maildrop.authlib_essential=1;
592 			break;
593 #if DOVECOTAUTH
594 		case 't':
595 			if (!*optarg && argn < argc)	optarg=argv[argn++];
596 			if (!*optarg)
597 			{
598 				mout << "You didn't specify the location of Dovecot auth socket.\n";
599 				return (EX_TEMPFAIL);
600 			}
601 			else
602 				dovecotauth_addr=optarg;
603 			break;
604 #endif
605 		case 'h':
606 			help();
607 			return (EX_TEMPFAIL);
608 		default:
609 			bad();
610 		}
611 	}
612 
613 	my_pw=0;
614 	found=0;
615 	orig_uid=getuid();
616 	orig_gid=getgid();
617 	orig_gid2=getegid();
618 	if (!deliverymode && argn < argc)
619 		recipe=argv[argn++];
620 	else
621 	{
622 		if (!deliverymode)	deliverymode="";
623 
624 		if (*deliverymode)
625 		{
626 
627 #if DOVECOTAUTH
628 			if (dovecotauth_addr)
629 			{
630 				found = find_in_dovecotauth(dovecotauth_addr, &maildrop, deliverymode);
631 			}
632 			else
633 #endif
634 			{
635 				found = find_in_authlib(&maildrop, deliverymode);
636 			}
637 
638 			if ( !found )
639 			{
640 				my_pw=getpwnam(deliverymode);
641 				if (!my_pw)
642 					nouser();
643 				if (
644 #if	RESET_GID
645 				    setgroupid(my_pw->pw_gid) < 0
646 #else
647 				    (geteuid() == 0 && setgroupid(getegid()) < 0)
648 #endif
649 				     ||
650 				    setuid(my_pw->pw_uid) < 0)
651 				{
652 					nochangeuidgid();
653 				}
654 				if (getuid() != my_pw->pw_uid)
655 					nochangeuidgid(); // Security violation.
656 
657 				maildrop.init_home=my_pw->pw_dir;
658 				maildrop.init_logname=my_pw->pw_name;
659 				maildrop.init_shell=
660 					my_pw->pw_shell && *my_pw->pw_shell
661 						? my_pw->pw_shell:"/bin/sh";
662 				maildrop.init_default=
663 					GetDefaultMailbox(my_pw->pw_name);
664 				found=1;
665 			}
666 		}
667 		maildrop.isdelivery=1;
668 #if	RESTRICT_TRUSTED
669 		if ( getuid() != orig_uid && !trusted_uidgid(orig_uid,
670 			orig_gid, orig_gid2))
671 		{
672 			errexit=EX_TEMPFAIL;
673 			throw "You are not a trusted user.";
674 					// Security violation
675 		}
676 #endif
677 	}
678 
679 #if	HAVE_COURIER
680 #if	RESTRICT_TRUSTED
681 	if (numuidgid)
682 	{
683 	uid_t	un=0;
684 	gid_t	gn=getgid();
685 
686 		if (deliverymode && *deliverymode)
687 		{
688 			errexit=EX_TEMPFAIL;
689 			throw "Cannot use both -d and -D options.";
690 		}
691 
692 		if ( !trusted_uidgid(orig_uid, orig_gid, orig_gid2))
693 		{
694 			errexit=EX_TEMPFAIL;
695 			throw "You are not authorized to use the -D option.";
696 		}
697 
698 		if (!isdigit( (int)(unsigned char)*numuidgid))
699 		{
700 			errexit=EX_TEMPFAIL;
701 			throw "Invalid -D option.";
702 		}
703 
704 		do
705 		{
706 			un=un * 10 + (*numuidgid++ - '0');
707 		} while (isdigit( (int)(unsigned char)*numuidgid));
708 
709 		if ( *numuidgid )
710 		{
711 			if ( *numuidgid++ != '/' ||
712 				!isdigit( (int)(unsigned char)*numuidgid))
713 			{
714 				errexit=EX_TEMPFAIL;
715 				throw "Invalid -D option.";
716 			}
717 			gn=0;
718 			do
719 			{
720 				gn=gn * 10 + (*numuidgid++ - '0');
721 			} while (isdigit( (int)(unsigned char)*numuidgid));
722 
723 			if ( *numuidgid )
724 			{
725 				errexit=EX_TEMPFAIL;
726 				throw "Invalid -D option.";
727 			}
728 		}
729 		if (setgroupid(gn) < 0 ||
730 		    setuid(un) < 0)
731 		{
732 			perror("setuid/setgid");
733 			exit(1);
734 		}
735 		deliverymode="";
736 		orig_uid=un;	/* See below for another Courier hook */
737 	}
738 #endif
739 #endif
740 
741 
742 #if	RESET_GID
743 	if (setgroupid(getgid()) < 0)
744 	{
745 		perror("setgid");
746 		exit(1);
747 	}
748 #endif
749 
750 uid_t	my_u=getuid();
751 
752 	if (setuid(my_u) < 0)	// Drop any setuid privileges.
753 	{
754 		perror("setuid");
755 		exit(1);
756 	}
757 
758 	if (!found)
759 	{
760 #if HAVE_COURIER
761 		if (!deliverymode)
762 #endif
763 		{
764 			my_pw=getpwuid(my_u);
765 			if (!my_pw)
766 			{
767 				errexit=EX_TEMPFAIL;
768 				throw "Cannot determine my username.";
769 			}
770 
771 			maildrop.init_home=my_pw->pw_dir;
772 			maildrop.init_logname=my_pw->pw_name;
773 			maildrop.init_shell=
774 				my_pw->pw_shell && *my_pw->pw_shell
775 					? my_pw->pw_shell:"/bin/sh";
776 			maildrop.init_default=
777 				GetDefaultMailbox(my_pw->pw_name);
778 		}
779 	}
780 
781 int	i;
782 Buffer	name;
783 Buffer	value;
784 
785 	for (i=0; environ[i]; i++)
786 	{
787 		name=environ[i];
788 
789 		char	*p=strchr(environ[i], '=');
790 
791 		value= p ? (name.Length(p - environ[i]), p+1):"";
792 
793 		if (maildrop.isdelivery)
794 		{
795 			if (name == "LANG" ||
796 			    name == "LANGUAGE" ||
797 			    strncmp(name, "LC_", 3) == 0)
798 				;
799 			else
800 				continue;
801 		}
802 
803 		SetVar(name, value);
804 	}
805 
806 	i=1;
807 	while (argn < argc)
808 	{
809 		name="";
810 		name.append( (unsigned long)i);
811 		value=argv[argn++];
812 		SetVar(name, value);
813 		++i;
814 	}
815 
816 #if	HAVE_COURIER
817 	if (deliverymode && orig_uid == getuid())
818 	{
819 	const char *p;
820 
821 		if ((p=getenv("HOME")) && *p)
822 			maildrop.init_home=p;
823 
824 		if ((p=getenv("LOGNAME")) && *p)
825 			maildrop.init_logname=p;
826 
827 		if ((p=getenv("SHELL")) && *p)
828 			maildrop.init_shell=p;
829 
830 		p=getenv("MAILDROPDEFAULT");
831 
832 		if (!p || !*p)
833 		{
834 			p=getenv("LOCAL");
835 
836 			if (p && *p)
837 				p=GetDefaultMailbox(p);
838 			else
839 				p="./Maildir";
840 		}
841 		maildrop.init_default=p;
842 
843 		if ((p=getenv("MAILDIRQUOTA")) && *p)
844 			maildrop.init_quota=p;
845 	}
846 #endif
847 
848 	if (deliverymode)
849 	{
850 	struct	stat	buf;
851 	Buffer	b;
852 
853 		b=maildrop.init_home;
854 		b += '\0';
855 
856 	const char *h=b;
857 
858 		if (VerboseLevel() > 1)
859 			merr << "maildrop: Changing to " << h << "\n";
860 
861 		if (chdir(h) < 0)
862 		{
863 			errexit=EX_TEMPFAIL;
864 			throw "Unable to change to home directory.";
865 		}
866 		recipe=".mailfilter";
867 
868 		if ( stat(".", &buf) < 0)
869 			tempfail("Cannot stat() home directory.");
870 		if ( !S_ISDIR(buf.st_mode))
871 			tempfail("Home directory is not a directory.");
872 		if ( buf.st_mode & S_IWOTH)
873 			tempfail("Invalid home directory permissions - world writable.");
874 		if ( buf.st_uid != getuid())
875 			tempfail("Home directory owned by wrong user.");
876 
877 		// Quietly terminate if the sticky bit is set on the homedir
878 
879 		if ( buf.st_mode & S_ISVTX)
880 			return (EX_TEMPFAIL);
881 
882 		if (embedded_filter)
883 		{
884 			i=stat(".mailfilters", &buf);
885 
886 			if ( i < 0 && errno != ENOENT)
887 				tempfail("Unable to read $HOME/.mailfilters.");
888 			else if ( i >= 0)
889 			{
890 				if ( !S_ISDIR(buf.st_mode))
891 					tempfail("$HOME/.mailfilters should be a directory.");
892 				if ( buf.st_mode & (S_IRWXO|S_IRWXG))
893 					tempfail("Invalid permissions on $HOME/.mailfilters - remove world and group perms.");
894 				if ( buf.st_uid != getuid())
895 					tempfail("Invalid user ownership of $HOME/.mailfilters.");
896 			}
897 			recipe = embedded_filter;
898 		}
899 	}
900 #if	SHARED_TEMPDIR
901 
902 #else
903 	maildrop.tempdir=maildrop.init_home;
904 	maildrop.tempdir += "/" TEMPDIR;
905 	maildrop.tempdir += '\0';
906 	mkdir( (const char *)maildrop.tempdir, 0700 );
907 #endif
908 	maildrop.reset_vars();
909 
910 Buffer	msg;
911 
912 	maildrop.global_timer.Set(GLOBAL_TIMEOUT);
913 	maildrop.msgptr->Init(0, extra_headers); // Read message from standard input.
914 	maildrop.msginfo.info( *maildrop.msgptr );
915 	maildrop.msgptr->setmsgsize();
916 
917 
918 	if (!from)	from="";
919 	if (*from)	maildrop.msginfo.fromname=from;
920 
921 // If invoking user is trusted, trust the From line, else set it to invoking
922 // user.
923 
924 	if (
925 #if KEEP_FROMLINE
926 
927 // The original From_ line is kept, if necessary
928 
929 #else
930 
931 // If invoking user is trusted, trust the From line, else set it to invoking
932 // user.
933 		!trusted_uidgid(orig_uid, orig_gid, orig_gid2) ||
934 #endif
935 
936 		maildrop.msginfo.fromname.Length() == 0)
937 	{
938 		maildrop.msginfo.fromname=maildrop.init_logname;
939 	}
940 
941 	if (explicit_from)
942 		maildrop.msginfo.fromname=from;
943 
944 	name="FROM";
945 	value=maildrop.msginfo.fromname;
946 	SetVar(name, value);
947 
948 	if (VerboseLevel() > 1)
949 	{
950 		msg.reset();
951 		msg.append("Message envelope sender=");
952 		if (maildrop.msginfo.fromname.Length() > 0)
953 			msg += maildrop.msginfo.fromname;
954 		msg.append("\n");
955 		msg += '\0';
956 		merr.write(msg);
957 	}
958 
959 	name="HOSTNAME";
960 	sethostname(value);
961 	SetVar(name, value);
962 
963 int	fd;
964 
965 	//
966 	//
967 
968 	if (!embedded_filter && deliverymode)
969 	{
970 	Recipe r;
971 	Lexer in;
972 
973 		fd=in.Open(ETCDIR "/maildroprc");
974 		if (fd < 0)
975 		{
976 			if (errno != ENOENT)
977 			{
978 				errexit=EX_TEMPFAIL;
979 				throw "Error opening " ETCDIR "/maildroprc.";
980 			}
981 		}
982 		else
983 		{
984 			if (r.ParseRecipe(in) < 0)
985 				return (EX_TEMPFAIL);
986 			r.ExecuteRecipe();
987 		}
988 	}
989 
990 Recipe	r;
991 Lexer	in;
992 
993 #ifdef	DEFAULTEXT
994 int	firstdefault=1;
995 #endif
996 
997 	name="MAILFILTER";
998 	value=recipe;
999 	SetVar(name, value);
1000 
1001 	for (;;)
1002 	{
1003 		if (embedded_filter)
1004 		{
1005 			msg=".mailfilters/";
1006 			msg += recipe;
1007 			if (VerboseLevel() > 1)
1008 				merr << "maildrop: Attempting " << msg << "\n";
1009 			msg += '\0';
1010 			fd=in.Open((const char *)msg);
1011 		}
1012 		else
1013 		{
1014 			msg=recipe;
1015 			if (VerboseLevel() > 1)
1016 				merr << "maildrop: Attempting " << msg << "\n";
1017 			msg += '\0';
1018 			fd=in.Open((const char *)msg);
1019 			break;
1020 		}
1021 #ifndef	DEFAULTEXT
1022 		break;
1023 #else
1024 		if (fd >= 0)	break;
1025 		if (errno != ENOENT)	break;
1026 
1027 		if (firstdefault)
1028 		{
1029 			recipe += DEFAULTEXT;
1030 			firstdefault=0;
1031 			continue;
1032 		}
1033 
1034 		// Pop DEFAULTEXT bytes from end of recipe name
1035 
1036 		for (fd=sizeof(DEFAULTEXT)-1; fd; --fd)
1037 			recipe.pop();
1038 
1039 		while (recipe.Length())
1040 		{
1041 			if (recipe.pop() == '-')
1042 			{
1043 				recipe += DEFAULTEXT;
1044 				break;
1045 			}
1046 		}
1047 		if (recipe.Length() == 0)
1048 		{
1049 			msg=".mailfilters/";
1050 			msg += DEFAULTEXT+1;
1051 			if (VerboseLevel() > 1)
1052 				merr << "maildrop: Attempting " << msg << "\n";
1053 			msg += '\0';
1054 			fd=in.Open((const char *)msg);
1055 			break;
1056 		}
1057 #endif
1058 	}
1059 
1060 	if (fd < 0)
1061 	{
1062 		//
1063 		//  If we are operating in delivery mode, it's ok if
1064 		//  .mailfilter does not exist.
1065 		//
1066 		if (!deliverymode || errno != ENOENT)
1067 		{
1068 		static char buf[80];
1069 
1070 			sprintf(buf, "Unable to open filter file, errno=%d.",
1071 				errno);
1072 			errexit=EX_TEMPFAIL;
1073 			throw buf;
1074 		}
1075 	}
1076 	else
1077 	{
1078 	struct	stat	stat_buf;
1079 
1080 		setprocgroup();
1081 
1082 		if (fstat( fd, &stat_buf) != 0)
1083 			tempfail("stat() failed.");
1084 
1085 		if (!S_ISREG(stat_buf.st_mode))
1086 			tempfail("mailfilter file isn't a regular file.");
1087 		if (stat_buf.st_mode & (S_IRWXO | S_IRWXG))
1088 			tempfail("Cannot have world/group permissions on the filter file - for your own good.");
1089 		if (stat_buf.st_uid != getuid())
1090 			tempfail("mailfilter file is owned by the wrong user.");
1091 
1092 		if (r.ParseRecipe(in) < 0)
1093 			return (EX_TEMPFAIL);
1094 
1095 //		if (maildrop.msgptr->MessageSize() > 0)
1096 			r.ExecuteRecipe();
1097 	}
1098 //
1099 // If a message is successfully delivered, an exception is thrown.
1100 // If we're here, we should deliver to the default mailbox.
1101 //
1102 
1103 	if (!maildrop.embedded_mode)
1104 	{
1105 		name="DEFAULT";
1106 
1107 	const char *v=GetVarStr(name);
1108 
1109 		if (!v)
1110 		{
1111 			errexit=EX_TEMPFAIL;
1112 			throw "DEFAULT mailbox not defined.";
1113 		}
1114 
1115 		value=v;
1116 		value += '\0';
1117 		if (delivery((const char *)value) < 0)
1118 			return (EX_TEMPFAIL);
1119 	}
1120 
1121 	value="EXITCODE";
1122 	return ( GetVar(value)->Int("0") );
1123 }
1124 
main(int argc,char ** argv)1125 int main(int argc, char **argv)
1126 {
1127 
1128 #if HAVE_SETLOCALE
1129 	setlocale(LC_ALL, "C");
1130 #endif
1131 
1132 	_exit(Maildrop::trap(run, argc, argv));
1133 }
1134 
GetDefaultMailbox(const char * username)1135 const char *GetDefaultMailbox(const char *username)
1136 {
1137 static Buffer buf;
1138 int	isfile=0;
1139 
1140 	buf="";
1141 
1142 const	char *p=DEFAULT_DEF;
1143 
1144 	if (*p != SLASH_CHAR)	// Relative to home directory
1145 	{
1146 		buf=maildrop.init_home;
1147 		buf.push(SLASH_CHAR);
1148 		isfile=1;
1149 	}
1150 
1151 	while (*p)
1152 	{
1153 		if (*p != '=')
1154 		{
1155 			buf.push(*p);
1156 			++p;
1157 		}
1158 
1159 	const char *q=username;
1160 
1161 		while (*p == '=')
1162 		{
1163 			buf.push (*q ? *q:'.');
1164 			if (*q)	q++;
1165 			p++;
1166 		}
1167 	}
1168 
1169 	if (!isfile)
1170 	{
1171 		buf.push(SLASH_CHAR);
1172 		buf += username;
1173 	}
1174 	buf += '\0';
1175 	return (buf);
1176 }
1177 
1178 #if	SHARED_TEMPDIR
1179 
1180 #else
TempName()1181 const char *TempName()
1182 {
1183 Buffer	t;
1184 
1185 	t=(const char *)maildrop.tempdir;
1186 	t += "/tmp.";
1187 	t += '\0';
1188 	return (TempName((const char *)t, 0));
1189 }
1190 #endif
1191