xref: /openbsd-src/libexec/mail.local/locking.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
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