kuvert/kuvert_submit.c

250 lines
6.7 KiB
C

/*
*
* this file is part of kuvert, a wrapper around your mta that
* does pgp/gpg signing/signing+encrypting transparently, based
* on the content of your public keyring(s) and your preferences.
*
* copyright (c) 1999-2008 Alexander Zangerl <az+kuvert@snafu.priv.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stdio.h>
#include <pwd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <syslog.h>
#include <stdlib.h>
#define CONFFILE "/.kuvert"
#define DEFAULT_QUEUEDIR "/.kuvert_queue"
#define BUFLEN 65536
#define FALLBACKMTA "/usr/sbin/sendmail"
#define BAILOUT(a,...) {fprintf(stderr,"%s: ",argv[0]); fprintf(stderr, a "\n",##__VA_ARGS__);syslog(LOG_ERR,a,##__VA_ARGS__); exit(1);}
int main(int argc,char **argv)
{
struct passwd *pwentry;
/* fixme sizes */
char filen[256],buffer[BUFLEN],dirn[256];
int res,c,spaceleft;
char *p,*dirnp;
FILE *out;
FILE *cf;
struct stat statbuf;
int direct=1,norecips=0,testmode=0,i;
/* determine whether to queue stuff or to call sendmail
directly: if there is no proper config file for kuvert in $HOME
or if given -bv go direct, otherwise we enqueue. */
openlog(argv[0],LOG_NDELAY|LOG_PID,LOG_MAIL);
for(i=1;i<argc;++i)
{
if (!strncmp(argv[i],"-bv",3))
{
testmode=1;
syslog(LOG_INFO,"-bv argument present, running sendmail.");
break;
}
}
if (!testmode)
{
/* look for config file in $HOME */
pwentry=getpwuid(getuid());
if (!pwentry)
BAILOUT("getpwuid failed: %s",strerror(errno));
/* open and scan the conffile for an queue-file definition
if there is no conffile, kuvert wont work ever */
if (snprintf(filen,sizeof(filen),"%s%s",pwentry->pw_dir,CONFFILE)==-1)
BAILOUT("overlong filename, suspicious",NULL);
if (!(cf=fopen(filen,"r")))
{
/* no config file -> exec sendmail */
syslog(LOG_INFO,"user has no "CONFFILE" config file, running sendmail");
}
else
{
direct=0;
/* scan the lines for ^QUEUEDIR\s+ */
dirnp=NULL;
while(!feof(cf))
{
p=fgets(buffer,sizeof(buffer)-1,cf);
/* empty file? ok, we'll ignore it */
if (!p)
break;
if (!strncasecmp(buffer,"QUEUEDIR",sizeof("QUEUEDIR")-1))
{
p=buffer+sizeof("QUEUEDIR")-1;
for(;*p && isspace(*p);++p)
;
if (*p)
{
dirnp=p;
/* strip the newline from the string */
for(;*p && *p != '\n';++p)
;
if (*p == '\n')
*p=0;
/* strip eventual trailing whitespace */
for(--p;p>dirnp && isspace(*p);--p)
*p=0;
}
/* empty dir? ignore it */
if (strlen(dirnp)<2)
dirnp=NULL;
break;
}
}
fclose(cf);
}
}
/* direct to sendmail requested? */
if (direct)
{
/* mangle argv[0], so that it gets recognizeable by sendmail */
argv[0]=FALLBACKMTA;
*buffer=0;
/* bah, c stringhandling is ugly... i just want all args
in one string for a nice syslog line... */
for(c=0,spaceleft=sizeof(buffer);
c<argc;
spaceleft-=strlen(argv[c++]))
{
if (spaceleft <= 0)
BAILOUT("overlong command line, suspicious.",NULL);
strncat(buffer,argv[c],spaceleft);
--spaceleft && c<argc-1 && strcat(buffer," ");
}
syslog(LOG_INFO,"executing MTA '%s' directly",buffer);
execv(FALLBACKMTA,argv);
/* must not reach here */
BAILOUT("execv "FALLBACKMTA" failed: %s",strerror(errno));
}
/* otherwise queue the stuff for kuvert,
first check queuedir and create if missing */
if (!dirnp)
{
if(snprintf(dirn,sizeof(dirn),"%s%s",pwentry->pw_dir,DEFAULT_QUEUEDIR)
==-1)
BAILOUT("overlong dirname, suspicous.",NULL);
dirnp=dirn;
}
res=stat(dirnp,&statbuf);
if (res)
{
if (errno == ENOENT)
{
/* seems to be missing -> try to create it */
if (mkdir(dirnp,0700))
BAILOUT("mkdir %s failed: %s\n",dirnp,strerror(errno));
}
else
BAILOUT("stat %s failed: %s\n",dirnp,strerror(errno));
}
else if (!S_ISDIR(statbuf.st_mode))
{
BAILOUT("%s is not a directory",dirnp);
}
else if (statbuf.st_uid != getuid())
{
BAILOUT("%s is not owned by you - refusing to run",dirnp);
}
else if ((statbuf.st_mode & 0777) != 0700)
{
BAILOUT("%s does not have mode 0700 - refusing to run",dirnp);
}
umask(066); /* absolutely no access for group/others... */
/* dir does exist now */
snprintf(filen,sizeof(filen),"%s/%d",dirnp,getpid());
/* file create and lock */
if (!(out=fopen(filen,"a")))
{
BAILOUT("fopen %s failed: %s\n",filen,strerror(errno));
}
if (flock(fileno(out),LOCK_EX))
{
BAILOUT("flock failed: %s\n",strerror(errno));
}
/* scan the arguments for the LSB-mandated options:
we ignore any options but -f, -t.
/* no getopt error messages, please! */
opterr=0;
while ((c=getopt(argc,argv,"f:t"))!=-1)
{
if (c=='?')
continue; /* we simply ignore uninteresting options */
else if (c=='f')
{
/* pass the intended envelope sender */
fprintf(out,"X-Kuvert-From: %s\n",optarg);
}
else if (c=='t')
{
/* no recipients given, so we don't need to pass any recips */
norecips=1;
}
}
if (!norecips && optind<argc)
{
fprintf(out,"X-Kuvert-To: ");
for(c=optind;c<argc;++c)
{
fprintf(out,"%s%s",argv[c],(c<argc-1?", ":"\n"));
}
}
fflush(out);
/* now finally put the data in the queuefile */
do
{
res=fread(buffer,1,BUFLEN,stdin);
if (!res && ferror(stdin))
BAILOUT("fread failure: %s",strerror(errno));
if (fwrite(buffer,1,res,out)!=res && ferror(out))
BAILOUT("fwrite failure: %s",strerror(errno));
}
while (res==BUFLEN);
if (fflush(out)==EOF)
BAILOUT("fflush failed: %s",strerror(errno));
if (flock(fileno(out),LOCK_UN))
{
BAILOUT("flock (unlock) failed: %s",strerror(errno));
}
if (fclose(out)==EOF)
BAILOUT("fclose failed: %s",strerror(errno));
return 0;
}