1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
|
commit 7e67d082737b3df4788caf85fedd607b3acd9786
Author: Peter Seebach <peter.seebach@windriver.com>
Date: Fri May 16 15:53:06 2014 -0500
permissions updates: improve fchmodat, mask out write bits
Upstream-Status: Backport of several patches from 1.6 branch,
combined.
Backport from pseudo 1.6 of improvements to fchmodat (handle
AT_SYMLINK_NOFOLLOW by rejecting it if the host system does,
to make GNU tar happier), also mask out write bits from filesystem
modes to avoid security problems.
Also start tracking umask so we can use the right modes for
open, mkdir, and mknod.
The 1.6 patches are:
87c53ea58befef48677846693aab445df1850e16
3c716e0bab4f0cfe4be84caa9ce5fd5e3f5e2a23
c98e4f43b5d6499748a5057134408f4ba4854fb4
2f71a021b725c1aa415439209a89327f0b997d02
14925786b55202d8147b0af719038e8a23ef73c0
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 113f675..cc966ce 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -1,3 +1,18 @@
+2014-05-27:
+ * (seebs) start noticing umask, mask it out from open or mkdir
+ calls rather than relying on underlying open/mkdir to do it.
+
+2014-05-16:
+ * (seebs) fchmodat: don't drop flags, report failures, to improve
+ compatibility/consistency. Cache the knowledge that
+ AT_SYMLINK_NOFOLLOW gets ENOTSUP.
+ * (seebs) mask out group/other write bits in real filesystem to
+ reduce risks when assembling a rootfs including world-writeable
+ directories.
+
+2014-05-15:
+ * (seebs) drop flags when calling fchmodat() to appease GNU tar.
+
2013-02-27:
* (seebs) Oh, hey, what if I took out my debug messages?
* (seebs) update docs a bit to reduce bitrot
diff --git a/makewrappers b/makewrappers
index e87cc56..0127766 100755
--- a/makewrappers
+++ b/makewrappers
@@ -204,6 +204,7 @@ class Function:
'uid_t': '0',
'int': '-1',
'long': '-1',
+ 'mode_t': '0',
'ssize_t': '-1'
}
diff --git a/ports/darwin/guts/open.c b/ports/darwin/guts/open.c
index c66cc15..520bb70 100644
--- a/ports/darwin/guts/open.c
+++ b/ports/darwin/guts/open.c
@@ -9,6 +9,9 @@
struct stat buf = { };
int existed = 1;
int save_errno;
+
+ /* mask out mode bits appropriately */
+ mode = mode & ~pseudo_umask;
#ifdef PSEUDO_FORCE_ASYNCH
flags &= ~O_SYNC;
#endif
diff --git a/ports/linux/guts/__xmknodat.c b/ports/linux/guts/__xmknodat.c
index 59b4f2f..0888b8a 100644
--- a/ports/linux/guts/__xmknodat.c
+++ b/ports/linux/guts/__xmknodat.c
@@ -9,6 +9,9 @@
pseudo_msg_t *msg;
struct stat64 buf;
+ /* mask out mode bits appropriately */
+ mode = mode & ~pseudo_umask;
+
/* we don't use underlying call, so _ver is irrelevant to us */
(void) ver;
diff --git a/ports/linux/guts/openat.c b/ports/linux/guts/openat.c
index 8460073..4053549 100644
--- a/ports/linux/guts/openat.c
+++ b/ports/linux/guts/openat.c
@@ -10,6 +10,9 @@
int existed = 1;
int save_errno;
+ /* mask out mode bits appropriately */
+ mode = mode & ~pseudo_umask;
+
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
if (dirfd != AT_FDCWD) {
errno = ENOSYS;
diff --git a/ports/unix/guts/fchmodat.c b/ports/unix/guts/fchmodat.c
index 59a92ce..69a953c 100644
--- a/ports/unix/guts/fchmodat.c
+++ b/ports/unix/guts/fchmodat.c
@@ -8,6 +8,7 @@
*/
PSEUDO_STATBUF buf;
int save_errno = errno;
+ static int picky_fchmodat = 0;
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
if (dirfd != AT_FDCWD) {
@@ -15,6 +16,16 @@
return -1;
}
if (flags & AT_SYMLINK_NOFOLLOW) {
+ /* Linux, as of this writing, will always reject this.
+ * GNU tar relies on getting the rejection. To cut down
+ * on traffic, we check for the failure, and if we saw
+ * a failure previously, we reject it right away and tell
+ * the caller to retry.
+ */
+ if (picky_fchmodat) {
+ errno = ENOTSUP;
+ return -1;
+ }
rc = base_lstat(path, &buf);
} else {
rc = base_stat(path, &buf);
@@ -50,13 +61,22 @@
/* user bits added so "root" can always access files. */
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
- /* note: if path was a symlink, and AT_NOFOLLOW_SYMLINKS was
+ /* note: if path was a symlink, and AT_SYMLINK_NOFOLLOW was
* specified, we already bailed previously. */
real_chmod(path, PSEUDO_FS_MODE(mode, S_ISDIR(buf.st_mode)));
#else
- real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode, S_ISDIR(buf.st_mode)), flags);
+ rc = real_fchmodat(dirfd, path, PSEUDO_FS_MODE(mode, S_ISDIR(buf.st_mode)), flags);
+ /* AT_SYMLINK_NOFOLLOW isn't supported by fchmodat. GNU tar
+ * tries to use it anyway, figuring it can just retry if that
+ * fails. So we want to report that *particular* failure instead
+ * of doing the fallback.
+ */
+ if (rc == -1 && errno == ENOTSUP && (flags & AT_SYMLINK_NOFOLLOW)) {
+ picky_fchmodat = 1;
+ return -1;
+ }
#endif
- /* we ignore a failure from underlying fchmod, because pseudo
+ /* we otherwise ignore failures from underlying fchmod, because pseudo
* may believe you are permitted to change modes that the filesystem
* doesn't. Note that we also don't need to know whether the
* file might be a (pseudo) block device or some such; pseudo
diff --git a/ports/unix/guts/mkdirat.c b/ports/unix/guts/mkdirat.c
index e846b70..e0b6af9 100644
--- a/ports/unix/guts/mkdirat.c
+++ b/ports/unix/guts/mkdirat.c
@@ -6,11 +6,14 @@
* wrap_mkdirat(int dirfd, const char *path, mode_t mode) {
* int rc = -1;
*/
+ /* mask out mode bits appropriately */
+ mode = mode & ~pseudo_umask;
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
if (dirfd != AT_FDCWD) {
errno = ENOSYS;
return -1;
}
+
rc = real_mkdir(path, PSEUDO_FS_MODE(mode, 1));
#else
rc = real_mkdirat(dirfd, path, PSEUDO_FS_MODE(mode, 1));
diff --git a/ports/unix/guts/mknodat.c b/ports/unix/guts/mknodat.c
index 6fd5b42..5d8d47c 100644
--- a/ports/unix/guts/mknodat.c
+++ b/ports/unix/guts/mknodat.c
@@ -10,6 +10,9 @@
PSEUDO_STATBUF buf;
int save_errno = errno;
+ /* mask out mode bits appropriately */
+ mode = mode & ~pseudo_umask;
+
#ifdef PSEUDO_NO_REAL_AT_FUNCTIONS
if (dirfd != AT_FDCWD) {
errno = ENOSYS;
diff --git a/ports/unix/guts/umask.c b/ports/unix/guts/umask.c
new file mode 100644
index 0000000..6b060d3
--- /dev/null
+++ b/ports/unix/guts/umask.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014 Wind River Systems; see
+ * guts/COPYRIGHT for information.
+ *
+ * mode_t umask(mode_t mask)
+ * mode_t rc = 0;
+ */
+
+ pseudo_umask = mask;
+ rc = real_umask(mask);
+
+/* return rc;
+ * }
+ */
diff --git a/ports/unix/wrapfuncs.in b/ports/unix/wrapfuncs.in
index 8460a65..e0e9739 100644
--- a/ports/unix/wrapfuncs.in
+++ b/ports/unix/wrapfuncs.in
@@ -67,3 +67,4 @@ void sync(void); /* async_skip= */
int syncfs(int fd); /* async_skip=0 */
int sync_file_range(int fd, off64_t offset, off64_t nbytes, unsigned int flags); /* async_skip=0 */
int msync(void *addr, size_t length, int flags); /* async_skip=0 */
+mode_t umask(mode_t mask);
diff --git a/pseudo_client.c b/pseudo_client.c
index b6d11a6..535c810 100644
--- a/pseudo_client.c
+++ b/pseudo_client.c
@@ -71,6 +71,8 @@ int pseudo_disabled = 0;
int pseudo_allow_fsync = 0;
static int pseudo_local_only = 0;
+int pseudo_umask = 022;
+
static char **fd_paths = NULL;
static int nfds = 0;
static int messages = 0;
@@ -219,6 +221,9 @@ pseudo_init_client(void) {
if (!pseudo_disabled && !pseudo_inited) {
char *pseudo_path = 0;
+ pseudo_umask = umask(022);
+ umask(pseudo_umask);
+
pseudo_path = pseudo_prefix_path(NULL);
if (pseudo_prefix_dir_fd == -1) {
if (pseudo_path) {
diff --git a/pseudo_client.h b/pseudo_client.h
index f36a772..5bf820e 100644
--- a/pseudo_client.h
+++ b/pseudo_client.h
@@ -72,6 +72,8 @@ extern char *pseudo_passwd;
extern size_t pseudo_chroot_len;
extern int pseudo_nosymlinkexp;
+extern int pseudo_umask;
+
/* Root can read and write files, and enter directories which have no
* read, write, or execute permissions. (But can't execute files without
* execute permissions!)
@@ -85,6 +87,6 @@ extern int pseudo_nosymlinkexp;
* None of this will behave very sensibly if umask has 0700 bits in it;
* this is a known limitation.
*/
-#define PSEUDO_FS_MODE(mode, isdir) ((mode) | S_IRUSR | S_IWUSR | ((isdir) ? S_IXUSR : 0))
-#define PSEUDO_DB_MODE(fs_mode, user_mode) (((fs_mode) & ~0700) | ((user_mode & 0700)))
+#define PSEUDO_FS_MODE(mode, isdir) (((mode) | S_IRUSR | S_IWUSR | ((isdir) ? S_IXUSR : 0)) & ~(S_IWGRP | S_IWOTH))
+#define PSEUDO_DB_MODE(fs_mode, user_mode) (((fs_mode) & ~0722) | ((user_mode & 0722)))
|