Skip to content

Commit c3ab669

Browse files
committed
Handle opening the same file
We introduced a regression in 4.0.1 when closing the TOCTOU issue. We incorrectly refuse to open the same file twice to save both a CA and cert into the same location. This changes the opening logic to always open for both reading and writing to check if it is zero-length. Also switches the EC curve tests to output the CA and service cert to a common path to ensure we are testing this case properly. Updates some incorrect expected behaviors in the test_dhparams_creation test as well. Fixes: #99 Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
1 parent 9708ebc commit c3ab669

File tree

4 files changed

+96
-23
lines changed

4 files changed

+96
-23
lines changed

src/io_utils.c

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <string.h>
3838
#include <talloc.h>
3939
#include <sys/stat.h>
40+
#include <unistd.h>
4041

4142
#include "config.h"
4243
#ifdef HAVE_GETTEXT
@@ -102,17 +103,85 @@ static int
102103
sscg_io_utils_open_file (const char *path, bool overwrite, FILE **fp)
103104
{
104105
FILE *_fp = NULL;
105-
if (overwrite)
106-
_fp = fopen (path, "w");
107-
else
108-
_fp = fopen (path, "wx");
106+
struct stat st;
107+
int ret;
108+
int fd;
109+
110+
/* Try to open with r+ mode (file must exist) */
111+
_fp = fopen (path, "r+");
109112
if (!_fp)
110113
{
111-
SSCG_ERROR ("Could not open file %s: %s\n", path, strerror (errno));
112-
return errno;
114+
/* If file doesn't exist, create it with w+ mode */
115+
if (errno == ENOENT)
116+
{
117+
_fp = fopen (path, "w+");
118+
if (!_fp)
119+
{
120+
SSCG_ERROR (
121+
"Could not create file %s: %s\n", path, strerror (errno));
122+
return errno;
123+
}
124+
/* New file has size 0, so we can proceed */
125+
ret = EOK;
126+
goto done;
127+
}
128+
else
129+
{
130+
SSCG_ERROR ("Could not open file %s: %s\n", path, strerror (errno));
131+
ret = errno;
132+
goto done;
133+
}
134+
}
135+
136+
/* File exists and was opened successfully, check its size */
137+
fd = fileno (_fp);
138+
ret = fstat (fd, &st);
139+
if (ret != 0)
140+
{
141+
SSCG_ERROR ("Could not stat file %s: %s\n", path, strerror (errno));
142+
ret = errno;
143+
goto done;
113144
}
114-
*fp = _fp;
115-
return EOK;
145+
146+
/* Check if file size is greater than zero */
147+
if (st.st_size > 0)
148+
{
149+
if (overwrite)
150+
{
151+
/* Truncate the file */
152+
ret = ftruncate (fd, 0);
153+
if (ret != 0)
154+
{
155+
SSCG_ERROR (
156+
"Could not truncate file %s: %s\n", path, strerror (errno));
157+
ret = errno;
158+
goto done;
159+
}
160+
/* Rewind to beginning after truncation */
161+
rewind (_fp);
162+
}
163+
else
164+
{
165+
/* File exists and overwrite is false - return error */
166+
SSCG_ERROR ("File %s already exists\n", path);
167+
ret = EEXIST;
168+
goto done;
169+
}
170+
}
171+
172+
/* File size is zero or has been truncated */
173+
ret = EOK;
174+
175+
done:
176+
if (ret == EOK)
177+
{
178+
*fp = _fp;
179+
_fp = NULL;
180+
}
181+
182+
if (_fp)
183+
fclose (_fp);
184+
return ret;
116185
}
117186

118187

src/sscg.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@ main (int argc, const char **argv)
7575

7676
struct sscg_stream *stream = NULL;
7777

78-
/* Always use umask 0577 for generating certificates and keys
79-
This means that it's opened as write-only by the effective
80-
user. */
81-
umask (0577);
78+
/* Always use umask 0177 for generating certificates and keys
79+
This means that it's opened as read/write only by the effective
80+
user.
81+
*/
82+
umask (0177);
8283

8384
#ifdef HAVE_GETTEXT
8485
/* Initialize internationalization */

test/test_dhparams_creation.sh

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ set -e
5454
DHPARAMS_TMPDIR=$(mktemp --directory --tmpdir=$GITHUB_WORKSPACE sscg_dhparams_test_XXXXXX)
5555
WRITABLE_DIR="$DHPARAMS_TMPDIR/writable"
5656
READONLY_DIR="$DHPARAMS_TMPDIR/readonly"
57+
READONLY_WITH_DHPARAMS_DIR="$DHPARAMS_TMPDIR/readonly_with_dhparams"
5758
DHPARAMS_DIR="$DHPARAMS_TMPDIR/dhparams"
5859

5960
function cleanup {
@@ -69,14 +70,16 @@ trap cleanup EXIT
6970
# Set up test directories
7071
mkdir -p "$WRITABLE_DIR"
7172
mkdir -p "$READONLY_DIR"
73+
mkdir -p "$READONLY_WITH_DHPARAMS_DIR"
7274
mkdir -p "$DHPARAMS_DIR"
7375

7476
# Create a pre-existing dhparams.pem file for some tests
75-
touch "$DHPARAMS_DIR/dhparams.pem"
77+
echo "preexisting dhparams.pem" > "$DHPARAMS_DIR/dhparams.pem"
7678

7779
# Copy pre-existing dhparams.pem to readonly directory before making it readonly
78-
cp "$DHPARAMS_DIR/dhparams.pem" "$READONLY_DIR/dhparams.pem"
79-
chmod 555 "$READONLY_DIR"
80+
cp "$DHPARAMS_DIR/dhparams.pem" "$READONLY_WITH_DHPARAMS_DIR/dhparams.pem"
81+
chmod 0555 "$READONLY_DIR"
82+
chmod 0555 "$READONLY_WITH_DHPARAMS_DIR"
8083

8184
failed_tests=0
8285
total_tests=8
@@ -260,9 +263,9 @@ run_test \
260263
8 \
261264
"--dhparams-file to non-writable path, existing file" \
262265
"$WRITABLE_DIR" \
263-
"$READONLY_DIR/dhparams.pem" \
266+
"$READONLY_WITH_DHPARAMS_DIR/dhparams.pem" \
264267
17 \
265-
"$READONLY_DIR/dhparams.pem" \
268+
"$READONLY_WITH_DHPARAMS_DIR/dhparams.pem" \
266269
"false" \
267270
"$WRITABLE_DIR"
268271

test/test_ecdsa_cert_validity.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,18 @@ pushd "$TMPDIR"
185185
--key-type ecdsa \
186186
--ec-curve "$_arg_ec_curve" \
187187
--cert-key-password mypassword \
188-
--dhparams-file "dhparams.pem"
188+
--dhparams-file "dhparams.pem" \
189+
--ca-file "combined.crt" \
190+
--cert-file "combined.crt"
189191

190192
# Verify that the expected files were created
191193
test -e dhparams.pem
192-
test -e ca.crt
193-
test -e service.pem
194+
test -e combined.crt
194195
test -e service-key.pem
195196

196197
# Verify that they have the correct file format
197198
openssl dhparam -noout -in dhparams.pem
198-
openssl x509 -noout -in ca.crt
199-
openssl x509 -noout -in service.pem
199+
openssl x509 -noout -in combined.crt
200200

201201
grep "ENCRYPTED PRIVATE KEY" service-key.pem
202202
openssl pkey -noout -in service-key.pem -passin pass:mypassword
@@ -210,7 +210,7 @@ curve_info=$(openssl pkey -text -noout -in service-key.pem -passin pass:mypasswo
210210
echo "$curve_info" | grep "$_arg_ec_curve"
211211

212212
# Validate the certificates
213-
openssl verify -x509_strict -CAfile ca.crt service.pem
213+
openssl verify -x509_strict -CAfile combined.crt combined.crt
214214

215215
popd # $TMPDIR
216216

0 commit comments

Comments
 (0)