gboolean auth_check_uid_role (GDBusMethodInvocation *invocation, uid_t uid, const gchar *role) { int err = 0; gs_free struct passwd *pw = NULL; gs_free struct group *wheel_gr = NULL; gs_free struct group *role_gr = NULL; gs_free gid_t *gids = NULL; if (uid == 0) return TRUE; pw = getpwuid_a (uid, &err); if (pw == NULL) goto error; wheel_gr = getgrnam_a ("wheel", NULL); role_gr = role ? getgrnam_a (role, NULL) : NULL; int n_groups; gids = getgrouplist_a (pw->pw_name, pw->pw_gid, &n_groups, &err); if (gids == NULL) goto error; for (int i = 0; i < n_groups; i++) if ((wheel_gr && gids[i] == wheel_gr->gr_gid) || (role_gr && gids[i] == role_gr->gr_gid)) return TRUE; error: if (err) g_dbus_method_invocation_return_error (invocation, COCKPIT_ERROR, COCKPIT_ERROR_FAILED, "%s", strerror(err)); else g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Method %s.%s needs role '%s'", g_dbus_method_invocation_get_interface_name (invocation), g_dbus_method_invocation_get_method_name (invocation), role ? role : "wheel"); return FALSE; }
gboolean auth_uid_is_wheel (uid_t uid) { gs_free struct passwd *pw = getpwuid_a (uid, NULL); if (pw == NULL) return FALSE; gs_free struct group *gr = getgrnam_a ("wheel", NULL); if (gr == NULL) return FALSE; int n_groups; gs_free gid_t *gids = getgrouplist_a (pw->pw_name, pw->pw_gid, &n_groups, NULL); if (gids == NULL) return FALSE; for (int i = 0; i < n_groups; i++) if (gids[i] == gr->gr_gid) return TRUE; return FALSE; }
static int run_bridge (const gchar *interactive, gboolean privileged_slave) { CockpitTransport *transport; gboolean terminated = FALSE; gboolean interupted = FALSE; gboolean closed = FALSE; CockpitPortal *super = NULL; CockpitPortal *pcp = NULL; gpointer polkit_agent = NULL; const gchar *directory; struct passwd *pwd; GPid daemon_pid = 0; GPid agent_pid = 0; guint sig_term; guint sig_int; int outfd; uid_t uid; cockpit_set_journal_logging (G_LOG_DOMAIN, !isatty (2)); /* * The bridge always runs from within $XDG_RUNTIME_DIR * This makes it easy to create user sockets and/or files. */ if (!privileged_slave) { directory = g_get_user_runtime_dir (); if (g_mkdir_with_parents (directory, 0700) < 0) g_warning ("couldn't create runtime dir: %s: %s", directory, g_strerror (errno)); else if (g_chdir (directory) < 0) g_warning ("couldn't change to runtime dir: %s: %s", directory, g_strerror (errno)); } /* Always set environment variables early */ uid = geteuid(); pwd = getpwuid_a (uid); if (pwd == NULL) { g_message ("couldn't get user info: %s", g_strerror (errno)); } else { g_setenv ("USER", pwd->pw_name, TRUE); g_setenv ("HOME", pwd->pw_dir, TRUE); g_setenv ("SHELL", pwd->pw_shell, TRUE); } /* Reset the umask, typically this is done in .bashrc for a login shell */ umask (022); /* * This process talks on stdin/stdout. However lots of stuff wants to write * to stdout, such as g_debug, and uses fd 1 to do that. Reroute fd 1 so that * it goes to stderr, and use another fd for stdout. */ outfd = dup (1); if (outfd < 0 || dup2 (2, 1) < 1) { g_warning ("bridge couldn't redirect stdout to stderr"); outfd = 1; } sig_term = g_unix_signal_add (SIGTERM, on_signal_done, &terminated); sig_int = g_unix_signal_add (SIGINT, on_signal_done, &interupted); g_type_init (); /* Start daemons if necessary */ if (!interactive && !privileged_slave) { if (!have_env ("DBUS_SESSION_BUS_ADDRESS")) daemon_pid = start_dbus_daemon (); if (!have_env ("SSH_AUTH_SOCK")) agent_pid = start_ssh_agent (); } packages = cockpit_packages_new (); cockpit_dbus_internal_startup (interactive != NULL); if (interactive) { /* Allow skipping the init message when interactive */ init_received = TRUE; transport = cockpit_interact_transport_new (0, outfd, interactive); } else { transport = cockpit_pipe_transport_new_fds ("stdio", 0, outfd); } if (uid != 0) { if (!interactive) polkit_agent = cockpit_polkit_agent_register (transport, NULL); super = cockpit_portal_new_superuser (transport); } g_resources_register (cockpitassets_get_resource ()); cockpit_web_failure_resource = "/org/cockpit-project/Cockpit/fail.html"; pcp = cockpit_portal_new_pcp (transport); cockpit_dbus_time_startup (); cockpit_dbus_user_startup (pwd); cockpit_dbus_setup_startup (); g_free (pwd); pwd = NULL; g_signal_connect (transport, "control", G_CALLBACK (on_transport_control), NULL); g_signal_connect (transport, "closed", G_CALLBACK (on_closed_set_flag), &closed); send_init_command (transport); /* Owns the channels */ channels = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); while (!terminated && !closed && !interupted) g_main_context_iteration (NULL, TRUE); if (polkit_agent) cockpit_polkit_agent_unregister (polkit_agent); if (super) g_object_unref (super); g_object_unref (pcp); g_object_unref (transport); g_hash_table_destroy (channels); cockpit_dbus_internal_cleanup (); cockpit_packages_free (packages); packages = NULL; if (daemon_pid) kill (daemon_pid, SIGTERM); if (agent_pid) kill (agent_pid, SIGTERM); g_source_remove (sig_term); g_source_remove (sig_int); /* So the caller gets the right signal */ if (terminated) raise (SIGTERM); return 0; }