diff options
author | Stefan Walter <stefw@src.gnome.org> | 2008-12-11 23:33:20 +0000 |
---|---|---|
committer | Stefan Walter <stefw@src.gnome.org> | 2008-12-11 23:33:20 +0000 |
commit | e458b511e09377004cf6375b731865ac01990ad9 (patch) | |
tree | d3534b602dced507fb7ba05809509e28a06c7033 /daemon | |
parent | ca9140424a21b53d9b45330b7587c6e85e7cdb5e (diff) |
Rework initialization of the daemon so that most initialization can happen
* common/gkr-crypto.c:
* common/gkr-secure-memory.c:
* common/gkr-secure-memory.h:
* daemon/gkr-daemon.c:
* daemon/gkr-daemon.h:
* daemon/gkr-daemon-dbus.c:
* daemon/gkr-daemon-ops.c:
* pam/gkr-pam-module.c: Rework initialization of the daemon
so that most initialization can happen after starting via PAM.
Fixes bug #558181
svn path=/trunk/; revision=1381
Diffstat (limited to 'daemon')
-rw-r--r-- | daemon/gkr-daemon-dbus.c | 13 | ||||
-rw-r--r-- | daemon/gkr-daemon-ops.c | 7 | ||||
-rw-r--r-- | daemon/gkr-daemon.c | 356 | ||||
-rw-r--r-- | daemon/gkr-daemon.h | 6 |
4 files changed, 217 insertions, 165 deletions
diff --git a/daemon/gkr-daemon-dbus.c b/daemon/gkr-daemon-dbus.c index 982b663c..d0726ce4 100644 --- a/daemon/gkr-daemon-dbus.c +++ b/daemon/gkr-daemon-dbus.c @@ -202,7 +202,7 @@ register_environment_in_session (void) dbus_message_unref (msg); if (!reply) { - g_warning ("couldn't set environment variable in session: %s", derr.message); + g_message ("couldn't set environment variable in session: %s", derr.message); return; } @@ -357,15 +357,6 @@ gkr_daemon_dbus_setup (void) DBusError derr = { 0 }; const gchar *env; - /* - * We may be launched before the DBUS session, (ie: via PAM) - * and DBus tries to launch itself somehow, so double check - * that it has really started. - */ - env = getenv ("DBUS_SESSION_BUS_ADDRESS"); - if (!env || !env[0]) - return; - if (dbus_initialized) return; @@ -414,7 +405,7 @@ gkr_daemon_dbus_setup (void) case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: case DBUS_REQUEST_NAME_REPLY_EXISTS: g_message ("another gnome-keyring-daemon is running"); - return; + break; default: g_return_if_reached (); break; diff --git a/daemon/gkr-daemon-ops.c b/daemon/gkr-daemon-ops.c index b7121283..c20b9d89 100644 --- a/daemon/gkr-daemon-ops.c +++ b/daemon/gkr-daemon-ops.c @@ -1713,8 +1713,11 @@ op_prepare_daemon_environment (GkrBuffer *packet, GkrBuffer *result, GkrKeyringR g_strfreev (environment); - /* We may have received DBUS environment variable so try and setup DBUS */ - gkr_daemon_dbus_setup (); + /* + * We've now definitely received everything we need to run. Ask + * the daemon to complete the initialization. + */ + gkr_daemon_complete_initialization(); gkr_buffer_add_uint32 (result, GNOME_KEYRING_RESULT_OK); diff --git a/daemon/gkr-daemon.c b/daemon/gkr-daemon.c index 08960a3a..c1966a4f 100644 --- a/daemon/gkr-daemon.c +++ b/daemon/gkr-daemon.c @@ -90,28 +90,30 @@ static GMainLoop *loop = NULL; /* All the components to run on startup if not set in gconf */ #ifdef WITH_SSH -#define DEFAULT_COMPONENTS "ssh,keyring,pkcs11" +#define DEFAULT_COMPONENTS "ssh,pkcs11" #else -#define DEFAULT_COMPONENTS "keyring,pkcs11" +#define DEFAULT_COMPONENTS "pkcs11" #endif static gboolean run_foreground = FALSE; static gboolean run_daemonized = FALSE; -static gboolean unlock_with_login = FALSE; -static gboolean start_any_daemon = FALSE; +static gboolean run_for_login = FALSE; +static gboolean run_for_start = FALSE; static gchar* run_components = NULL; +static gchar* login_password = NULL; +static gboolean initialization_completed = FALSE; static GOptionEntry option_entries[] = { { "foreground", 'f', 0, G_OPTION_ARG_NONE, &run_foreground, "Run in the foreground", NULL }, { "daemonize", 'd', 0, G_OPTION_ARG_NONE, &run_daemonized, "Run as a daemon", NULL }, - { "login", 'l', 0, G_OPTION_ARG_NONE, &unlock_with_login, - "Use login password from stdin", NULL }, - { "start", 's', 0, G_OPTION_ARG_NONE, &start_any_daemon, - "Start or initialize an already running daemon" }, + { "login", 'l', 0, G_OPTION_ARG_NONE, &run_for_login, + "Run for a user login. Read login password from stdin", NULL }, + { "start", 's', 0, G_OPTION_ARG_NONE, &run_for_start, + "Start a dameon or initialize an already running daemon." }, { "components", 'c', 0, G_OPTION_ARG_STRING, &run_components, - "The components to run", DEFAULT_COMPONENTS }, + "The optional components to run", DEFAULT_COMPONENTS }, { NULL } }; @@ -136,9 +138,9 @@ parse_arguments (int *argc, char** argv[]) } /* Check the arguments */ - if (unlock_with_login && start_any_daemon) { + if (run_for_login && run_for_start) { g_printerr ("gnome-keyring-daemon: The --start option is incompatible with --login"); - start_any_daemon = FALSE; + run_for_login = FALSE; } @@ -369,6 +371,12 @@ read_login_password (int fd) /* We only accept a max of 8K as the login password */ #define MAX_LENGTH 8192 #define MAX_BLOCK 256 + + /* + * When --login is specified then the login password is passed + * in on stdin. All data (including newlines) are part of the + * password. + */ gchar *buf = gkr_secure_alloc (MAX_BLOCK); gchar *ret = NULL; @@ -401,28 +409,18 @@ read_login_password (int fd) } static void -close_stdinout (void) +cleanup_and_exit (int code) { - int fd; - - fd = open ("/dev/null", O_RDONLY); - sane_dup2 (fd, 0); - close (fd); - - fd = open ("/dev/null", O_WRONLY); - sane_dup2 (fd, 1); - close (fd); - - fd = open ("/dev/null", O_WRONLY); - sane_dup2 (fd, 2); - close (fd); + gkr_cleanup_perform (); + exit (code); } static void -cleanup_and_exit (int code) +clear_login_password (void) { - gkr_cleanup_perform (); - _exit (code); + if(login_password) + gkr_secure_strfree (login_password); + login_password = NULL; } static gboolean @@ -430,11 +428,32 @@ lifetime_slave_pipe_io (GIOChannel *channel, GIOCondition cond, gpointer callback_data) { - cleanup_and_exit (2); + gkr_cleanup_perform (); + _exit (2); return FALSE; } static void +slave_lifetime_to_fd (void) +{ + const char *env; + GIOChannel *channel; + int fd; + + env = getenv ("GNOME_KEYRING_LIFETIME_FD"); + if (env && env[0]) { + fd = atoi (env); + if (fd != 0) { + channel = g_io_channel_unix_new (fd); + g_io_add_watch (channel, + G_IO_IN | G_IO_HUP, + lifetime_slave_pipe_io, NULL); + g_io_channel_unref (channel); + } + } +} + +static void print_environment (pid_t pid) { const gchar **env; @@ -445,7 +464,7 @@ print_environment (pid_t pid) } static gboolean -initialize_running_daemon (int sock) +initialize_other_running_daemon (int sock) { GnomeKeyringResult res; gchar **envp, **e; @@ -506,11 +525,11 @@ start_or_initialize_daemon (void) * a daemon process, just return and let things go * their normal way. */ - sock = gnome_keyring_socket_connect_daemon (FALSE); + sock = gnome_keyring_socket_connect_daemon (FALSE, TRUE); if (sock == -1) return FALSE; - ret = initialize_running_daemon (sock); + ret = initialize_other_running_daemon (sock); close (sock); /* Initialization failed, start this process up as a daemon */ @@ -532,147 +551,194 @@ start_or_initialize_daemon (void) return TRUE; } -int -main (int argc, char *argv[]) +static void +fork_and_print_environment (void) { - const char *env; - int fd; + int status; pid_t pid; - GIOChannel *channel; - GMainContext *ctx; - gchar *login_password; - unsigned seed; + int fd, i; + + if (run_foreground) { + print_environment (getpid ()); + return; + } - g_type_init (); - g_thread_init (NULL); + pid = fork (); + + if (pid != 0) { + + /* Here we are in the initial process */ + + if (run_daemonized) { + + /* Initial process, waits for intermediate child */ + if (pid == -1) + exit (1); + + waitpid (pid, &status, 0); + if (WEXITSTATUS (status) != 0) + exit (WEXITSTATUS (status)); + + } else { + /* Not double forking, we know the PID */ + print_environment (pid); + } + + /* The initial process exits successfully */ + exit (0); + } - parse_arguments (&argc, &argv); + if (!run_daemonized) + return; -#ifdef HAVE_LOCALE_H - /* internationalisation */ - setlocale (LC_ALL, ""); -#endif + /* Double fork if need to daemonize properly */ + pid = fork (); + + if (pid != 0) { -#ifdef HAVE_GETTEXT - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); -#endif + /* Here we are in the intermediate child process */ + + /* + * This process exits, so that the final child will inherit + * init as parent to avoid zombies + */ + if (pid == -1) + exit (1); + + /* We've done two forks. Now we know the PID */ + print_environment (pid); + + /* The intermediate child exits */ + exit (0); + } - /* - * If asked to start a daemon, see if one is already running - * and initialize it if so. + /* Here we are in the resulting daemon process. */ + + for (i = 0; i < 3; ++i) { + fd = open ("/dev/null", O_RDONLY); + sane_dup2 (fd, i); + close (fd); + } +} + +gboolean +gkr_daemon_complete_initialization(void) +{ + /* + * Sometimes we don't initialize the full daemon right on + * startup. When run with --login is one such case. */ - if (start_any_daemon) { - if (start_or_initialize_daemon ()) - return 0; + + if (initialization_completed) { + g_message ("The daemon was already initialized."); + return TRUE; } - gkr_crypto_setup (); - - gcry_create_nonce (&seed, sizeof (seed)); - srand (seed); + gkr_daemon_dbus_setup (); /* Initialize object storage */ if (!gkr_pk_object_storage_initialize ()) - cleanup_and_exit (1); + return FALSE; #ifdef ROOT_CERTIFICATES if (!gkr_pk_root_storage_initialize ()) - cleanup_and_exit (1); + return FALSE; #endif /* Initialize the appropriate components */ - if (check_run_component ("keyring")) { - if (!gkr_daemon_io_create_master_socket ()) - cleanup_and_exit (1); - } - + #ifdef WITH_SSH if (check_run_component ("ssh")) { if (!gkr_daemon_ssh_io_initialize () || !gkr_ssh_storage_initialize ()) - cleanup_and_exit (1); + return FALSE; } #endif if (check_run_component ("pkcs11")) { if (!gkr_pkcs11_daemon_setup ()) - cleanup_and_exit (1); - } - + return FALSE; + } + + initialization_completed = TRUE; + return TRUE; +} + +int +main (int argc, char *argv[]) +{ + GMainContext *ctx; + /* - * When --login is specified then the login password is passed - * in on stdin. All data (including newlines) are part of the - * password. + * The gnome-keyring startup is not as simple as I wish it could be. + * + * It's often started in the primidoral stages of a session, where + * there's no DBus, no GConf, and no proper X display. This is the + * strange world of PAM. + * + * When started with the --login option, we do as little initialization + * as possible. We expect a login password on the stdin, and unlock + * or create the login keyring. + * + * Then later we expect gnome-keyring-dameon to be run again with the + * --start option. This second gnome-keyring-daemon will hook the + * original daemon up with environment variables necessary to initialize + * itself and bring it into the session. This second daemon usually exits. + * + * Without either of these options, we follow a more boring and + * predictable startup. */ - login_password = unlock_with_login ? read_login_password (STDIN) : NULL; + + g_type_init (); + g_thread_init (NULL); + +#ifdef HAVE_LOCALE_H + /* internationalisation */ + setlocale (LC_ALL, ""); +#endif + +#ifdef HAVE_GETTEXT + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif + + gkr_crypto_setup (); + + /* Send all warning or error messages to syslog */ + prepare_logging (); + + parse_arguments (&argc, &argv); + + /* The --start option */ + if (run_for_start) { + if (start_or_initialize_daemon ()) + cleanup_and_exit (0); + } /* - * The whole forking and daemonizing dance starts here. + * Always initialize the keyring subsystem. This is a necessary + * component that everything else depends on in one way or + * another. */ - if (!run_foreground) { - pid = fork (); - - /* An intermediate child */ - if (pid == 0) { - if (run_daemonized) { - pid = fork (); - - /* Still in the intermedate child */ - if (pid != 0) { - gkr_secure_free (login_password); - - /* This process exits, so that the - * final child will inherit init as parent - * to avoid zombies - */ - if (pid == -1) { - exit (1); - } else { - /* This is where we know the pid of the daemon. - * The initial process will waitpid until we exit, - * so there is no race */ - print_environment (pid); - exit (0); - } - } - } - - /* final child continues here */ - - /* The initial process */ - } else { - gkr_secure_free (login_password); - - if (run_daemonized) { - int status; - - /* Initial process, waits for intermediate child */ - if (pid == -1) - exit (1); - - waitpid (pid, &status, 0); - if (WEXITSTATUS (status) != 0) - exit (WEXITSTATUS (status)); - - } else { - print_environment (pid); - } - - exit (0); - } - - /* The final child ... */ - close_stdinout (); + if (!gkr_daemon_io_create_master_socket ()) + cleanup_and_exit (1); + /* The --login option. Delayed initialization */ + if (run_for_login) { + login_password = read_login_password (STDIN); + atexit (clear_login_password); + + /* Not a login daemon. Initialize now. */ } else { - print_environment (getpid ()); + if (!gkr_daemon_complete_initialization ()) + cleanup_and_exit (1); } + + /* The whole forking and daemonizing dance starts here. */ + fork_and_print_environment(); - /* Daemon process continues here */ - - /* Send all warning or error messages to syslog */ + /* Prepare logging a second time, since we may be in a different process */ prepare_logging(); loop = g_main_loop_new (NULL, FALSE); @@ -683,29 +749,19 @@ main (int argc, char *argv[]) gkr_unix_signal_connect (ctx, SIGHUP, signal_handler, NULL); gkr_unix_signal_connect (ctx, SIGTERM, signal_handler, NULL); - env = getenv ("GNOME_KEYRING_LIFETIME_FD"); - if (env && env[0]) { - fd = atoi (env); - if (fd != 0) { - channel = g_io_channel_unix_new (fd); - g_io_add_watch (channel, - G_IO_IN | G_IO_HUP, - lifetime_slave_pipe_io, NULL); - g_io_channel_unref (channel); - } - } + /* TODO: Do we still need this? XFCE still seems to use it. */ + slave_lifetime_to_fd (); gkr_async_workers_init (loop); - gkr_daemon_dbus_setup (); /* * Unlock the login keyring if we were given a password on STDIN. * If it does not exist. We create it. */ - if (unlock_with_login && login_password) { + if (login_password) { if (!gkr_keyring_login_unlock (login_password)) g_warning ("Failed to unlock login on startup"); - gkr_secure_free (login_password); + gkr_secure_strclear (login_password); } g_main_loop_run (loop); diff --git a/daemon/gkr-daemon.h b/daemon/gkr-daemon.h index ff234233..2641e1b4 100644 --- a/daemon/gkr-daemon.h +++ b/daemon/gkr-daemon.h @@ -47,10 +47,12 @@ extern GkrDaemonOperation keyring_ops[]; void gkr_daemon_quit (void); +gboolean gkr_daemon_complete_initialization (void); + gboolean gkr_daemon_io_create_master_socket (void); + const gchar* gkr_daemon_io_get_socket_path (void); -/* Dbus Initialization/Cleanup */ -void gkr_daemon_dbus_setup (void); +void gkr_daemon_dbus_setup (void); #endif /* GNOME_KEYRING_DAEMON_H */ |