1 /* $OpenBSD: locking.c,v 1.12 2015/01/16 06:39:50 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com> 5 * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of the authors may not be used to endorse or promote products 17 * derived from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 20 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 21 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 22 * THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <fcntl.h> 34 #include <pwd.h> 35 #include <syslog.h> 36 #include <time.h> 37 #include <unistd.h> 38 #include <limits.h> 39 #include <errno.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <stdarg.h> 44 #include "pathnames.h" 45 #include "mail.local.h" 46 47 static char lpath[PATH_MAX]; 48 49 void 50 rellock(void) 51 { 52 53 if (lpath[0]) 54 unlink(lpath); 55 } 56 57 int 58 getlock(char *name, struct passwd *pw) 59 { 60 struct stat sb, fsb; 61 int lfd=-1; 62 char buf[8*1024]; 63 int tries = 0; 64 65 (void)snprintf(lpath, sizeof lpath, "%s/%s.lock", 66 _PATH_MAILDIR, name); 67 68 if (stat(_PATH_MAILDIR, &sb) != -1 && 69 (sb.st_mode & S_IWOTH) == S_IWOTH) { 70 /* 71 * We have a writeable spool, deal with it as 72 * securely as possible. 73 */ 74 time_t ctim = -1; 75 76 seteuid(pw->pw_uid); 77 if (lstat(lpath, &sb) != -1) 78 ctim = sb.st_ctime; 79 while (1) { 80 /* 81 * Deal with existing user.lock files 82 * or directories or symbolic links that 83 * should not be here. 84 */ 85 if (readlink(lpath, buf, sizeof buf-1) != -1) { 86 if (lstat(lpath, &sb) != -1 && 87 S_ISLNK(sb.st_mode)) { 88 seteuid(sb.st_uid); 89 unlink(lpath); 90 seteuid(pw->pw_uid); 91 } 92 goto again; 93 } 94 if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK, 95 S_IRUSR|S_IWUSR)) != -1) 96 break; 97 again: 98 if (tries > 10) { 99 merr(NOTFATAL, "%s: %s", lpath, 100 strerror(errno)); 101 seteuid(0); 102 return(-1); 103 } 104 if (tries > 9 && 105 (lfd = open(lpath, O_WRONLY|O_EXLOCK, 0)) != -1) { 106 if (fstat(lfd, &fsb) != -1 && 107 lstat(lpath, &sb) != -1) { 108 if (fsb.st_dev == sb.st_dev && 109 fsb.st_ino == sb.st_ino && 110 ctim == fsb.st_ctime ) { 111 seteuid(fsb.st_uid); 112 baditem(lpath); 113 seteuid(pw->pw_uid); 114 } 115 } 116 close(lfd); 117 } 118 sleep(1U << tries); 119 tries++; 120 continue; 121 } 122 seteuid(0); 123 } else { 124 /* 125 * Only root can write the spool directory. 126 */ 127 while (1) { 128 if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL, 129 S_IRUSR|S_IWUSR)) != -1) 130 break; 131 if (tries > 9) { 132 merr(NOTFATAL, "%s: %s", lpath, strerror(errno)); 133 return(-1); 134 } 135 sleep(1U << tries); 136 tries++; 137 } 138 } 139 return(lfd); 140 } 141 142 void 143 baditem(char *path) 144 { 145 char npath[PATH_MAX]; 146 int fd; 147 148 if (unlink(path) == 0) 149 return; 150 snprintf(npath, sizeof npath, "%s/mailXXXXXXXXXX", _PATH_MAILDIR); 151 if ((fd = mkstemp(npath)) == -1) 152 return; 153 close(fd); 154 if (rename(path, npath) == -1) 155 unlink(npath); 156 else 157 merr(NOTFATAL, "nasty spool item %s renamed to %s", 158 path, npath); 159 /* XXX if we fail to rename, another attempt will happen later */ 160 } 161 162 void 163 merr(int isfatal, const char *fmt, ...) 164 { 165 va_list ap; 166 167 va_start(ap, fmt); 168 vsyslog(LOG_ERR, fmt, ap); 169 va_end(ap); 170 if (isfatal) 171 exit(1); 172 } 173