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