1 /* $OpenBSD: locking.c,v 1.14 2020/02/09 14:59:20 millert 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(const 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 mwarn("%s: %s", lpath, strerror(errno)); 100 seteuid(0); 101 return(-1); 102 } 103 if (tries > 9 && 104 (lfd = open(lpath, O_WRONLY|O_EXLOCK, 0)) != -1) { 105 if (fstat(lfd, &fsb) != -1 && 106 lstat(lpath, &sb) != -1) { 107 if (fsb.st_dev == sb.st_dev && 108 fsb.st_ino == sb.st_ino && 109 ctim == fsb.st_ctime ) { 110 seteuid(fsb.st_uid); 111 baditem(lpath); 112 seteuid(pw->pw_uid); 113 } 114 } 115 close(lfd); 116 } 117 sleep(1U << tries); 118 tries++; 119 continue; 120 } 121 seteuid(0); 122 } else { 123 /* 124 * Only root can write the spool directory. 125 */ 126 while (1) { 127 if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL, 128 S_IRUSR|S_IWUSR)) != -1) 129 break; 130 if (tries > 9) { 131 mwarn("%s: %s", lpath, strerror(errno)); 132 return(-1); 133 } 134 sleep(1U << tries); 135 tries++; 136 } 137 } 138 return(lfd); 139 } 140 141 void 142 baditem(char *path) 143 { 144 char npath[PATH_MAX]; 145 int fd; 146 147 if (unlink(path) == 0) 148 return; 149 snprintf(npath, sizeof npath, "%s/mailXXXXXXXXXX", _PATH_MAILDIR); 150 if ((fd = mkstemp(npath)) == -1) 151 return; 152 close(fd); 153 if (rename(path, npath) == -1) 154 unlink(npath); 155 else 156 mwarn("nasty spool item %s renamed to %s", path, npath); 157 /* XXX if we fail to rename, another attempt will happen later */ 158 } 159 160 void 161 mwarn(const char *fmt, ...) 162 { 163 va_list ap; 164 165 va_start(ap, fmt); 166 vsyslog(LOG_ERR, fmt, ap); 167 va_end(ap); 168 } 169 170 void 171 merr(int eval, const char *fmt, ...) 172 { 173 va_list ap; 174 175 va_start(ap, fmt); 176 vsyslog(LOG_ERR, fmt, ap); 177 va_end(ap); 178 exit(eval); 179 } 180