1 /* $OpenBSD: locking.c,v 1.8 2006/04/01 22:48:57 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 #ifndef lint 32 static char rcsid[] = "$OpenBSD: locking.c,v 1.8 2006/04/01 22:48:57 deraadt Exp $"; 33 #endif /* not lint */ 34 35 #include <sys/param.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <pwd.h> 39 #include <syslog.h> 40 #include <time.h> 41 #include <unistd.h> 42 #include <errno.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <stdarg.h> 47 #include "pathnames.h" 48 #include "mail.local.h" 49 50 static char lpath[MAXPATHLEN]; 51 52 void 53 rellock(void) 54 { 55 56 if (lpath[0]) 57 unlink(lpath); 58 } 59 60 int 61 getlock(char *name, struct passwd *pw) 62 { 63 struct stat sb, fsb; 64 int lfd=-1; 65 char buf[8*1024]; 66 int tries = 0; 67 68 (void)snprintf(lpath, sizeof lpath, "%s/%s.lock", 69 _PATH_MAILDIR, name); 70 71 if (stat(_PATH_MAILDIR, &sb) != -1 && 72 (sb.st_mode & S_IWOTH) == S_IWOTH) { 73 /* 74 * We have a writeable spool, deal with it as 75 * securely as possible. 76 */ 77 time_t ctim = -1; 78 79 seteuid(pw->pw_uid); 80 if (lstat(lpath, &sb) != -1) 81 ctim = sb.st_ctime; 82 while (1) { 83 /* 84 * Deal with existing user.lock files 85 * or directories or symbolic links that 86 * should not be here. 87 */ 88 if (readlink(lpath, buf, sizeof buf-1) != -1) { 89 if (lstat(lpath, &sb) != -1 && 90 S_ISLNK(sb.st_mode)) { 91 seteuid(sb.st_uid); 92 unlink(lpath); 93 seteuid(pw->pw_uid); 94 } 95 goto again; 96 } 97 if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK, 98 S_IRUSR|S_IWUSR)) != -1) 99 break; 100 again: 101 if (tries > 10) { 102 merr(NOTFATAL, "%s: %s", lpath, 103 strerror(errno)); 104 seteuid(0); 105 return(-1); 106 } 107 if (tries > 9 && 108 (lfd = open(lpath, O_WRONLY|O_EXLOCK, 0)) != -1) { 109 if (fstat(lfd, &fsb) != -1 && 110 lstat(lpath, &sb) != -1) { 111 if (fsb.st_dev == sb.st_dev && 112 fsb.st_ino == sb.st_ino && 113 ctim == fsb.st_ctime ) { 114 seteuid(fsb.st_uid); 115 baditem(lpath); 116 seteuid(pw->pw_uid); 117 } 118 } 119 } 120 sleep(1U << tries); 121 tries++; 122 continue; 123 } 124 seteuid(0); 125 } else { 126 /* 127 * Only root can write the spool directory. 128 */ 129 while (1) { 130 if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL, 131 S_IRUSR|S_IWUSR)) != -1) 132 break; 133 if (tries > 9) { 134 merr(NOTFATAL, "%s: %s", lpath, strerror(errno)); 135 return(-1); 136 } 137 sleep(1U << tries); 138 tries++; 139 } 140 } 141 return(lfd); 142 } 143 144 void 145 baditem(char *path) 146 { 147 char npath[MAXPATHLEN]; 148 149 if (unlink(path) == 0) 150 return; 151 snprintf(npath, sizeof npath, "%s/mailXXXXXXXXXX", _PATH_MAILDIR); 152 if (mktemp(npath) == NULL) 153 return; 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