1 /* $OpenBSD: locking.c,v 1.11 2014/01/17 21:42:47 tobias 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/param.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 <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <stdarg.h>
43 #include "pathnames.h"
44 #include "mail.local.h"
45
46 static char lpath[MAXPATHLEN];
47
48 void
rellock(void)49 rellock(void)
50 {
51
52 if (lpath[0])
53 unlink(lpath);
54 }
55
56 int
getlock(char * name,struct passwd * pw)57 getlock(char *name, struct passwd *pw)
58 {
59 struct stat sb, fsb;
60 int lfd=-1;
61 char buf[8*1024];
62 int tries = 0;
63
64 (void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
65 _PATH_MAILDIR, name);
66
67 if (stat(_PATH_MAILDIR, &sb) != -1 &&
68 (sb.st_mode & S_IWOTH) == S_IWOTH) {
69 /*
70 * We have a writeable spool, deal with it as
71 * securely as possible.
72 */
73 time_t ctim = -1;
74
75 seteuid(pw->pw_uid);
76 if (lstat(lpath, &sb) != -1)
77 ctim = sb.st_ctime;
78 while (1) {
79 /*
80 * Deal with existing user.lock files
81 * or directories or symbolic links that
82 * should not be here.
83 */
84 if (readlink(lpath, buf, sizeof buf-1) != -1) {
85 if (lstat(lpath, &sb) != -1 &&
86 S_ISLNK(sb.st_mode)) {
87 seteuid(sb.st_uid);
88 unlink(lpath);
89 seteuid(pw->pw_uid);
90 }
91 goto again;
92 }
93 if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK,
94 S_IRUSR|S_IWUSR)) != -1)
95 break;
96 again:
97 if (tries > 10) {
98 merr(NOTFATAL, "%s: %s", lpath,
99 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 merr(NOTFATAL, "%s: %s", lpath, strerror(errno));
132 return(-1);
133 }
134 sleep(1U << tries);
135 tries++;
136 }
137 }
138 return(lfd);
139 }
140
141 void
baditem(char * path)142 baditem(char *path)
143 {
144 char npath[MAXPATHLEN];
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 merr(NOTFATAL, "nasty spool item %s renamed to %s",
157 path, npath);
158 /* XXX if we fail to rename, another attempt will happen later */
159 }
160
161 void
merr(int isfatal,const char * fmt,...)162 merr(int isfatal, const char *fmt, ...)
163 {
164 va_list ap;
165
166 va_start(ap, fmt);
167 vsyslog(LOG_ERR, fmt, ap);
168 va_end(ap);
169 if (isfatal)
170 exit(1);
171 }
172