File: 0011-Avoid-TOCTTOU-on-tempdir-and-destdir.patch

package info (click to toggle)
safecat 1.13%2Bgit20170317.185e8bf-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 776 kB
  • sloc: ansic: 2,748; makefile: 4
file content (205 lines) | stat: -rw-r--r-- 6,619 bytes parent folder | download
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
From: =?utf-8?b?0L3QsNCx?= <nabijaczleweli@nabijaczleweli.xyz>
Date: Fri, 22 Aug 2025 17:21:55 +0200
Subject: Avoid TOCTTOU on tempdir and destdir

---
 safecat.c   | 38 +++++++++++++-------------------------
 sig.c       |  5 +++--
 stat_dir.c  | 19 ++++++++++++-------
 stat_dir.h  |  2 +-
 writefile.c |  5 +++--
 5 files changed, 32 insertions(+), 37 deletions(-)

diff --git a/safecat.c b/safecat.c
index 335e8f7..17d1f5f 100644
--- a/safecat.c
+++ b/safecat.c
@@ -35,7 +35,8 @@
 
 /* Function prototypes. */
 void die_nomem() { strerr_die_x(111,"safecat: fatal: ","out of memory"); }
-stralloc tmppath = {0};
+int tempdirfd = -1;
+stralloc outfile = {0};
 
 /* ****************************************************************** */
 int main(int argc, char *argv[]) {
@@ -43,9 +44,7 @@ int main(int argc, char *argv[]) {
   char *destdir  = NULL;
   int   outfd    = 0;
   FILE *outfdf   = NULL;
-  stralloc dstpath = {0};
-  stralloc outfile = {0};
-  stralloc outpath = {0};
+  int destdirfd = -1;
   struct stat filestat;
   unsigned int count = 0;
 
@@ -63,24 +62,14 @@ int main(int argc, char *argv[]) {
   set_alarm_handler();
 
   /* Step 1:  Check that the supplied directories are OK. */
-  stat_dir(tempdir);
-  stat_dir(destdir);
+  tempdirfd = stat_dir(tempdir);
+  destdirfd = stat_dir(destdir);
 
   /* Step 2:  Stat the temporary file.  Wait for ENOENT as a response. */
   for(count=1;;count++) {
     /* Get the temporary filename to use now for dumping data. */
     mk_tempfile(&outfile);
-    if (!stralloc_cats(&outpath,tempdir)) die_nomem();
-    if (!stralloc_append(&outpath, '/')) die_nomem();
-    if (!stralloc_cat(&outpath,&outfile)) die_nomem();
-    if(stat(outpath.s,&filestat) == -1 && errno == ENOENT) {
-      if (!stralloc_cats(&dstpath, destdir)) die_nomem();
-      if (!stralloc_append(&dstpath, '/')) die_nomem();
-      if (!stralloc_cat(&dstpath,&outfile)) die_nomem();
-
-      if (!stralloc_cats(&tmppath, tempdir)) die_nomem();
-      if (!stralloc_append(&tmppath, '/')) die_nomem();
-      if (!stralloc_cat(&tmppath,&outfile)) die_nomem();
+    if(fstatat(tempdirfd,outfile.s,&filestat,0) == -1 && errno == ENOENT) {
       break;
     }
 
@@ -91,20 +80,19 @@ int main(int argc, char *argv[]) {
 
     /* Wait 2 seconds, and try again. */
     stralloc_copys(&outfile,"");
-    stralloc_copys(&outpath,"");
     sleep(2);
   }
 
   /* Step 4:  Create the file tempdir/time.MusecPpid.host */
   alarm(86400);
-  outfd = open(tmppath.s,O_WRONLY | O_EXCL | O_CREAT | O_LARGEFILE,0666);
+  outfd = openat(tempdirfd,outfile.s,O_WRONLY | O_EXCL | O_CREAT | O_LARGEFILE,0666);
   if(outfd == -1) {
     strerr_die_sys(111,"safecat: fatal: ","couldn't create output file: ");
   }
 
   outfdf = fdopen(outfd, "w");
   if (!outfdf) {
-    unlink(tmppath.s);
+    unlinkat(tempdirfd,outfile.s,0);
     strerr_die_sys(111,"safecat: fatal: ","can't fdopen output file: ");
   }
 
@@ -113,24 +101,24 @@ int main(int argc, char *argv[]) {
 
   /* Close the file, checking the return value. */
   if(fsync(outfd) == -1 || fclose(outfdf) == -1) {
-    unlink(tmppath.s);
+    unlinkat(tempdirfd,outfile.s,0);
     strerr_die_sys(111,"safecat: fatal: ","can't fsync/close output file: ");
   }
 
 #if RENAME_NOREPLACE
-  if(renameat2(AT_FDCWD,tmppath.s,AT_FDCWD,dstpath.s,RENAME_NOREPLACE) != 0)
+  if(renameat2(tempdirfd,outfile.s,destdirfd,outfile.s,RENAME_NOREPLACE) != 0)
 #endif
   {
 
   /* Step 6:  Link the temp file to its final destination. */
-  if(link(tmppath.s,dstpath.s) == -1) {
-    unlink(tmppath.s);
+  if(linkat(tempdirfd,outfile.s,destdirfd,outfile.s,0) == -1) {
+    unlinkat(tempdirfd,outfile.s,0);
     strerr_die_sys(111,"safecat: fatal: ","can't link output file: ");
   }
   /* We've succeeded!  Now, no matter what, we return "success" */
 
   /* Okay, delete the temporary file. If it fails, bummer. */
-  unlink(tmppath.s);
+  unlinkat(tempdirfd,outfile.s,0);
 
   }
   /* Print the name of the file we've created, as a curtesy. */
diff --git a/sig.c b/sig.c
index d474b81..9d2c519 100644
--- a/sig.c
+++ b/sig.c
@@ -6,9 +6,10 @@
 #include <unistd.h>
 
 /* ****************************************************************** */
-extern stralloc tmppath;
+extern int tempdirfd;
+extern stralloc outfile;
 static void alarm_handler(int sig) {
-  unlink(tmppath.s);
+  unlinkat(tempdirfd,outfile.s,0);
   strerr_die_x(111,"safecat: fatal: ","Timer has expired; giving up");
 }
 /* ****************************************************************** */
diff --git a/stat_dir.c b/stat_dir.c
index dbffd93..9dbf2ad 100644
--- a/stat_dir.c
+++ b/stat_dir.c
@@ -1,22 +1,27 @@
 /* Copyright (c) 2000, Len Budney. See COPYING for details. */
 
+#define _GNU_SOURCE
 #include "stat_dir.h"
 #include "strerr.h"
 #include <sys/stat.h>
 #include <unistd.h>
+#include <fcntl.h>
+
+#ifndef O_PATH
+#define O_PATH O_RDONLY
+#endif
 
 /* ****************************************************************** */
-void stat_dir(char *dirname) {
+int stat_dir(char *dirname) {
   struct stat filestat;
+  int ret = open(dirname, O_PATH | O_DIRECTORY);
 
-  if(stat(dirname,&filestat) != 0) {
-    strerr_die_sys(111,"safecat: fatal: ","could not stat directory: ");
-  }
-  if( !S_ISDIR(filestat.st_mode) ) {
-    strerr_die_x(111, "safecat: fatal: ","not a directory");
+  if(ret == -1 || fstat(ret, &filestat) != 0) {
+    strerr_die_sys(111,"safecat: fatal: ","could not open directory ",dirname,": ");
   }
   if((filestat.st_mode & S_IWUSR) != S_IWUSR) {
-    strerr_die_x(111, "safecat: fatal: ","directory not writable");
+    strerr_die_x(111, "safecat: fatal: ","directory ",dirname," not writable");
   }
+  return ret;
 }
 /* ****************************************************************** */
diff --git a/stat_dir.h b/stat_dir.h
index 12bd783..79718d0 100644
--- a/stat_dir.h
+++ b/stat_dir.h
@@ -1,2 +1,2 @@
 /* Check whether a directory exists, is a directory, and is writable. */
-void stat_dir(char *dirname);
+int stat_dir(char *dirname);
diff --git a/writefile.c b/writefile.c
index 02414b9..f91cb84 100644
--- a/writefile.c
+++ b/writefile.c
@@ -12,7 +12,8 @@
 #include <errno.h>
 #include <unistd.h>
 
-extern stralloc tmppath;
+extern int tempdirfd;
+extern stralloc outfile;
 
 /* ****************************************************************** */
 void writefile(FILE * fd) {
@@ -55,7 +56,7 @@ void writefile(FILE * fd) {
   return;
 
  fail:
-  unlink(tmppath.s);
+  unlinkat(tempdirfd,outfile.s,0);
   strerr_die_x(111,"safecat: fatal: ","unable to copy standard input");
 }
 /* ****************************************************************** */