jaromail

a commandline tool to easily and privately handle your e-mail
git clone git://parazyd.org/jaromail.git
Log | Files | Refs | Submodules | README

commit 62f0ccb0d60fd8abcc865a95d55c4888296f2ebc
parent 1b6377f437237bd7bbad948b91143d3eaa919384
Author: Jaromil <jaromil@dyne.org>
Date:   Wed, 12 Sep 2012 13:36:06 +0200

updated ignore list and mutt source

Diffstat:
M.gitignore | 29+++++++++++++++++++++++++++++
Asrc/mutt-1.5.21/mutt_dotlock.c | 758+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 787 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,6 +1,8 @@ *~ *.o *.zwc +*.gmo +.deps dotlock fetchaddr fetchdate @@ -21,3 +23,30 @@ src/mairix/fromcheck.c src/mairix/fromcheck.h src/mairix/nvpscan.c src/mairix/nvpscan.h +src/mutt-1.5.21/Makefile +src/mutt-1.5.21/config.h +src/mutt-1.5.21/config.log +src/mutt-1.5.21/config.status +src/mutt-1.5.21/contrib/Makefile +src/mutt-1.5.21/doc/Makefile +src/mutt-1.5.21/flea +src/mutt-1.5.21/hcachever.sh +src/mutt-1.5.21/hcversion.h +src/mutt-1.5.21/imap/Makefile +src/mutt-1.5.21/imap/libimap.a +src/mutt-1.5.21/intl/Makefile +src/mutt-1.5.21/keymap_defs.h +src/mutt-1.5.21/m4/Makefile +src/mutt-1.5.21/make.log +src/mutt-1.5.21/mutt_dotlock +src/mutt-1.5.21/mutt_md5 +src/mutt-1.5.21/muttbug.sh +src/mutt-1.5.21/patchlist.c +src/mutt-1.5.21/pgpewrap +src/mutt-1.5.21/pgpring +src/mutt-1.5.21/po/Makefile +src/mutt-1.5.21/po/Makefile.in +src/mutt-1.5.21/po/POTFILES +src/mutt-1.5.21/reldate.h +src/mutt-1.5.21/smime_keys +src/mutt-1.5.21/stamp-h1 diff --git a/src/mutt-1.5.21/mutt_dotlock.c b/src/mutt-1.5.21/mutt_dotlock.c @@ -0,0 +1,758 @@ +/* + * Copyright (C) 1996-2000 Michael R. Elkins <me@mutt.org> + * Copyright (C) 1998-2001,2007 Thomas Roessler <roessler@does-not-exist.org> + * + * 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 + * (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This module either be compiled into Mutt, or it can be + * built as a separate program. For building it + * separately, define the DL_STANDALONE preprocessor + * macro. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <dirent.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/utsname.h> +#include <errno.h> +#include <time.h> +#include <fcntl.h> +#include <limits.h> + +#ifndef _POSIX_PATH_MAX +#include <limits.h> +#endif + +#include "dotlock.h" + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +#ifdef DL_STANDALONE +# include "reldate.h" +#endif + +#define MAXLINKS 1024 /* maximum link depth */ + +#ifdef DL_STANDALONE + +# define LONG_STRING 1024 +# define MAXLOCKATTEMPT 5 + +# define strfcpy(A,B,C) strncpy (A,B,C), *(A+(C)-1)=0 + +# ifdef USE_SETGID + +# ifdef HAVE_SETEGID +# define SETEGID setegid +# else +# define SETEGID setgid +# endif +# ifndef S_ISLNK +# define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK ? 1 : 0) +# endif + +# endif + +# ifndef HAVE_SNPRINTF +extern int snprintf (char *, size_t, const char *, ...); +# endif + +#else /* DL_STANDALONE */ + +# ifdef USE_SETGID +# error Do not try to compile dotlock as a mutt module when requiring egid switching! +# endif + +# include "mutt.h" +# include "mx.h" + +#endif /* DL_STANDALONE */ + +static int DotlockFlags; +static int Retry = MAXLOCKATTEMPT; + +#ifdef DL_STANDALONE +static char *Hostname; +#endif + +#ifdef USE_SETGID +static gid_t UserGid; +static gid_t MailGid; +#endif + +static int dotlock_deference_symlink (char *, size_t, const char *); +static int dotlock_prepare (char *, size_t, const char *, int fd); +static int dotlock_check_stats (struct stat *, struct stat *); +static int dotlock_dispatch (const char *, int fd); + +#ifdef DL_STANDALONE +static int dotlock_init_privs (void); +static void usage (const char *); +#endif + +static void dotlock_expand_link (char *, const char *, const char *); +static void BEGIN_PRIVILEGED (void); +static void END_PRIVILEGED (void); + +/* These functions work on the current directory. + * Invoke dotlock_prepare () before and check their + * return value. + */ + +static int dotlock_try (void); +static int dotlock_unlock (const char *); +static int dotlock_unlink (const char *); +static int dotlock_lock (const char *); + + +#ifdef DL_STANDALONE + +#define check_flags(a) if (a & DL_FL_ACTIONS) usage (argv[0]) + +int main (int argc, char **argv) +{ + int i; + char *p; + struct utsname utsname; + + /* first, drop privileges */ + + if (dotlock_init_privs () == -1) + return DL_EX_ERROR; + + + /* determine the system's host name */ + + uname (&utsname); + if (!(Hostname = strdup (utsname.nodename))) /* __MEM_CHECKED__ */ + return DL_EX_ERROR; + if ((p = strchr (Hostname, '.'))) + *p = '\0'; + + + /* parse the command line options. */ + DotlockFlags = 0; + + while ((i = getopt (argc, argv, "dtfupr:")) != EOF) + { + switch (i) + { + /* actions, mutually exclusive */ + case 't': check_flags (DotlockFlags); DotlockFlags |= DL_FL_TRY; break; + case 'd': check_flags (DotlockFlags); DotlockFlags |= DL_FL_UNLINK; break; + case 'u': check_flags (DotlockFlags); DotlockFlags |= DL_FL_UNLOCK; break; + + /* other flags */ + case 'f': DotlockFlags |= DL_FL_FORCE; break; + case 'p': DotlockFlags |= DL_FL_USEPRIV; break; + case 'r': DotlockFlags |= DL_FL_RETRY; Retry = atoi (optarg); break; + + default: usage (argv[0]); + } + } + + if (optind == argc || Retry < 0) + usage (argv[0]); + + return dotlock_dispatch (argv[optind], -1); +} + + +/* + * Determine our effective group ID, and drop + * privileges. + * + * Return value: + * + * 0 - everything went fine + * -1 - we couldn't drop privileges. + * + */ + + +static int +dotlock_init_privs (void) +{ + +# ifdef USE_SETGID + + UserGid = getgid (); + MailGid = getegid (); + + if (SETEGID (UserGid) != 0) + return -1; + +# endif + + return 0; +} + + +#else /* DL_STANDALONE */ + +/* + * This function is intended to be invoked from within + * mutt instead of mx.c's invoke_dotlock (). + */ + +int dotlock_invoke (const char *path, int fd, int flags, int retry) +{ + int currdir; + int r; + + DotlockFlags = flags; + + if ((currdir = open (".", O_RDONLY)) == -1) + return DL_EX_ERROR; + + if (!(DotlockFlags & DL_FL_RETRY) || retry) + Retry = MAXLOCKATTEMPT; + else + Retry = 0; + + r = dotlock_dispatch (path, fd); + + fchdir (currdir); + close (currdir); + + return r; +} + +#endif /* DL_STANDALONE */ + + +static int dotlock_dispatch (const char *f, int fd) +{ + char realpath[_POSIX_PATH_MAX]; + + /* If dotlock_prepare () succeeds [return value == 0], + * realpath contains the basename of f, and we have + * successfully changed our working directory to + * `dirname $f`. Additionally, f has been opened for + * reading to verify that the user has at least read + * permissions on that file. + * + * For a more detailed explanation of all this, see the + * lengthy comment below. + */ + + if (dotlock_prepare (realpath, sizeof (realpath), f, fd) != 0) + return DL_EX_ERROR; + + /* Actually perform the locking operation. */ + + if (DotlockFlags & DL_FL_TRY) + return dotlock_try (); + else if (DotlockFlags & DL_FL_UNLOCK) + return dotlock_unlock (realpath); + else if (DotlockFlags & DL_FL_UNLINK) + return dotlock_unlink (realpath); + else /* lock */ + return dotlock_lock (realpath); +} + + +/* + * Get privileges + * + * This function re-acquires the privileges we may have + * if the user told us to do so by giving the "-p" + * command line option. + * + * BEGIN_PRIVILEGES () won't return if an error occurs. + * + */ + +static void +BEGIN_PRIVILEGED (void) +{ +#ifdef USE_SETGID + if (DotlockFlags & DL_FL_USEPRIV) + { + if (SETEGID (MailGid) != 0) + { + /* perror ("setegid"); */ + exit (DL_EX_ERROR); + } + } +#endif +} + +/* + * Drop privileges + * + * This function drops the group privileges we may have. + * + * END_PRIVILEGED () won't return if an error occurs. + * + */ + +static void +END_PRIVILEGED (void) +{ +#ifdef USE_SETGID + if (DotlockFlags & DL_FL_USEPRIV) + { + if (SETEGID (UserGid) != 0) + { + /* perror ("setegid"); */ + exit (DL_EX_ERROR); + } + } +#endif +} + +#ifdef DL_STANDALONE + +/* + * Usage information. + * + * This function doesn't return. + * + */ + +static void +usage (const char *av0) +{ + fprintf (stderr, "dotlock [Mutt %s (%s)]\n", VERSION, ReleaseDate); + fprintf (stderr, "usage: %s [-t|-f|-u|-d] [-p] [-r <retries>] file\n", + av0); + + fputs ("\noptions:" + "\n -t\t\ttry" + "\n -f\t\tforce" + "\n -u\t\tunlock" + "\n -d\t\tunlink" + "\n -p\t\tprivileged" +#ifndef USE_SETGID + " (ignored)" +#endif + "\n -r <retries>\tRetry locking" + "\n", stderr); + + exit (DL_EX_ERROR); +} + +#endif + +/* + * Access checking: Let's avoid to lock other users' mail + * spool files if we aren't permitted to read them. + * + * Some simple-minded access (2) checking isn't sufficient + * here: The problem is that the user may give us a + * deeply nested path to a file which has the same name + * as the file he wants to lock, but different + * permissions, say, e.g. + * /tmp/lots/of/subdirs/var/spool/mail/root. + * + * He may then try to replace /tmp/lots/of/subdirs by a + * symbolic link to / after we have invoked access () to + * check the file's permissions. The lockfile we'd + * create or remove would then actually be + * /var/spool/mail/root. + * + * To avoid this attack, we proceed as follows: + * + * - First, follow symbolic links a la + * dotlock_deference_symlink (). + * + * - get the result's dirname. + * + * - chdir to this directory. If you can't, bail out. + * + * - try to open the file in question, only using its + * basename. If you can't, bail out. + * + * - fstat that file and compare the result to a + * subsequent lstat (only using the basename). If + * the comparison fails, bail out. + * + * dotlock_prepare () is invoked from main () directly + * after the command line parsing has been done. + * + * Return values: + * + * 0 - Evereything's fine. The program's new current + * directory is the contains the file to be locked. + * The string pointed to by bn contains the name of + * the file to be locked. + * + * -1 - Something failed. Don't continue. + * + * tlr, Jul 15 1998 + */ + +static int +dotlock_check_stats (struct stat *fsb, struct stat *lsb) +{ + /* S_ISLNK (fsb->st_mode) should actually be impossible, + * but we may have mixed up the parameters somewhere. + * play safe. + */ + + if (S_ISLNK (lsb->st_mode) || S_ISLNK (fsb->st_mode)) + return -1; + + if ((lsb->st_dev != fsb->st_dev) || + (lsb->st_ino != fsb->st_ino) || + (lsb->st_mode != fsb->st_mode) || + (lsb->st_nlink != fsb->st_nlink) || + (lsb->st_uid != fsb->st_uid) || + (lsb->st_gid != fsb->st_gid) || + (lsb->st_rdev != fsb->st_rdev) || + (lsb->st_size != fsb->st_size)) + { + /* something's fishy */ + return -1; + } + + return 0; +} + +static int +dotlock_prepare (char *bn, size_t l, const char *f, int _fd) +{ + struct stat fsb, lsb; + char realpath[_POSIX_PATH_MAX]; + char *basename, *dirname; + char *p; + int fd; + int r; + + if (dotlock_deference_symlink (realpath, sizeof (realpath), f) == -1) + return -1; + + if ((p = strrchr (realpath, '/'))) + { + *p = '\0'; + basename = p + 1; + dirname = realpath; + } + else + { + basename = realpath; + dirname = "."; + } + + if (strlen (basename) + 1 > l) + return -1; + + strfcpy (bn, basename, l); + + if (chdir (dirname) == -1) + return -1; + + if (_fd != -1) + fd = _fd; + else if ((fd = open (basename, O_RDONLY)) == -1) + return -1; + + r = fstat (fd, &fsb); + + if (_fd == -1) + close (fd); + + if (r == -1) + return -1; + + if (lstat (basename, &lsb) == -1) + return -1; + + if (dotlock_check_stats (&fsb, &lsb) == -1) + return -1; + + return 0; +} + +/* + * Expand a symbolic link. + * + * This function expects newpath to have space for + * at least _POSIX_PATH_MAX characters. + * + */ + +static void +dotlock_expand_link (char *newpath, const char *path, const char *link) +{ + const char *lb = NULL; + size_t len; + + /* link is full path */ + if (*link == '/') + { + strfcpy (newpath, link, _POSIX_PATH_MAX); + return; + } + + if ((lb = strrchr (path, '/')) == NULL) + { + /* no path in link */ + strfcpy (newpath, link, _POSIX_PATH_MAX); + return; + } + + len = lb - path + 1; + memcpy (newpath, path, len); + strfcpy (newpath + len, link, _POSIX_PATH_MAX - len); +} + + +/* + * Deference a chain of symbolic links + * + * The final path is written to d. + * + */ + +static int +dotlock_deference_symlink (char *d, size_t l, const char *path) +{ + struct stat sb; + char realpath[_POSIX_PATH_MAX]; + const char *pathptr = path; + int count = 0; + + while (count++ < MAXLINKS) + { + if (lstat (pathptr, &sb) == -1) + { + /* perror (pathptr); */ + return -1; + } + + if (S_ISLNK (sb.st_mode)) + { + char linkfile[_POSIX_PATH_MAX]; + char linkpath[_POSIX_PATH_MAX]; + int len; + + if ((len = readlink (pathptr, linkfile, sizeof (linkfile) - 1)) == -1) + { + /* perror (pathptr); */ + return -1; + } + + linkfile[len] = '\0'; + dotlock_expand_link (linkpath, pathptr, linkfile); + strfcpy (realpath, linkpath, sizeof (realpath)); + pathptr = realpath; + } + else + break; + } + + strfcpy (d, pathptr, l); + return 0; +} + +/* + * Dotlock a file. + * + * realpath is assumed _not_ to be an absolute path to + * the file we are about to lock. Invoke + * dotlock_prepare () before using this function! + * + */ + +#define HARDMAXATTEMPTS 10 + +static int +dotlock_lock (const char *realpath) +{ + char lockfile[_POSIX_PATH_MAX + LONG_STRING]; + char nfslockfile[_POSIX_PATH_MAX + LONG_STRING]; + size_t prev_size = 0; + int fd; + int count = 0; + int hard_count = 0; + struct stat sb; + time_t t; + + snprintf (nfslockfile, sizeof (nfslockfile), "%s.%s.%d", + realpath, Hostname, (int) getpid ()); + snprintf (lockfile, sizeof (lockfile), "%s.lock", realpath); + + + BEGIN_PRIVILEGED (); + + unlink (nfslockfile); + + while ((fd = open (nfslockfile, O_WRONLY | O_EXCL | O_CREAT, 0)) < 0) + { + END_PRIVILEGED (); + + + if (errno != EAGAIN) + { + /* perror ("cannot open NFS lock file"); */ + return DL_EX_ERROR; + } + + + BEGIN_PRIVILEGED (); + } + + END_PRIVILEGED (); + + + close (fd); + + while (hard_count++ < HARDMAXATTEMPTS) + { + + BEGIN_PRIVILEGED (); + link (nfslockfile, lockfile); + END_PRIVILEGED (); + + if (stat (nfslockfile, &sb) != 0) + { + /* perror ("stat"); */ + return DL_EX_ERROR; + } + + if (sb.st_nlink == 2) + break; + + if (count == 0) + prev_size = sb.st_size; + + if (prev_size == sb.st_size && ++count > Retry) + { + if (DotlockFlags & DL_FL_FORCE) + { + BEGIN_PRIVILEGED (); + unlink (lockfile); + END_PRIVILEGED (); + + count = 0; + continue; + } + else + { + BEGIN_PRIVILEGED (); + unlink (nfslockfile); + END_PRIVILEGED (); + return DL_EX_EXIST; + } + } + + prev_size = sb.st_size; + + /* don't trust sleep (3) as it may be interrupted + * by users sending signals. + */ + + t = time (NULL); + do { + sleep (1); + } while (time (NULL) == t); + } + + BEGIN_PRIVILEGED (); + unlink (nfslockfile); + END_PRIVILEGED (); + + return DL_EX_OK; +} + + +/* + * Unlock a file. + * + * The same comment as for dotlock_lock () applies here. + * + */ + +static int +dotlock_unlock (const char *realpath) +{ + char lockfile[_POSIX_PATH_MAX + LONG_STRING]; + int i; + + snprintf (lockfile, sizeof (lockfile), "%s.lock", + realpath); + + BEGIN_PRIVILEGED (); + i = unlink (lockfile); + END_PRIVILEGED (); + + if (i == -1) + return DL_EX_ERROR; + + return DL_EX_OK; +} + +/* remove an empty file */ + +static int +dotlock_unlink (const char *realpath) +{ + struct stat lsb; + int i = -1; + + if (dotlock_lock (realpath) != DL_EX_OK) + return DL_EX_ERROR; + + if ((i = lstat (realpath, &lsb)) == 0 && lsb.st_size == 0) + unlink (realpath); + + dotlock_unlock (realpath); + + return (i == 0) ? DL_EX_OK : DL_EX_ERROR; +} + + +/* + * Check if a file can be locked at all. + * + * The same comment as for dotlock_lock () applies here. + * + */ + +static int +dotlock_try (void) +{ +#ifdef USE_SETGID + struct stat sb; +#endif + + if (access (".", W_OK) == 0) + return DL_EX_OK; + +#ifdef USE_SETGID + if (stat (".", &sb) == 0) + { + if ((sb.st_mode & S_IWGRP) == S_IWGRP && sb.st_gid == MailGid) + return DL_EX_NEED_PRIVS; + } +#endif + + return DL_EX_IMPOSSIBLE; +}