ifcfg,keyfile: fix temporary file races (CVE-2016-0764)

Two of these raised Coverity's eyebrows.

CID 59389 (#1 of 1): Insecure temporary file (SECURE_TEMP)
5.  secure_temp: Calling mkstemp without securely setting umask first.

CID 59388 (#1 of 1): Insecure temporary file (SECURE_TEMP)
1.  secure_temp: Calling mkstemp without securely setting umask first.

Last one raised mine.

When a connection is edited and saved, there's a small window during which and
unprivileged authenticated local user can read out connection secrets (e.g. a
VPN or Wi-Fi password). The security impact is perhaps of low severity as
there's no way to force another user to save their connection.
This commit is contained in:
Lubomir Rintel
2016-01-29 10:27:10 +01:00
parent 503b714f15
commit 60b7ed3bdc
2 changed files with 21 additions and 34 deletions

View File

@@ -144,11 +144,15 @@ write_secret_file (const char *path,
char *tmppath; char *tmppath;
int fd = -1, written; int fd = -1, written;
gboolean success = FALSE; gboolean success = FALSE;
mode_t saved_umask;
tmppath = g_malloc0 (strlen (path) + 10); tmppath = g_malloc0 (strlen (path) + 10);
memcpy (tmppath, path, strlen (path)); memcpy (tmppath, path, strlen (path));
strcat (tmppath, ".XXXXXX"); strcat (tmppath, ".XXXXXX");
/* Only readable by root */
saved_umask = umask (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
errno = 0; errno = 0;
fd = mkstemp (tmppath); fd = mkstemp (tmppath);
if (fd < 0) { if (fd < 0) {
@@ -158,17 +162,6 @@ write_secret_file (const char *path,
goto out; goto out;
} }
/* Only readable by root */
errno = 0;
if (fchmod (fd, S_IRUSR | S_IWUSR)) {
close (fd);
unlink (tmppath);
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
"Could not set permissions for temporary file '%s': %d",
path, errno);
goto out;
}
errno = 0; errno = 0;
written = write (fd, data, len); written = write (fd, data, len);
if (written != len) { if (written != len) {
@@ -193,6 +186,7 @@ write_secret_file (const char *path,
success = TRUE; success = TRUE;
out: out:
umask (saved_umask);
g_free (tmppath); g_free (tmppath);
return success; return success;
} }

View File

@@ -46,12 +46,16 @@ write_cert_key_file (const char *path,
char *tmppath; char *tmppath;
int fd = -1, written; int fd = -1, written;
gboolean success = FALSE; gboolean success = FALSE;
mode_t saved_umask;
tmppath = g_malloc0 (strlen (path) + 10); tmppath = g_malloc0 (strlen (path) + 10);
g_assert (tmppath); g_assert (tmppath);
memcpy (tmppath, path, strlen (path)); memcpy (tmppath, path, strlen (path));
strcat (tmppath, ".XXXXXX"); strcat (tmppath, ".XXXXXX");
/* Only readable by root */
saved_umask = umask (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
errno = 0; errno = 0;
fd = mkstemp (tmppath); fd = mkstemp (tmppath);
if (fd < 0) { if (fd < 0) {
@@ -61,17 +65,6 @@ write_cert_key_file (const char *path,
goto out; goto out;
} }
/* Only readable by root */
errno = 0;
if (fchmod (fd, S_IRUSR | S_IWUSR) != 0) {
close (fd);
unlink (tmppath);
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
"Could not set permissions for temporary file '%s': %d",
path, errno);
goto out;
}
errno = 0; errno = 0;
written = write (fd, data, data_len); written = write (fd, data, data_len);
if (written != data_len) { if (written != data_len) {
@@ -96,6 +89,7 @@ write_cert_key_file (const char *path,
} }
out: out:
umask (saved_umask);
g_free (tmppath); g_free (tmppath);
return success; return success;
} }
@@ -241,6 +235,8 @@ _internal_write_connection (NMConnection *connection,
WriteInfo info = { 0 }; WriteInfo info = { 0 };
GError *local_err = NULL; GError *local_err = NULL;
int errsv; int errsv;
gboolean success = FALSE;
mode_t saved_umask;
g_return_val_if_fail (!out_path || !*out_path, FALSE); g_return_val_if_fail (!out_path || !*out_path, FALSE);
g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE); g_return_val_if_fail (keyfile_dir && keyfile_dir[0] == '/', FALSE);
@@ -324,13 +320,15 @@ _internal_write_connection (NMConnection *connection,
if (existing_path != NULL && strcmp (path, existing_path) != 0) if (existing_path != NULL && strcmp (path, existing_path) != 0)
unlink (existing_path); unlink (existing_path);
saved_umask = umask (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
g_file_set_contents (path, data, len, &local_err); g_file_set_contents (path, data, len, &local_err);
if (local_err) { if (local_err) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED, g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
"error writing to file '%s': %s", "error writing to file '%s': %s",
path, local_err->message); path, local_err->message);
g_error_free (local_err); g_error_free (local_err);
return FALSE; goto out;
} }
if (chown (path, owner_uid, owner_grp) < 0) { if (chown (path, owner_uid, owner_grp) < 0) {
@@ -339,23 +337,18 @@ _internal_write_connection (NMConnection *connection,
"error chowning '%s': %s (%d)", "error chowning '%s': %s (%d)",
path, g_strerror (errsv), errsv); path, g_strerror (errsv), errsv);
unlink (path); unlink (path);
return FALSE; goto out;
}
if (chmod (path, S_IRUSR | S_IWUSR) < 0) {
errsv = errno;
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
"error setting permissions on '%s': %s (%d)",
path, g_strerror (errsv), errsv);
unlink (path);
return FALSE;
} }
if (out_path && g_strcmp0 (existing_path, path)) { if (out_path && g_strcmp0 (existing_path, path)) {
*out_path = path; /* pass path out to caller */ *out_path = path; /* pass path out to caller */
path = NULL; path = NULL;
} }
return TRUE;
success = TRUE;
out:
umask (saved_umask);
return success;
} }
gboolean gboolean