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");
}
/* ****************************************************************** */
|