From 129b6223a19e7fb2753f8cad7957ac5402394076 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Fri, 1 Apr 2011 12:09:45 -0400 Subject: [PATCH 2/4] Make PolkitUnixProcess also record the uid of the process This is needed to avoid possible TOCTTOU issues since a process can change both its real uid and effective uid. Signed-off-by: David Zeuthen --- docs/polkit/polkit-1-sections.txt | 7 +- src/polkit/polkitsubject.c | 25 +++- src/polkit/polkitunixprocess.c | 346 +++++++++++++++++++++++++------------ src/polkit/polkitunixprocess.h | 18 ++- 4 files changed, 278 insertions(+), 118 deletions(-) diff --git a/docs/polkit/polkit-1-sections.txt b/docs/polkit/polkit-1-sections.txt index 12141e3..9f4fcf8 100644 --- a/docs/polkit/polkit-1-sections.txt +++ b/docs/polkit/polkit-1-sections.txt @@ -145,10 +145,13 @@ POLKIT_UNIX_SESSION_GET_CLASS PolkitUnixProcess polkit_unix_process_new polkit_unix_process_new_full +polkit_unix_process_new_for_owner +polkit_unix_process_set_pid polkit_unix_process_get_pid +polkit_unix_process_set_start_time polkit_unix_process_get_start_time -polkit_unix_process_set_pid -polkit_unix_process_get_owner +polkit_unix_process_set_uid +polkit_unix_process_get_uid PolkitUnixProcessClass POLKIT_UNIX_PROCESS diff --git a/src/polkit/polkitsubject.c b/src/polkit/polkitsubject.c index 577afec..d2c4c20 100644 --- a/src/polkit/polkitsubject.c +++ b/src/polkit/polkitsubject.c @@ -238,13 +238,18 @@ polkit_subject_from_string (const gchar *str, { gint scanned_pid; guint64 scanned_starttime; - if (sscanf (str, "unix-process:%d:%" G_GUINT64_FORMAT, &scanned_pid, &scanned_starttime) == 2) + gint scanned_uid; + if (sscanf (str, "unix-process:%d:%" G_GUINT64_FORMAT ":%d", &scanned_pid, &scanned_starttime, &scanned_uid) == 3) + { + subject = polkit_unix_process_new_for_owner (scanned_pid, scanned_starttime, scanned_uid); + } + else if (sscanf (str, "unix-process:%d:%" G_GUINT64_FORMAT, &scanned_pid, &scanned_starttime) == 2) { subject = polkit_unix_process_new_full (scanned_pid, scanned_starttime); } else if (sscanf (str, "unix-process:%d", &scanned_pid) == 1) { - subject = polkit_unix_process_new_full (scanned_pid, 0); + subject = polkit_unix_process_new (scanned_pid); if (polkit_unix_process_get_start_time (POLKIT_UNIX_PROCESS (subject)) == 0) { g_object_unref (subject); @@ -297,6 +302,8 @@ polkit_subject_to_gvariant (PolkitSubject *subject) g_variant_new_uint32 (polkit_unix_process_get_pid (POLKIT_UNIX_PROCESS (subject)))); g_variant_builder_add (&builder, "{sv}", "start-time", g_variant_new_uint64 (polkit_unix_process_get_start_time (POLKIT_UNIX_PROCESS (subject)))); + g_variant_builder_add (&builder, "{sv}", "uid", + g_variant_new_int32 (polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)))); } else if (POLKIT_IS_UNIX_SESSION (subject)) { @@ -395,6 +402,7 @@ polkit_subject_new_for_gvariant (GVariant *variant, GVariant *v; guint32 pid; guint64 start_time; + gint32 uid; v = lookup_asv (details_gvariant, "pid", G_VARIANT_TYPE_UINT32, error); if (v == NULL) @@ -414,7 +422,18 @@ polkit_subject_new_for_gvariant (GVariant *variant, start_time = g_variant_get_uint64 (v); g_variant_unref (v); - ret = polkit_unix_process_new_full (pid, start_time); + v = lookup_asv (details_gvariant, "uid", G_VARIANT_TYPE_INT32, error); + if (v != NULL) + { + uid = g_variant_get_int32 (v); + g_variant_unref (v); + } + else + { + uid = -1; + } + + ret = polkit_unix_process_new_for_owner (pid, start_time, uid); } else if (g_strcmp0 (kind, "unix-session") == 0) { diff --git a/src/polkit/polkitunixprocess.c b/src/polkit/polkitunixprocess.c index 876da69..913be3a 100644 --- a/src/polkit/polkitunixprocess.c +++ b/src/polkit/polkitunixprocess.c @@ -62,6 +62,7 @@ struct _PolkitUnixProcess gint pid; guint64 start_time; + gint uid; }; struct _PolkitUnixProcessClass @@ -74,6 +75,7 @@ enum PROP_0, PROP_PID, PROP_START_TIME, + PROP_UID }; static void subject_iface_init (PolkitSubjectIface *subject_iface); @@ -81,6 +83,9 @@ static void subject_iface_init (PolkitSubjectIface *subject_iface); static guint64 get_start_time_for_pid (gint pid, GError **error); +static gint _polkit_unix_process_get_owner (PolkitUnixProcess *process, + GError **error); + #ifdef HAVE_FREEBSD static gboolean get_kinfo_proc (gint pid, struct kinfo_proc *p); #endif @@ -92,6 +97,7 @@ G_DEFINE_TYPE_WITH_CODE (PolkitUnixProcess, polkit_unix_process, G_TYPE_OBJECT, static void polkit_unix_process_init (PolkitUnixProcess *unix_process) { + unix_process->uid = -1; } static void @@ -108,6 +114,10 @@ polkit_unix_process_get_property (GObject *object, g_value_set_int (value, unix_process->pid); break; + case PROP_UID: + g_value_set_int (value, unix_process->uid); + break; + case PROP_START_TIME: g_value_set_uint64 (value, unix_process->start_time); break; @@ -132,6 +142,14 @@ polkit_unix_process_set_property (GObject *object, polkit_unix_process_set_pid (unix_process, g_value_get_int (value)); break; + case PROP_UID: + polkit_unix_process_set_uid (unix_process, g_value_get_int (value)); + break; + + case PROP_START_TIME: + polkit_unix_process_set_start_time (unix_process, g_value_get_uint64 (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -139,12 +157,39 @@ polkit_unix_process_set_property (GObject *object, } static void +polkit_unix_process_constructed (GObject *object) +{ + PolkitUnixProcess *process = POLKIT_UNIX_PROCESS (object); + + /* sets start_time and uid in case they are unset */ + + if (process->start_time == 0) + process->start_time = get_start_time_for_pid (process->pid, NULL); + + if (process->uid == -1) + { + GError *error; + error = NULL; + process->uid = _polkit_unix_process_get_owner (process, &error); + if (error != NULL) + { + process->uid = -1; + g_error_free (error); + } + } + + if (G_OBJECT_CLASS (polkit_unix_process_parent_class)->constructed != NULL) + G_OBJECT_CLASS (polkit_unix_process_parent_class)->constructed (object); +} + +static void polkit_unix_process_class_init (PolkitUnixProcessClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->get_property = polkit_unix_process_get_property; gobject_class->set_property = polkit_unix_process_set_property; + gobject_class->constructed = polkit_unix_process_constructed; /** * PolkitUnixProcess:pid: @@ -156,7 +201,7 @@ polkit_unix_process_class_init (PolkitUnixProcessClass *klass) g_param_spec_int ("pid", "Process ID", "The UNIX process ID", - -1, + 0, G_MAXINT, 0, G_PARAM_CONSTRUCT | @@ -166,6 +211,27 @@ polkit_unix_process_class_init (PolkitUnixProcessClass *klass) G_PARAM_STATIC_NICK)); /** + * PolkitUnixProcess:uid: + * + * The UNIX user id of the process or -1 if unknown. + * + * Note that this is the real user-id, not the effective user-id. + */ + g_object_class_install_property (gobject_class, + PROP_UID, + g_param_spec_int ("uid", + "User ID", + "The UNIX user ID", + -1, + G_MAXINT, + -1, + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + + /** * PolkitUnixProcess:start-time: * * The start time of the process. @@ -178,7 +244,8 @@ polkit_unix_process_class_init (PolkitUnixProcessClass *klass) 0, G_MAXUINT64, 0, - G_PARAM_READABLE | + G_PARAM_CONSTRUCT | + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); @@ -186,113 +253,50 @@ polkit_unix_process_class_init (PolkitUnixProcessClass *klass) } /** - * polkit_unix_process_get_pid: + * polkit_unix_process_get_uid: * @process: A #PolkitUnixProcess. * - * Gets the process id for @process. + * Gets the user id for @process. Note that this is the real user-id, + * not the effective user-id. * - * Returns: The process id for @process. + * Returns: The user id for @process or -1 if unknown. */ gint -polkit_unix_process_get_pid (PolkitUnixProcess *process) +polkit_unix_process_get_uid (PolkitUnixProcess *process) { - g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), 0); - return process->pid; + g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), -1); + return process->uid; } /** - * polkit_unix_process_get_owner: + * polkit_unix_process_set_uid: * @process: A #PolkitUnixProcess. - * @error: (allow-none): Return location for error or %NULL. + * @uid: The user id to set for @process or -1 to unset it. * - * Gets the uid of the owner of @process. + * Sets the (real, not effective) user id for @process. + */ +void +polkit_unix_process_set_uid (PolkitUnixProcess *process, + gint uid) +{ + g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process)); + g_return_if_fail (uid >= -1); + process->uid = uid; +} + +/** + * polkit_unix_process_get_pid: + * @process: A #PolkitUnixProcess. * - * Note that this returns the real user-id (not the effective user-id) of @process. + * Gets the process id for @process. * - * Returns: The UNIX user id of the owner for @process or 0 if @error is set. - **/ + * Returns: The process id for @process. + */ gint -polkit_unix_process_get_owner (PolkitUnixProcess *process, - GError **error) +polkit_unix_process_get_pid (PolkitUnixProcess *process) { - gint result; - gchar *contents; - gchar **lines; -#ifdef HAVE_FREEBSD - struct kinfo_proc p; -#else - gchar filename[64]; - guint n; -#endif - g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), 0); - g_return_val_if_fail (error == NULL || *error == NULL, 0); - - result = 0; - lines = NULL; - contents = NULL; - -#ifdef HAVE_FREEBSD - if (get_kinfo_proc (process->pid, &p) == 0) - { - g_set_error (error, - POLKIT_ERROR, - POLKIT_ERROR_FAILED, - "get_kinfo_proc() failed for pid %d: %s", - process->pid, - g_strerror (errno)); - goto out; - } - - result = p.ki_uid; -#else - - /* see 'man proc' for layout of the status file - * - * Uid, Gid: Real, effective, saved set, and file system UIDs (GIDs). - */ - g_snprintf (filename, sizeof filename, "/proc/%d/status", process->pid); - if (!g_file_get_contents (filename, - &contents, - NULL, - error)) - { - goto out; - } - lines = g_strsplit (contents, "\n", -1); - for (n = 0; lines != NULL && lines[n] != NULL; n++) - { - gint real_uid, effective_uid; - if (!g_str_has_prefix (lines[n], "Uid:")) - continue; - if (sscanf (lines[n] + 4, "%d %d", &real_uid, &effective_uid) != 2) - { - g_set_error (error, - POLKIT_ERROR, - POLKIT_ERROR_FAILED, - "Unexpected line `%s' in file %s", - lines[n], - filename); - goto out; - } - else - { - result = real_uid; - goto out; - } - } - - g_set_error (error, - POLKIT_ERROR, - POLKIT_ERROR_FAILED, - "Didn't find any line starting with `Uid:' in file %s", - filename); -#endif - -out: - g_strfreev (lines); - g_free (contents); - return result; + return process->pid; } /** @@ -311,6 +315,21 @@ polkit_unix_process_get_start_time (PolkitUnixProcess *process) } /** + * polkit_unix_process_set_start_time: + * @process: A #PolkitUnixProcess. + * @start_time: The start time for @pid. + * + * Set the start time of @process. + */ +void +polkit_unix_process_set_start_time (PolkitUnixProcess *process, + guint64 start_time) +{ + g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process)); + process->start_time = start_time; +} + +/** * polkit_unix_process_set_pid: * @process: A #PolkitUnixProcess. * @pid: A process id. @@ -323,18 +342,17 @@ polkit_unix_process_set_pid (PolkitUnixProcess *process, { g_return_if_fail (POLKIT_IS_UNIX_PROCESS (process)); process->pid = pid; - if (pid != (gint) -1) - process->start_time = get_start_time_for_pid (pid, NULL); } /** * polkit_unix_process_new: * @pid: The process id. * - * Creates a new #PolkitUnixProcess for @pid. The start time of the - * process will be looked up in using e.g. the - * /proc filesystem depending on the platform in - * use. + * Creates a new #PolkitUnixProcess for @pid. + * + * The uid and start time of the process will be looked up in using + * e.g. the /proc filesystem depending on the + * platform in use. * * Returns: (transfer full): A #PolkitSubject. Free with g_object_unref(). */ @@ -353,22 +371,42 @@ polkit_unix_process_new (gint pid) * * Creates a new #PolkitUnixProcess object for @pid and @start_time. * + * The uid of the process will be looked up in using e.g. the + * /proc filesystem depending on the platform in + * use. + * * Returns: (transfer full): A #PolkitSubject. Free with g_object_unref(). */ PolkitSubject * polkit_unix_process_new_full (gint pid, guint64 start_time) { - PolkitUnixProcess *process; - - process = POLKIT_UNIX_PROCESS (polkit_unix_process_new ((gint) -1)); - process->pid = pid; - if (start_time != 0) - process->start_time = start_time; - else - process->start_time = get_start_time_for_pid (pid, NULL); + return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_PROCESS, + "pid", pid, + "start_time", start_time, + NULL)); +} - return POLKIT_SUBJECT (process); +/** + * polkit_unix_process_new_for_owner: + * @pid: The process id. + * @start_time: The start time for @pid or 0 to look it up in e.g. /proc. + * @uid: The (real, not effective) uid of the owner of @pid or -1 to look it up in e.g. /proc. + * + * Creates a new #PolkitUnixProcess object for @pid, @start_time and @uid. + * + * Returns: (transfer full): A #PolkitSubject. Free with g_object_unref(). + */ +PolkitSubject * +polkit_unix_process_new_for_owner (gint pid, + guint64 start_time, + gint uid) +{ + return POLKIT_SUBJECT (g_object_new (POLKIT_TYPE_UNIX_PROCESS, + "pid", pid, + "start_time", start_time, + "uid", uid, + NULL)); } static guint @@ -616,3 +654,95 @@ out: return start_time; } + +static gint +_polkit_unix_process_get_owner (PolkitUnixProcess *process, + GError **error) +{ + gint result; + gchar *contents; + gchar **lines; +#ifdef HAVE_FREEBSD + struct kinfo_proc p; +#else + gchar filename[64]; + guint n; +#endif + + g_return_val_if_fail (POLKIT_IS_UNIX_PROCESS (process), 0); + g_return_val_if_fail (error == NULL || *error == NULL, 0); + + result = 0; + lines = NULL; + contents = NULL; + +#ifdef HAVE_FREEBSD + if (get_kinfo_proc (process->pid, &p) == 0) + { + g_set_error (error, + POLKIT_ERROR, + POLKIT_ERROR_FAILED, + "get_kinfo_proc() failed for pid %d: %s", + process->pid, + g_strerror (errno)); + goto out; + } + + result = p.ki_uid; +#else + + /* see 'man proc' for layout of the status file + * + * Uid, Gid: Real, effective, saved set, and file system UIDs (GIDs). + */ + g_snprintf (filename, sizeof filename, "/proc/%d/status", process->pid); + if (!g_file_get_contents (filename, + &contents, + NULL, + error)) + { + goto out; + } + lines = g_strsplit (contents, "\n", -1); + for (n = 0; lines != NULL && lines[n] != NULL; n++) + { + gint real_uid, effective_uid; + if (!g_str_has_prefix (lines[n], "Uid:")) + continue; + if (sscanf (lines[n] + 4, "%d %d", &real_uid, &effective_uid) != 2) + { + g_set_error (error, + POLKIT_ERROR, + POLKIT_ERROR_FAILED, + "Unexpected line `%s' in file %s", + lines[n], + filename); + goto out; + } + else + { + result = real_uid; + goto out; + } + } + + g_set_error (error, + POLKIT_ERROR, + POLKIT_ERROR_FAILED, + "Didn't find any line starting with `Uid:' in file %s", + filename); +#endif + +out: + g_strfreev (lines); + g_free (contents); + return result; +} + +/* deprecated public method */ +gint +polkit_unix_process_get_owner (PolkitUnixProcess *process, + GError **error) +{ + return _polkit_unix_process_get_owner (process, error); +} diff --git a/src/polkit/polkitunixprocess.h b/src/polkit/polkitunixprocess.h index b88cd03..531a57d 100644 --- a/src/polkit/polkitunixprocess.h +++ b/src/polkit/polkitunixprocess.h @@ -47,16 +47,24 @@ typedef struct _PolkitUnixProcess PolkitUnixProcess; typedef struct _PolkitUnixProcessClass PolkitUnixProcessClass; GType polkit_unix_process_get_type (void) G_GNUC_CONST; -PolkitSubject *polkit_unix_process_new (gint pid); -PolkitSubject *polkit_unix_process_new_full (gint pid, - guint64 start_time); - +PolkitSubject *polkit_unix_process_new (gint pid); +PolkitSubject *polkit_unix_process_new_full (gint pid, + guint64 start_time); +PolkitSubject *polkit_unix_process_new_for_owner (gint pid, + guint64 start_time, + gint uid); gint polkit_unix_process_get_pid (PolkitUnixProcess *process); guint64 polkit_unix_process_get_start_time (PolkitUnixProcess *process); +gint polkit_unix_process_get_uid (PolkitUnixProcess *process); void polkit_unix_process_set_pid (PolkitUnixProcess *process, gint pid); +void polkit_unix_process_set_uid (PolkitUnixProcess *process, + gint uid); +void polkit_unix_process_set_start_time (PolkitUnixProcess *process, + guint64 start_time); + gint polkit_unix_process_get_owner (PolkitUnixProcess *process, - GError **error); + GError **error) G_GNUC_DEPRECATED_FOR (polkit_unix_process_get_uid); G_END_DECLS -- 1.7.4.4