summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@redhat.com>2014-01-23 15:34:59 +0100
committerMartin Pitt <martin.pitt@ubuntu.com>2015-06-30 08:02:31 +0200
commit875ddfce1bc02ae8e81cbb05874d148132bec3b0 (patch)
tree15cf0d41ecfe40079b8e1e7f6db9f4080f3f391a
parentb208be44157253005ac053aeebe13e234a3ac291 (diff)
UDisksSpawnedJob: Retrieve uid/gid info before forking
A security analysis revealed a possible issue in the UDisksSpawnedJob code. Reportedly the NSS modules might not be available if the fork() happened at an inconvenient time. This patch moves retrieval of UID information in the parent process just before forking. https://bugs.freedesktop.org/show_bug.cgi?id=73669
-rw-r--r--src/udisksspawnedjob.c74
1 files changed, 48 insertions, 26 deletions
diff --git a/src/udisksspawnedjob.c b/src/udisksspawnedjob.c
index b181933..124c221 100644
--- a/src/udisksspawnedjob.c
+++ b/src/udisksspawnedjob.c
@@ -64,6 +64,10 @@ struct _UDisksSpawnedJob
gchar *input_string;
uid_t run_as_uid;
uid_t run_as_euid;
+ gid_t real_egid;
+ gid_t real_gid;
+ uid_t real_uid;
+ char *real_pwname;
const gchar *input_string_cursor;
GPid child_pid;
@@ -371,30 +375,10 @@ static void
child_setup (gpointer user_data)
{
UDisksSpawnedJob *job = UDISKS_SPAWNED_JOB (user_data);
- struct passwd pwstruct;
- gchar pwbuf[8192];
- struct passwd *pw = NULL;
- int rc;
- gid_t egid;
if (job->run_as_uid == getuid () && job->run_as_euid == geteuid ())
goto out;
- rc = getpwuid_r (job->run_as_euid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
- if (rc != 0 || pw == NULL)
- {
- g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as_euid);
- abort ();
- }
- egid = pw->pw_gid;
-
- rc = getpwuid_r (job->run_as_uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
- if (rc != 0 || pw == NULL)
- {
- g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as_uid);
- abort ();
- }
-
/* become the user...
*
* TODO: this might need to involve running the whole PAM 'session'
@@ -408,22 +392,22 @@ child_setup (gpointer user_data)
g_printerr ("Error resetting groups: %m\n");
abort ();
}
- if (initgroups (pw->pw_name, pw->pw_gid) != 0)
+ if (initgroups (job->real_pwname, job->real_gid) != 0)
{
g_printerr ("Error initializing groups for user %s and group %d: %m\n",
- pw->pw_name, (gint) pw->pw_gid);
+ job->real_pwname, (gint) job->real_gid);
abort ();
}
- if (setregid (pw->pw_gid, egid) != 0)
+ if (setregid (job->real_gid, job->real_egid) != 0)
{
g_printerr ("Error setting real+effective gid %d and %d: %m\n",
- (gint) pw->pw_gid, (gint) egid);
+ (gint) job->real_gid, (gint) job->real_egid);
abort ();
}
- if (setreuid (pw->pw_uid, job->run_as_euid) != 0)
+ if (setreuid (job->real_uid, job->run_as_euid) != 0)
{
g_printerr ("Error setting real+effective uid %d and %d: %m\n",
- (gint) pw->pw_uid, (gint) job->run_as_euid);
+ (gint) job->real_uid, (gint) job->run_as_euid);
abort ();
}
@@ -438,6 +422,10 @@ udisks_spawned_job_constructed (GObject *object)
GError *error;
gint child_argc;
gchar **child_argv;
+ struct passwd pwstruct;
+ gchar pwbuf[8192];
+ struct passwd *pw = NULL;
+ int rc;
if (G_OBJECT_CLASS (udisks_spawned_job_parent_class)->constructed != NULL)
G_OBJECT_CLASS (udisks_spawned_job_parent_class)->constructed (object);
@@ -474,6 +462,34 @@ udisks_spawned_job_constructed (GObject *object)
goto out;
}
+ /* Save real egid and gid info for the child process */
+ if (job->run_as_uid != getuid () || job->run_as_euid != geteuid ())
+ {
+ rc = getpwuid_r (job->run_as_euid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
+ if (rc != 0 || pw == NULL)
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "No password record for uid %d: %m\n", (gint) job->run_as_euid);
+ emit_completed_with_error_in_idle (job, error);
+ g_error_free (error);
+ goto out;
+ }
+ job->real_egid = pw->pw_gid;
+
+ rc = getpwuid_r (job->run_as_uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
+ if (rc != 0 || pw == NULL)
+ {
+ g_set_error(&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "No password record for uid %d: %m\n", (gint) job->run_as_uid);
+ emit_completed_with_error_in_idle (job, error);
+ g_error_free (error);
+ goto out;
+ }
+ job->real_gid = pw->pw_gid;
+ job->real_uid = pw->pw_uid;
+ job->real_pwname = strdup (pw->pw_name);
+ }
+
error = NULL;
if (!g_spawn_async_with_pipes (NULL, /* working directory */
child_argv,
@@ -962,5 +978,11 @@ udisks_spawned_job_release_resources (UDisksSpawnedJob *job)
g_cancellable_disconnect (udisks_base_job_get_cancellable (UDISKS_BASE_JOB (job)), job->cancellable_handler_id);
job->cancellable_handler_id = 0;
}
+
+ if (job->real_pwname != NULL)
+ {
+ free (job->real_pwname);
+ job->real_pwname = NULL;
+ }
}