I was recently reminded of this topic by a private message I received. It comes up periodically... Mutt should not ever write files with any permissions other than 0600 (-rw-------) because it can't know whether it's really safe to do so, and neither can you, until you've looked at it. As a reminder, to those who are interested, a summary of the points:
- Mutt's job PRIMARILY is to operate on untrusted data that the user
did not initiate receiving. It arrives largely unsolicited via the
internet. It (or any other mail client) is, by and large, the only
program for which this is true, that a user regularly uses.
- The user CAN NOT, UNDER ANY CIRCUMSTANCES, reliably determine the
sensitivity of any file attachment without examining the contents,
which requires saving it to disk.
- If you are on a multi-user system, you DO NOT WANT users being able
to see your sensitive files, so you DO NOT WANT Mutt to mistakenly
write them with lax permissions. If you are the only user on the
system, you DO NOT NEED this feature. Users often think their data
isn't sensitive... until they don't. It only takes once to wreck
your day (or possibly even your life, say if the attachment
contains all your financial account info).
- If your attachment turns out to be sensitive, and you've saved it
to a place with permissions where others can read it, you have lost
the moment it lands on the disk.
This last point is pretty key. I posted about this about a year ago,
when the most recent (that I have seen) request to add a patch to
change the umask was posted. In my post I mentioned that it was
trivial to write a program to watch someone's attachment directory and
copy their attachments as they are written to disk, well before any
human could change the permissions to make that impossible. The
method of doing this requires no time at all--the files only need to
be on disk long enough for the kernel to notify any watchers, and for
those watchers to open the file. This should take at most
milliseconds, and generally not even that. Once the files are open,
you can change the permissions or delete the file even (granted, this
behavior is somewhat platform-dependent), and it won't matter--your
would-be attacker has already got what they need.
As an academic exercise, I wrote just such a program. It's 2 pages of
C++ code (instead of C, only because string manipulation is much
easier). You just run it with the name of the directory you want to
watch, and the name of the directory you want the files copied to, and
it will dutifully copy any newly written files in the watched
directory, pretty much instantaneously (large attachments may take
non-negligible time to finish copying, but once started there's
nothing you can do to stop them). Point it at your attachment
directory, have mutt save some attachments, and watch it go! The
program has other legitimate uses, like copying a watched directory to
a network drive or back-up media, etc..
This is Linux-specific, but there are libraries out there that do the
same thing on pretty much all platforms...
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/inotify.h>
#include <string>
using std::string;
#define IGNORE_VAL(expr)\
({ typeof(expr) __ignval __attribute((unused)) = (expr); })
#define DIRSEP "/"
int fail(const char *fmt, ...)
{
va_list ap;
string tmpfmt(fmt);
tmpfmt += "\n";
va_start(ap, fmt);
IGNORE_VAL(vfprintf(stderr, tmpfmt.c_str(), ap));
va_end(ap);
return 1;
}
string assemble_path(string dir, string file)
{
size_t len = dir.length();
string rc_path;
if (dir.empty())
rc_path = file;
// len - 1 can't be negative here
else if (dir[len - 1] == '/')
rc_path = dir + file;
else
rc_path = dir + DIRSEP + file;
return rc_path;
}
int copy_file(const char *src, const char *dst)
{
int ifd, ofd;
struct stat s;
void *inmap, *outmap;
size_t fsize;
ifd = open(src, O_RDONLY);
if (ifd == -1)
return fail("%s: open failed (%s)", src, strerror(errno));
ofd = open(dst, O_RDWR|O_CREAT|O_TRUNC, 0600);
if (ofd == -1)
return fail("%s: open failed (%s)", dst, strerror(errno));
if (fstat(ifd, &s) == -1)
return fail("%s: failed getting file size (%s)", src, strerror(errno));
fsize = s.st_size;
if (lseek(ofd, fsize - 1, SEEK_SET) == -1)
return fail("%s: lseek failed (%s)", src, strerror(errno));
if (write(ofd, "\0", 1) == -1)
return fail("%s: write failed (%s)", dst, strerror(errno));
if ((inmap = mmap(NULL, fsize, PROT_READ, MAP_SHARED, ifd, 0)) == (void
*)-1)
return fail("%s: mmap failed (%s)", src, strerror(errno));
if ((outmap = mmap(NULL, fsize, PROT_WRITE, MAP_SHARED, ofd, 0)) == (void
*)-1)
return fail("%s: mmap failed (%s)", dst, strerror(errno));
memcpy(outmap, inmap, fsize);
munmap(outmap, fsize);
munmap(inmap, fsize);
return 0;
}
#define BUF_LEN (sizeof (struct inotify_event) + NAME_MAX + 1)
int main(int argc, char **argv)
{
int infd, wd;
char buf[BUF_LEN] __attribute__ ((aligned(8)));
ssize_t numRead;
char *p;
struct inotify_event *event;
if (argc < 2) return fail("Usage: %s watchdir [copydir]", argv[0]);
string watchdir(argv[1]);
string copydir(".");
if (argc == 3) copydir = string(argv[2]);
infd = inotify_init();
if ((infd = inotify_init()) == -1)
return fail("inotify_init");
if ((wd = inotify_add_watch(infd, argv[1], IN_ALL_EVENTS)) == -1)
return fail("%s: adding watch failed", argv[1]);
printf("Watching %s...\n", argv[1]);
for (;;) {
numRead = read(infd, buf, BUF_LEN);
if (numRead <= 0) return fail("read on inotify fd failed.");
// Should only ever read one
for (p = buf; p < buf + numRead; ) {
event = (struct inotify_event *)p;
if (event->mask & IN_CLOSE_WRITE){
string src = assemble_path(watchdir, event->name);
string dst = assemble_path(copydir, event->name);
printf("Copying newly written file (%s) to %s\n", src.c_str(),
dst.c_str());
IGNORE_VAL(copy_file(src.c_str(), dst.c_str()));
}
p += sizeof(struct inotify_event) + event->len;
}
}
// never reached
return 0;
}
--
Derek D. Martin http://www.pizzashack.org/ GPG Key ID: 0xDFBEAD02
-=-=-=-=-
This message is posted from an invalid address. Replying to it will result in
undeliverable mail due to spam prevention. Sorry for the inconvenience.
pgponRpGu3PlB.pgp
Description: PGP signature
