diff options
author | Tomas Bzatek <tbzatek@redhat.com> | 2014-01-23 15:34:59 +0100 |
---|---|---|
committer | Martin Pitt <martin.pitt@ubuntu.com> | 2015-06-30 08:02:31 +0200 |
commit | 875ddfce1bc02ae8e81cbb05874d148132bec3b0 (patch) | |
tree | 15cf0d41ecfe40079b8e1e7f6db9f4080f3f391a | |
parent | b208be44157253005ac053aeebe13e234a3ac291 (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.c | 74 |
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; + } } |