diff options
-rw-r--r-- | ChangeLog | 64 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | acconfig.h | 1 | ||||
-rw-r--r-- | config/Makefile.am | 11 | ||||
-rw-r--r-- | config/gdm.conf.in | 2 | ||||
-rw-r--r-- | configure.in | 5 | ||||
-rw-r--r-- | daemon/auth.c | 6 | ||||
-rw-r--r-- | daemon/display.c | 46 | ||||
-rw-r--r-- | daemon/errorgui.c | 4 | ||||
-rw-r--r-- | daemon/gdm.c | 122 | ||||
-rw-r--r-- | daemon/gdm.h | 2 | ||||
-rw-r--r-- | daemon/misc.c | 22 | ||||
-rw-r--r-- | daemon/server.c | 25 | ||||
-rw-r--r-- | daemon/slave.c | 401 | ||||
-rw-r--r-- | daemon/verify-pam.c | 26 | ||||
-rw-r--r-- | docs/C/gdm.xml | 25 | ||||
-rw-r--r-- | gdm.spec.in | 3 | ||||
-rw-r--r-- | gui/gdmchooser.c | 117 | ||||
-rw-r--r-- | gui/gdmlogin.c | 15 | ||||
-rw-r--r-- | gui/greeter/greeter_item_ulist.c | 15 |
20 files changed, 706 insertions, 209 deletions
@@ -1,3 +1,67 @@ +Thu Jul 24 14:58:23 2003 George Lebl <jirka@5z.com> + + * daemon/gdm.c, daemon/slave.c, daemon/auth.c: change some + g_strconcat's to g_build_filename's to address #118040. + still more need to be converted + + * gdm.spec.in, daemon/gdm.h, config/gdm.conf.in, config/Makefile.am, + configure.in: By default use logdir of /var/log/gdm just + like redhat does it + + * daemon/gdm.c: check logdir to exist and if not set it to + ServAuthDir + + * gdm.spec.in, daemon/gdm.c, config/Makefile.am: Make the + ServAuthDir permissions to be 1770 with owenership root.gdm. + That makes it impossible for the gdm user to run DoS attacks + against the gdm daemon (though without any process limits set + it can still somewhat do that) + + * daemon/slave.c, daemon/display.c, daemon/gdm.c, daemon/misc.c, + daemon/server.c: Hunt more races and hangs. Make sure we really + don't do anything bad in signal handlers by making a setjmp + at the beginning of the slave_start function and returning + there from signal handlers to do final cleanup kind of stuff. + Also when we are receiving TERM signals while waiting on stuff + to die, be very un-nice to things and SIGKILL them. Also stop + using sleep if we might be using alarm at the same time. + + * daemon/display.c: whack non-useful signal block push on unmanage, + and if we get a TERM signal while waiting on the slave, then send + a TERM signal to the slave again. + + * daemon/errorgui.c: set USER, USERNAME and LOGNAME to "gdm" so that + they don't end up root by some mistake + + * daemon/slave.c: Use home of root rather then /root for the home + directory of gdmsetup. Also if we can't change to the home + directory chdir to / instead of leaving it at servauthdir. + Be anal about COOKIEs in the logfile. Also when things go + just a bit wrong and not completely whacko, don't ABORT but + just REMANAGE, the toplevel loop of death will handle things + for us correctly. And setsid a very close to the start of + the session to avoid a race ABORTing a display by mistake. + + * daemon/slave.c: make the PostLogin behave just like PostSession + with respect to the return value + + * daemon/verify-pam.c: avoid races on termination with the + verify_cleanup and handle some cases where crashes may (but + should not) occur. + + * daemon/gdm.c: whack unneeded signal blockers (the main daemon + is all async with a nice mainloop) + + * gui/gdmchooser.c: handle HUP gracefully, when one of the + config options we care about changes just restart self + instead of + + * gui/gdmlogin.c, gui/greeter/greeter_item_ulist.c: make + the username bold + + * docs/C/gdm.xml: update the PostLogin behaviour and the permissions + on the ServAuthDir + Wed Jul 23 15:13:33 2003 George Lebl <jirka@5z.com> * Release 2.4.2.98 @@ -12,6 +12,9 @@ thing and just ignore the ping if a signal comes, not sure how this will play wi the X stuff and if this would completely whack us out. The other option is to have a separate "pinger process" but that seems to heavyweight. +All the GUIs running as the gdm user should have some resource limits set to +make it hard to do DoS attacks by somehow exploiting a leak or some such. + Small TODO things: - If we can't setup pam display user visible errors and not just syslog stuff @@ -27,6 +27,7 @@ #undef EXPANDED_GDMCONFIGDIR #undef EXPANDED_LOCALEDIR #undef EXPANDED_AUTHDIR +#undef EXPANDED_LOGDIR #undef EXPANDED_SYSCONFDIR #undef EXPANDED_SESSDIR #undef X_SERVER diff --git a/config/Makefile.am b/config/Makefile.am index 1e4bb289..0c61b676 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -5,6 +5,7 @@ localedir = $(sysconfdir)/gdm sessdir = $(sysconfdir)/dm/Sessions initdir = $(sysconfdir)/gdm/Init authdir = $(localstatedir)/gdm +logdir = $(localstatedir)/gdm gnomercdir = $(sysconfdir)/gdm postdir = $(sysconfdir)/gdm/PostSession predir = $(sysconfdir)/gdm/PreSession @@ -121,10 +122,16 @@ install-data-hook: gdm.conf Xsession gnome.desktop Default.desktop CDE.desktop D chmod 755 $(DESTDIR)$(gnomercdir); \ fi + if test '!' -d $(DESTDIR)$(logdir); then \ + $(mkinstalldirs) $(DESTDIR)$(logdir); \ + chown root.root $(DESTDIR)$(logdir); \ + chmod 755 $(DESTDIR)$(logdir); \ + fi + if test '!' -d $(DESTDIR)$(authdir); then \ $(mkinstalldirs) $(DESTDIR)$(authdir); \ - chown gdm.gdm $(DESTDIR)$(authdir); \ - chmod 750 $(DESTDIR)$(authdir); \ + chown root.gdm $(DESTDIR)$(authdir); \ + chmod 1770 $(DESTDIR)$(authdir); \ fi system=`uname`; \ diff --git a/config/gdm.conf.in b/config/gdm.conf.in index 31a27f66..07e12808 100644 --- a/config/gdm.conf.in +++ b/config/gdm.conf.in @@ -59,7 +59,7 @@ Group=gdm # To try to kill all clients started at greeter time or in the Init script. # doesn't always work, only if those clients have a window of their own KillInitClients=true -LogDir=@EXPANDED_AUTHDIR@ +LogDir=@EXPANDED_LOGDIR@ # You should probably never change this value unless you have a weird setup PidFile=/var/run/gdm.pid # Note that a post login script is run before a PreSession script. diff --git a/configure.in b/configure.in index ec5865f2..39b9c432 100644 --- a/configure.in +++ b/configure.in @@ -498,6 +498,11 @@ EXPANDED_AUTHDIR=`eval echo $AUTHDIR_TMP` AC_SUBST(EXPANDED_AUTHDIR) AC_DEFINE_UNQUOTED(EXPANDED_AUTHDIR,"$EXPANDED_AUTHDIR") +LOGDIR_TMP="$localstatedir/log/gdm" +EXPANDED_LOGDIR=`eval echo $LOGDIR_TMP` +AC_SUBST(EXPANDED_LOGDIR) +AC_DEFINE_UNQUOTED(EXPANDED_LOGDIR,"$EXPANDED_LOGDIR") + if test -x /usr/X11R6/bin/X; then X_PATH="/usr/bin/X11:/usr/X11R6/bin:/opt/X11R6/bin" X_SERVER_PATH="/usr/X11R6/bin" diff --git a/daemon/auth.c b/daemon/auth.c index 6fff5cc2..a1d0a155 100644 --- a/daemon/auth.c +++ b/daemon/auth.c @@ -178,7 +178,7 @@ gdm_auth_secure_display (GdmDisplay *d) /* Note, Xnest can't use the ServAuthDir unless running as * root, which is rare anyway, unless the user is a wanker */ - d->authfile = g_strconcat (GdmUserAuthFB, "/.gdmXXXXXX", NULL); + d->authfile = g_build_filename (GdmUserAuthFB, ".gdmXXXXXX", NULL); umask (077); authfd = g_mkstemp (d->authfile); @@ -438,7 +438,7 @@ try_user_add_again: if (authdir == NULL) d->userauth = NULL; else - d->userauth = g_strconcat (authdir, "/", GdmUserAuthFile, NULL); + d->userauth = g_build_filename (authdir, GdmUserAuthFile, NULL); /* Find out if the Xauthority file passes the paranoia check */ if (automatic_tmp_dir || @@ -450,7 +450,7 @@ try_user_add_again: /* No go. Let's create a fallback file in GdmUserAuthFB (/tmp) */ d->authfb = TRUE; g_free (d->userauth); - d->userauth = g_strconcat (GdmUserAuthFB, "/.gdmXXXXXX", NULL); + d->userauth = g_build_filename (GdmUserAuthFB, ".gdmXXXXXX", NULL); authfd = g_mkstemp (d->userauth); if (authfd == -1) { diff --git a/daemon/display.c b/daemon/display.c index aaf5f286..7d4033d6 100644 --- a/daemon/display.c +++ b/daemon/display.c @@ -43,6 +43,7 @@ extern gboolean GdmXdmcp; extern gint xdmcp_sessions; extern gint flexi_servers; extern gint xdmcp_pending; +extern gboolean gdm_in_final_cleanup; extern GSList *displays; extern GdmConnection *fifoconn; extern GdmConnection *pipeconn; @@ -249,28 +250,12 @@ gdm_display_manage (GdmDisplay *d) d->slave_notify_fd = fds[0]; - if (d->type == TYPE_LOCAL) { - gdm_slave_start (d); - /* if we ever return, bad things are happening */ - gdm_server_stop (d); - _exit (DISPLAY_ABORT); - } else if (SERVER_IS_FLEXI (d)) { - gdm_slave_start (d); - gdm_server_stop (d); - /* we expect to return after the session finishes */ - _exit (DISPLAY_REMANAGE); - } else if (d->type == TYPE_XDMCP && - d->dispstat == XDMCP_MANAGED) { - gdm_slave_start (d); - gdm_server_stop (d); - /* we expect to return after the session finishes */ - _exit (DISPLAY_REMANAGE); - } + gdm_slave_start (d); + /* should never retern */ - /* yaikes, how did we ever get here, though I suppose - * it could be possible if XMDCP thing wasn't really set up */ + /* yaikes, how did we ever get here? */ gdm_server_stop (d); - _exit (DISPLAY_ABORT); + _exit (DISPLAY_REMANAGE); break; @@ -327,12 +312,25 @@ gdm_display_unmanage (GdmDisplay *d) d->name, (int)d->slavepid); /* Kill slave */ - gdm_sigchld_block_push (); if (d->slavepid > 1 && - kill (d->slavepid, SIGTERM) == 0) - ve_waitpid_no_signal (d->slavepid, 0, 0); + kill (d->slavepid, SIGTERM) == 0) { + int ret; +wait_again: + errno = 0; + ret = waitpid (d->slavepid, NULL, 0); + if (ret < 0 && + errno == EINTR) { + /* rekill the slave to tell it to + hurry up and die if we're getting + killed ourselves */ + if (ve_signal_was_notified (SIGTERM) || + ve_signal_was_notified (SIGINT)) { + kill (d->slavepid, SIGTERM); + } + goto wait_again; + } + } d->slavepid = 0; - gdm_sigchld_block_pop (); if (d->type == TYPE_LOCAL) d->dispstat = DISPLAY_DEAD; diff --git a/daemon/errorgui.c b/daemon/errorgui.c index ef5d831f..328f8e6b 100644 --- a/daemon/errorgui.c +++ b/daemon/errorgui.c @@ -211,6 +211,10 @@ setup_dialog (GdmDisplay *d, const char *name, int closefdexcept) openlog ("gdm", LOG_PID, LOG_DAEMON); + ve_setenv ("LOGNAME", GdmUser, TRUE); + ve_setenv ("USER", GdmUser, TRUE); + ve_setenv ("USERNAME", GdmUser, TRUE); + ve_setenv ("DISPLAY", d->name, TRUE); ve_unsetenv ("XAUTHORITY"); diff --git a/daemon/gdm.c b/daemon/gdm.c index 5615e349..686dec85 100644 --- a/daemon/gdm.c +++ b/daemon/gdm.c @@ -101,6 +101,7 @@ gboolean gdm_emergency_server = FALSE; gboolean gdm_first_login = TRUE; int gdm_in_signal = 0; +gboolean gdm_in_final_cleanup = FALSE; /* Configuration options */ gchar *GdmUser = NULL; @@ -212,6 +213,51 @@ compare_displays (gconstpointer a, gconstpointer b) return 0; } +static void +check_servauthdir (struct stat *statbuf) +{ + /* Enter paranoia mode */ + if (stat (GdmServAuthDir, statbuf) == -1) { + char *s = g_strdup_printf + (_("Server Authorization directory " + "(daemon/ServAuthDir) is set to %s " + "but this does not exist. Please " + "correct gdm configuration %s and " + "restart gdm."), GdmServAuthDir, + GDM_CONFIG_FILE); + gdm_text_message_dialog (s); + GdmPidFile = NULL; + gdm_fail (_("%s: Authdir %s does not exist. Aborting."), "gdm_config_parse", GdmServAuthDir); + } + + if (! S_ISDIR (statbuf->st_mode)) { + char *s = g_strdup_printf + (_("Server Authorization directory " + "(daemon/ServAuthDir) is set to %s " + "but this is not a directory. Please " + "correct gdm configuration %s and " + "restart gdm."), GdmServAuthDir, + GDM_CONFIG_FILE); + gdm_text_message_dialog (s); + GdmPidFile = NULL; + gdm_fail (_("%s: Authdir %s is not a directory. Aborting."), "gdm_config_parse", GdmServAuthDir); + } +} + +static void +check_logdir (void) +{ + struct stat statbuf; + + if (stat (GdmLogDir, &statbuf) == -1 || + ! S_ISDIR (statbuf.st_mode)) { + gdm_error (_("%s: Logdir %s does not exist or isn't a directory. Using ServAuthDir %s."), "gdm_config_parse", + GdmLogDir, GdmServAuthDir); + g_free (GdmLogDir); + GdmLogDir = g_strdup (GdmServAuthDir); + } +} + /** * gdm_config_parse: * @@ -400,8 +446,10 @@ gdm_config_parse (void) gdm_fail (_("%s: No authdir specified."), "gdm_config_parse"); } - if (ve_string_empty (GdmLogDir)) - GdmLogDir = GdmServAuthDir; + if (ve_string_empty (GdmLogDir)) { + g_free (GdmLogDir); + GdmLogDir = g_strdup (GdmServAuthDir); + } if (ve_string_empty (GdmSessDir)) gdm_error (_("%s: No sessions directory specified."), "gdm_config_parse"); @@ -640,35 +688,23 @@ gdm_config_parse (void) g_free (bin); - /* Enter paranoia mode */ - if (stat (GdmServAuthDir, &statbuf) == -1) { - char *s = g_strdup_printf - (_("Server Authorization directory " - "(daemon/ServAuthDir) is set to %s " - "but this does not exist. Please " - "correct gdm configuration %s and " - "restart gdm."), GdmServAuthDir, - GDM_CONFIG_FILE); - gdm_text_message_dialog (s); - GdmPidFile = NULL; - gdm_fail (_("%s: Authdir %s does not exist. Aborting."), "gdm_config_parse", GdmServAuthDir); - } + check_servauthdir (&statbuf); - if (! S_ISDIR (statbuf.st_mode)) { - char *s = g_strdup_printf - (_("Server Authorization directory " - "(daemon/ServAuthDir) is set to %s " - "but this is not a directory. Please " - "correct gdm configuration %s and " - "restart gdm."), GdmServAuthDir, - GDM_CONFIG_FILE); - gdm_text_message_dialog (s); - GdmPidFile = NULL; - gdm_fail (_("%s: Authdir %s is not a directory. Aborting."), "gdm_config_parse", GdmServAuthDir); - } + seteuid (0); + setegid (0); + + /* Now set things up for us as */ + chown (GdmServAuthDir, 0, GdmGroupId); + chmod (GdmServAuthDir, (S_IRWXU|S_IRWXG|S_ISVTX)); + + setegid (GdmGroupId); + seteuid (GdmUserId); + + /* again paranoid */ + check_servauthdir (&statbuf); - if (statbuf.st_uid != GdmUserId || statbuf.st_gid != GdmGroupId) { + if (statbuf.st_uid != 0 || statbuf.st_gid != GdmGroupId) { char *s = g_strdup_printf (_("Server Authorization directory " "(daemon/ServAuthDir) is set to %s " @@ -681,28 +717,30 @@ gdm_config_parse (void) gdm_text_message_dialog (s); GdmPidFile = NULL; gdm_fail (_("%s: Authdir %s is not owned by user %s, group %s. Aborting."), "gdm_config_parse", - GdmServAuthDir, GdmUser, GdmGroup); + GdmServAuthDir, gdm_root_user (), GdmGroup); } - if (statbuf.st_mode != (S_IFDIR|S_IRWXU|S_IRGRP|S_IXGRP)) { + if (statbuf.st_mode != (S_IFDIR|S_IRWXU|S_IRWXG|S_ISVTX)) { char *s = g_strdup_printf (_("Server Authorization directory " "(daemon/ServAuthDir) is set to %s " "but has the wrong permissions, it " - "should have permissions of 0750. " + "should have permissions of %o. " "Please correct the permissions or " "the gdm configuration %s and " "restart gdm."), - GdmServAuthDir, GDM_CONFIG_FILE); + GdmServAuthDir, (S_IRWXU|S_IRWXG|S_ISVTX), GDM_CONFIG_FILE); gdm_text_message_dialog (s); GdmPidFile = NULL; - gdm_fail (_("%s: Authdir %s has wrong permissions %o. Should be 0750. Aborting."), "gdm_config_parse", - GdmServAuthDir, statbuf.st_mode); + gdm_fail (_("%s: Authdir %s has wrong permissions %o. Should be %o. Aborting."), "gdm_config_parse", + GdmServAuthDir, statbuf.st_mode, (S_IRWXU|S_IRWXG|S_ISVTX)); } seteuid (0); setegid (0); + check_logdir (); + /* Check that user authentication is properly configured */ gdm_verify_check (); @@ -814,7 +852,7 @@ gdm_final_cleanup (void) gdm_debug ("gdm_final_cleanup"); - gdm_sigchld_block_push (); + gdm_in_final_cleanup = TRUE; if (extra_process > 1) { /* we sigterm extra processes, and we @@ -823,8 +861,6 @@ gdm_final_cleanup (void) extra_process = 0; } - gdm_sigchld_block_pop (); - list = g_slist_copy (displays); for (li = list; li != NULL; li = li->next) { GdmDisplay *d = li->data; @@ -1839,11 +1875,9 @@ send_slave_ack (GdmDisplay *d, const char *resp) g_free (not); } } - gdm_sigchld_block_push (); if (d->slavepid > 1) { kill (d->slavepid, SIGUSR2); } - gdm_sigchld_block_pop (); } static void @@ -1856,11 +1890,9 @@ send_slave_command (GdmDisplay *d, const char *command) write (d->master_notify_fd, cmd, strlen (cmd)); g_free (cmd); } - gdm_sigchld_block_push (); if (d->slavepid > 1) { kill (d->slavepid, SIGUSR2); } - gdm_sigchld_block_pop (); } @@ -2265,10 +2297,10 @@ gdm_handle_message (GdmConnection *conn, const char *msg, gpointer data) GSList *li; for (li = displays; li != NULL; li = li->next) { GdmDisplay *d = li->data; - gdm_sigchld_block_push (); if (d->greetpid > 1) kill (d->greetpid, SIGHUP); - gdm_sigchld_block_pop (); + else if (d->chooserpid > 1) + kill (d->chooserpid, SIGHUP); } } else if (strncmp (msg, GDM_SOP_WRITE_X_SERVERS " ", strlen (GDM_SOP_WRITE_X_SERVERS " ")) == 0) { @@ -2659,10 +2691,8 @@ notify_displays_int (const char *key, int val) gdm_fdprintf (disp->master_notify_fd, "%c%s %d\n", GDM_SLAVE_NOTIFY_KEY, key, val); - gdm_sigchld_block_push (); if (disp->slavepid > 1) kill (disp->slavepid, SIGUSR2); - gdm_sigchld_block_pop (); } } } @@ -2677,10 +2707,8 @@ notify_displays_string (const char *key, const char *val) gdm_fdprintf (disp->master_notify_fd, "%c%s %s\n", GDM_SLAVE_NOTIFY_KEY, key, val); - gdm_sigchld_block_push (); if (disp->slavepid > 1) kill (disp->slavepid, SIGUSR2); - gdm_sigchld_block_pop (); } } } diff --git a/daemon/gdm.h b/daemon/gdm.h index 106e814c..e77a1dba 100644 --- a/daemon/gdm.h +++ b/daemon/gdm.h @@ -134,7 +134,7 @@ enum { #define GDM_KEY_HALT "daemon/HaltCommand=/usr/bin/poweroff;/sbin/poweroff;/sbin/shutdown -h now;/usr/sbin/shutdown -h now" #define GDM_KEY_INITDIR "daemon/DisplayInitDir=" EXPANDED_SYSCONFDIR "/gdm/Init" #define GDM_KEY_KILLIC "daemon/KillInitClients=true" -#define GDM_KEY_LOGDIR "daemon/LogDir=" EXPANDED_AUTHDIR +#define GDM_KEY_LOGDIR "daemon/LogDir=" EXPANDED_LOGDIR #define GDM_KEY_PATH "daemon/DefaultPath=/bin:/usr/bin:" X_CONF_PATH ":" EXPANDED_BINDIR #define GDM_KEY_PIDFILE "daemon/PidFile=/var/run/gdm.pid" #define GDM_KEY_POSTSESS "daemon/PostSessionScriptDir=" EXPANDED_SYSCONFDIR "/gdm/PostSession/" diff --git a/daemon/misc.c b/daemon/misc.c index a72d5b4c..9d7d51f7 100644 --- a/daemon/misc.c +++ b/daemon/misc.c @@ -1240,6 +1240,13 @@ static struct sigaction oldterm, oldint, oldhup; static void jumpback_sighandler (int signal) { + /* this avoids a race see Note below. + We want to jump back only on the first + signal invocation, even if the signal + handler didn't return. */ + gboolean old_do_jumpback = do_jumpback; + do_jumpback = FALSE; + if (signal == SIGINT) oldint.sa_handler (signal); else if (signal == SIGTERM) @@ -1247,13 +1254,24 @@ jumpback_sighandler (int signal) else if (signal == SIGHUP) oldint.sa_handler (signal); /* no others should be set up */ + + /* Note that we may not get here since + the SIGTERM handler in slave.c + might have in fact done the big Longjmp + to the slave's death */ - if (do_jumpback) { - do_jumpback = FALSE; + if (old_do_jumpback) { Longjmp (signal_jumpback, 1); } } +/* + * This sets up interruptes to be proxied and the + * gethostbyname/addr to be whacked using longjmp, + * in case INT/TERM/HUP was gotten in which case + * we no longer care for the result of the + * resolution. + */ #define SETUP_INTERRUPTS_FOR_TERM_DECLS \ struct sigaction term; diff --git a/daemon/server.c b/daemon/server.c index 2094746e..7fc834da 100644 --- a/daemon/server.c +++ b/daemon/server.c @@ -173,8 +173,12 @@ gdm_server_reinit (GdmDisplay *disp) gdm_sigchld_block_pop (); /* a hack we have no way of knowing when the server died */ sleep (1); + return; } + /* Do note the interaction of this Setjmp and the signal + handlers and the Setjmp in slave.c */ + if (Setjmp (reinitjmp) == 0) { /* come here and we'll whack the server and wait to get an xio error */ @@ -192,6 +196,7 @@ gdm_server_reinit (GdmDisplay *disp) XCloseDisplay (disp->dsp); disp->dsp = NULL; } + sleep (1); Longjmp (reinitjmp, 1); } gdm_sigchld_block_pop (); @@ -218,6 +223,8 @@ gdm_server_reinit (GdmDisplay *disp) void gdm_server_stop (GdmDisplay *disp) { + static gboolean waiting_for_server = FALSE; + if (disp == NULL) return; @@ -242,13 +249,23 @@ gdm_server_stop (GdmDisplay *disp) /* avoid SIGCHLD race */ gdm_sigchld_block_push (); - servpid = disp->servpid; + + if (waiting_for_server) { + gdm_error ("gdm_server_stop: Some problem killing server, whacking with SIGKILL"); + if (disp->servpid > 1) + kill (disp->servpid, SIGKILL); + + } else { + if (disp->servpid > 1 && + kill (disp->servpid, SIGTERM) == 0) { + waiting_for_server = TRUE; + ve_waitpid_no_signal (disp->servpid, 0, 0); + waiting_for_server = FALSE; + } + } disp->servpid = 0; - if (servpid > 1 && - kill (servpid, SIGTERM) == 0) - ve_waitpid_no_signal (servpid, 0, 0); gdm_sigchld_block_pop (); gdm_server_whack_lockfile (disp); diff --git a/daemon/slave.c b/daemon/slave.c index 7fc6e0b4..71a4e8ef 100644 --- a/daemon/slave.c +++ b/daemon/slave.c @@ -84,8 +84,8 @@ static gboolean gdm_wait_for_ack = TRUE; /* wait for ack on all messages to * the daemon */ static int in_session_stop = 0; static int in_usr2_signal = 0; -static gboolean need_to_abort_after_session_stop = FALSE; -static gboolean just_abort_on_TERM = FALSE; +static gboolean need_to_quit_after_session_stop = FALSE; +static int exit_code_to_use = DISPLAY_REMANAGE; static gboolean session_started = FALSE; static gboolean greeter_disabled = FALSE; static gboolean greeter_no_focus = FALSE; @@ -196,6 +196,31 @@ static gboolean gdm_got_ack = FALSE; static char * gdm_ack_response = NULL; static GList *unhandled_notifies = NULL; + +/* for signals that want to exit */ +static Jmp_buf slave_start_jmp; +static gboolean return_to_slave_start_jmp = FALSE; +static gboolean already_in_slave_start_jmp = FALSE; +static char *slave_start_jmp_error_to_print = NULL; +enum { + JMP_FIRST_RUN = 0, + JMP_SESSION_STOP_AND_QUIT = 1, + JMP_JUST_QUIT_QUICKLY = 2 +}; +#define SIGNAL_EXIT_WITH_JMP(d,how) \ + { \ + if ((d)->slavepid == getpid () && return_to_slave_start_jmp) { \ + already_in_slave_start_jmp = TRUE; \ + Longjmp (slave_start_jmp, how); \ + } else { \ + /* evil! how this this happen */ \ + if (slave_start_jmp_error_to_print != NULL) \ + gdm_error (slave_start_jmp_error_to_print); \ + gdm_error ("Bad (very very VERY bad!) things happening in signal"); \ + _exit (DISPLAY_REMANAGE); \ + } \ + } + /* notify all waitpids, make waitpids check notifies */ static void slave_waitpid_notify_all (void) @@ -258,7 +283,13 @@ slave_waitpid (GdmWaitPid *wp) /* This is a real stupid fallback for a real stupid case */ while (wp->pid > 1) { - sleep (5); + struct timeval tv; + /* Wait 5 seconds. */ + tv.tv_sec = 5; + tv.tv_usec = 0; + select (0, NULL, NULL, NULL, &tv); + /* don't want to use sleep since we're using alarm + for pinging */ check_notifies_now (); } check_notifies_now (); @@ -381,6 +412,45 @@ whack_greeter_fds (void) greeter_fd_in = -1; } +static void +term_session_stop_and_quit (void) +{ + gdm_in_signal = 0; + already_in_slave_start_jmp = TRUE; + gdm_wait_for_ack = FALSE; + need_to_quit_after_session_stop = TRUE; + + if (slave_start_jmp_error_to_print != NULL) + gdm_error (slave_start_jmp_error_to_print); + slave_start_jmp_error_to_print = NULL; + + /* only if we're not hanging in session stop and getting a + TERM signal again */ + if (in_session_stop == 0 && session_started) + gdm_slave_session_stop (d->logged_in && login != NULL, + TRUE /* no_shutdown_check */); + + gdm_debug ("term_session_stop_and_quit: Final cleanup"); + + gdm_slave_quick_exit (exit_code_to_use); +} + +static void +term_quit (void) +{ + gdm_in_signal = 0; + already_in_slave_start_jmp = TRUE; + gdm_wait_for_ack = FALSE; + need_to_quit_after_session_stop = TRUE; + + if (slave_start_jmp_error_to_print != NULL) + gdm_error (slave_start_jmp_error_to_print); + slave_start_jmp_error_to_print = NULL; + + gdm_debug ("term_session_stop_and_quit: Final cleanup"); + + gdm_slave_quick_exit (exit_code_to_use); +} void gdm_slave_start (GdmDisplay *display) @@ -390,11 +460,28 @@ gdm_slave_start (GdmDisplay *display) static sigset_t mask; struct sigaction alrm, term, child, usr2; - if (!display) - return; + if (display == NULL) { + /* saaay ... what? */ + _exit (DISPLAY_REMANAGE); + } gdm_debug ("gdm_slave_start: Starting slave process for %s", display->name); + switch (Setjmp (slave_start_jmp)) { + case JMP_FIRST_RUN: + return_to_slave_start_jmp = TRUE; + break; + case JMP_SESSION_STOP_AND_QUIT: + term_session_stop_and_quit (); + /* huh? should never get here */ + _exit (DISPLAY_REMANAGE); + default: + case JMP_JUST_QUIT_QUICKLY: + term_quit (); + /* huh? should never get here */ + _exit (DISPLAY_REMANAGE); + } + if (display->type == TYPE_XDMCP && GdmPingInterval > 0) { /* Handle a ALRM signals from our ping alarms */ @@ -466,8 +553,10 @@ gdm_slave_start (GdmDisplay *display) gdm_slave_run (display); /* remote and flexi only run once */ - if (display->type != TYPE_LOCAL) - break; + if (display->type != TYPE_LOCAL) { + gdm_server_stop (display); + gdm_slave_quick_exit (DISPLAY_REMANAGE); + } the_time = time (NULL); @@ -478,9 +567,7 @@ gdm_slave_start (GdmDisplay *display) first_time = the_time; death_count = 0; } else if (death_count > 6) { - /* exitting the loop will cause an - * abort actually */ - break; + gdm_slave_quick_exit (DISPLAY_ABORT); } gdm_debug ("gdm_slave_start: Reinitializing things"); @@ -494,13 +581,17 @@ gdm_slave_start (GdmDisplay *display) } else { /* OK about to start again so rebake our cookies and reinit * the server */ - if ( ! gdm_auth_secure_display (d)) - break; + if ( ! gdm_auth_secure_display (d)) { + gdm_slave_quick_exit (DISPLAY_REMANAGE); + } gdm_slave_send_string (GDM_SOP_COOKIE, d->cookie); gdm_server_reinit (d); } } + /* very very very evil, should never break, we can't return from + here sanely */ + _exit (DISPLAY_ABORT); } static gboolean @@ -1008,6 +1099,10 @@ gdm_slave_run (GdmDisplay *display) * so no need to reinit the server nor rebake cookies * nor such nonsense */ } while (greet); + + /* If XDMCP stop pinging */ + if (d->type == TYPE_XDMCP) + alarm (0); } /* A hack really, this will wait around until the first mapped window @@ -1210,7 +1305,7 @@ run_config (GdmDisplay *display, struct passwd *pwent) openlog ("gdm", LOG_PID, LOG_DAEMON); - if (chdir ("/root") != 0) + if (chdir (pwent->pw_dir) != 0) chdir ("/"); /* exec the configurator */ @@ -1607,7 +1702,7 @@ run_pictures (void) g_free (cfgdir); if (picfile == NULL) { - picfile = g_strconcat (pwent->pw_dir, "/.face", NULL); + picfile = g_build_filename (pwent->pw_dir, ".face", NULL); if (access (picfile, R_OK) != 0) { g_free (picfile); picfile = NULL; @@ -1802,7 +1897,7 @@ gdm_slave_greeter (void) /* Open a pipe for greeter communications */ if (pipe (pipe1) < 0 || pipe (pipe2) < 0) - gdm_slave_exit (DISPLAY_ABORT, _("%s: Can't init pipe to gdmgreeter"), + gdm_slave_exit (DISPLAY_REMANAGE, _("%s: Can't init pipe to gdmgreeter"), "gdm_slave_greeter"); /* hackish ain't it */ @@ -2018,7 +2113,7 @@ gdm_slave_greeter (void) case -1: d->greetpid = 0; - gdm_slave_exit (DISPLAY_ABORT, _("%s: Can't fork gdmgreeter process"), "gdm_slave_greeter"); + gdm_slave_exit (DISPLAY_REMANAGE, _("%s: Can't fork gdmgreeter process"), "gdm_slave_greeter"); default: close (pipe1[0]); @@ -2067,8 +2162,19 @@ gdm_slave_send (const char *str, gboolean wait_for_ack) if ( ! gdm_wait_for_ack) wait_for_ack = FALSE; - if (gdm_in_signal == 0) - gdm_debug ("Sending %s", str); + /* Evil!, all this for debugging? */ + if (GdmDebug && gdm_in_signal == 0) { + if (strncmp (str, GDM_SOP_COOKIE " ", + strlen (GDM_SOP_COOKIE " ")) == 0) { + char *s = g_strndup + (str, strlen (GDM_SOP_COOKIE " XXXX XX")); + /* cut off most of the cookie for "security" */ + gdm_debug ("Sending %s...", s); + g_free (s); + } else { + gdm_debug ("Sending %s", str); + } + } if (wait_for_ack) { gdm_got_ack = FALSE; @@ -2084,8 +2190,12 @@ gdm_slave_send (const char *str, gboolean wait_for_ack) } if (fd < 0) { + /* FIXME: This is not likely to ever be used, remove + at some point. Other then slaves shouldn't be using + these functions. And if the pipe creation failed + in main daemon just abort the main daemon. */ /* Use the fifo as a fallback only now that we have a pipe */ - fifopath = g_strconcat (GdmServAuthDir, "/.gdmfifo", NULL); + fifopath = g_build_filename (GdmServAuthDir, ".gdmfifo", NULL); old = geteuid (); if (old != 0) seteuid (0); @@ -2132,14 +2242,30 @@ gdm_slave_send (const char *str, gboolean wait_for_ack) gdm_slave_handle_usr2_message (); } } else { - sleep (1); + struct timeval tv; + /* Wait 1 second. */ + tv.tv_sec = 1; + tv.tv_usec = 0; + select (0, NULL, NULL, NULL, &tv); + /* don't want to use sleep since we're using alarm + for pinging */ } } if (wait_for_ack && ! gdm_got_ack && - gdm_in_signal == 0) - gdm_error ("Timeout occured for sending message %s", str); + gdm_in_signal == 0) { + if (strncmp (str, GDM_SOP_COOKIE " ", + strlen (GDM_SOP_COOKIE " ")) == 0) { + char *s = g_strndup + (str, strlen (GDM_SOP_COOKIE " XXXX XX")); + /* cut off most of the cookie for "security" */ + gdm_debug ("Timeout occured for sending message %s...", s); + g_free (s); + } else { + gdm_debug ("Timeout occured for sending message %s", str); + } + } } void @@ -2233,7 +2359,7 @@ gdm_slave_chooser (void) /* Open a pipe for chooser communications */ if (pipe (p) < 0) - gdm_slave_exit (DISPLAY_ABORT, _("gdm_slave_chooser: Can't init pipe to gdmchooser")); + gdm_slave_exit (DISPLAY_REMANAGE, _("gdm_slave_chooser: Can't init pipe to gdmchooser")); /* Run the init script. gdmslave suspends until script has terminated */ gdm_slave_exec_script (d, GdmDisplayInit, NULL, NULL, @@ -2327,7 +2453,7 @@ gdm_slave_chooser (void) gdm_child_exit (DISPLAY_REMANAGE, _("gdm_slave_chooser: Error starting chooser on display %s"), d->name); case -1: - gdm_slave_exit (DISPLAY_ABORT, _("gdm_slave_chooser: Can't fork gdmchooser process")); + gdm_slave_exit (DISPLAY_REMANAGE, _("gdm_slave_chooser: Can't fork gdmchooser process")); default: gdm_debug ("gdm_slave_chooser: Chooser on pid %d", d->chooserpid); @@ -2389,7 +2515,7 @@ is_session_ok (const char *session_name) if (ve_string_empty (GdmSessDir)) return FALSE; - file = g_strconcat (GdmSessDir, "/", session_name, NULL); + file = g_build_filename (GdmSessDir, session_name, NULL); if (access (file, F_OK) == 0) { g_free (file); return TRUE; @@ -2431,7 +2557,7 @@ get_session_exec (const char *desktop) VeConfig *cfg; char *exec; - file = g_strconcat (GdmSessDir, "/", desktop, NULL); + file = g_build_filename (GdmSessDir, desktop, NULL); cfg = ve_config_get (file); g_free (file); exec = ve_config_get_string (cfg, "Desktop Entry/Exec"); @@ -2491,6 +2617,10 @@ session_child_run (struct passwd *pwent, char *argv[4]; gdm_unset_signals (); + if (setsid() < 0) + /* should never happen */ + gdm_error (_("%s: setsid() failed: %s!"), + "session_child_run", strerror(errno)); ve_setenv ("XAUTHORITY", GDM_AUTHFILE (d), TRUE); @@ -2501,9 +2631,9 @@ session_child_run (struct passwd *pwent, * unless in failsafe mode which needs to work when there is * no diskspace as well */ if ( ! failsafe && home_dir_ok) { - char *filename = g_strconcat (home_dir, - "/.xsession-errors", - NULL); + char *filename = g_build_filename (home_dir, + ".xsession-errors", + NULL); uid_t old = geteuid (); uid_t oldg = getegid (); @@ -2558,11 +2688,6 @@ session_child_run (struct passwd *pwent, gdm_clearenv (); - if (setsid() < 0) - /* should never happen */ - gdm_error (_("%s: setsid() failed: %s!"), - "session_child_run", strerror(errno)); - /* Prepare user session */ ve_setenv ("XAUTHORITY", d->userauth, TRUE); ve_setenv ("DISPLAY", d->name, TRUE); @@ -2613,6 +2738,10 @@ session_child_run (struct passwd *pwent, * not to leave the egid around */ setegid (pwent->pw_gid); + if (chdir (home_dir) != 0) { + chdir ("/"); + } + #ifdef HAVE_LOGINCAP if (setusercontext (NULL, pwent, pwent->pw_uid, LOGIN_SETLOGIN | LOGIN_SETPATH | @@ -2637,10 +2766,8 @@ session_child_run (struct passwd *pwent, ve_setenv ("GDM_LANG", language, TRUE); } - chdir (home_dir); - if (usrcfgok && savesess && home_dir_ok) { - gchar *cfgstr = g_strconcat (home_dir, "/.dmrc", NULL); + gchar *cfgstr = g_build_filename (home_dir, ".dmrc", NULL); if (dmrc == NULL) dmrc = ve_config_new (cfgstr); ve_config_set_string (dmrc, "Desktop/Session", @@ -2649,7 +2776,7 @@ session_child_run (struct passwd *pwent, } if (usrcfgok && savelang && home_dir_ok) { - gchar *cfgstr = g_strconcat (home_dir, "/.dmrc", NULL); + gchar *cfgstr = g_build_filename (home_dir, ".dmrc", NULL); if (dmrc == NULL) dmrc = ve_config_new (cfgstr); if (ve_string_empty (language)) @@ -2857,11 +2984,19 @@ gdm_slave_session_start (void) } /* Run the PostLogin script */ - gdm_slave_exec_script (d, GdmPostLogin, - login, pwent, - TRUE /* pass_stdout */, - TRUE /* set_parent */); - /* FIXME: ignore errors? */ + if (gdm_slave_exec_script (d, GdmPostLogin, + login, pwent, + TRUE /* pass_stdout */, + TRUE /* set_parent */) != EXIT_SUCCESS && + /* ignore errors in failsafe modes */ + ! failsafe) { + session_started = FALSE; + gdm_verify_cleanup (d); + gdm_error (_("gdm_slave_session_start: Execution of PostLogin script returned > 0. Aborting.")); + /* script failed so just try again */ + return; + + } if (pwent->pw_dir == NULL || ! g_file_test (pwent->pw_dir, G_FILE_TEST_IS_DIR)) { @@ -2909,7 +3044,7 @@ gdm_slave_session_start (void) } if (usrcfgok) { - gchar *cfgfile = g_strconcat (home_dir, "/.dmrc", NULL); + gchar *cfgfile = g_build_filename (home_dir, ".dmrc", NULL); VeConfig *cfg = ve_config_new (cfgfile); g_free (cfgfile); @@ -3113,7 +3248,7 @@ gdm_slave_session_start (void) if ((/* sanity */ end_time >= session_start_time) && (end_time - 10 <= session_start_time)) { - char *errfile = g_strconcat (home_dir, "/.xsession-errors", NULL); + char *errfile = g_build_filename (home_dir, ".xsession-errors", NULL); gdm_debug ("Session less than 10 seconds!"); /* FIXME: perhaps do some checking to display a better error, @@ -3218,10 +3353,10 @@ gdm_slave_session_stop (gboolean run_post_session, in_session_stop --; - if (need_to_abort_after_session_stop) { + if (need_to_quit_after_session_stop) { gdm_debug ("gdm_slave_session_stop: Final cleanup"); - gdm_slave_quick_exit (DISPLAY_ABORT); + gdm_slave_quick_exit (exit_code_to_use); } #ifdef __linux__ @@ -3235,7 +3370,7 @@ gdm_slave_session_stop (gboolean run_post_session, up again and then whacked. Waiting is safer then DISPLAY_ABORT, since if we really do get this wrong, then at the worst case the user will wait for a few moments. */ - if ( ! need_to_abort_after_session_stop && + if ( ! need_to_quit_after_session_stop && ! no_shutdown_check && access ("/sbin/runlevel", X_OK) == 0) { char ign; @@ -3247,15 +3382,19 @@ gdm_slave_session_stop (gboolean run_post_session, /* this is a stupid loop, but we may be getting signals, so we don't want to just do sleep (30) */ time_t c = time (NULL); - just_abort_on_TERM = TRUE; gdm_info (_("GDM detected a shutdown or reboot " "in progress.")); fclose (fp); while (c + 30 > time (NULL)) { - sleep (5); + struct timeval tv; + /* Wait 30 seconds. */ + tv.tv_sec = 30; + tv.tv_usec = 0; + select (0, NULL, NULL, NULL, &tv); + /* don't want to use sleep since we're using alarm + for pinging */ } /* hmm, didn't get TERM, weird */ - just_abort_on_TERM = FALSE; } else { fclose (fp); } @@ -3268,28 +3407,50 @@ gdm_slave_term_handler (int sig) { gdm_in_signal++; gdm_wait_for_ack = FALSE; + static gboolean got_term_before = FALSE; gdm_debug ("gdm_slave_term_handler: %s got TERM/INT signal", d->name); - if ( ! just_abort_on_TERM) { - /* this should stop some races */ - if (in_session_stop > 0 && ! need_to_abort_after_session_stop) { - gdm_in_signal--; - gdm_wait_for_ack = TRUE; - need_to_abort_after_session_stop = TRUE; - return; - } + exit_code_to_use = DISPLAY_ABORT; + need_to_quit_after_session_stop = TRUE; + + if (already_in_slave_start_jmp || + (got_term_before && in_session_stop > 0)) { + gdm_sigchld_block_push (); + /* be very very very nasty to the extra process if the user is really + trying to get rid of us */ + if (extra_process > 1) + kill (-(extra_process), SIGKILL); + /* also be very nasty to the X server at this stage */ + if (d->servpid > 1) + kill (d->servpid, SIGKILL); + gdm_sigchld_block_pop (); + gdm_in_signal--; + got_term_before = TRUE; + /* we're already quitting, just a matter of killing all the processes */ + return; + } + got_term_before = TRUE; - /* only if we're not hanging in session stop and getting a - TERM signal again */ - if (in_session_stop == 0 && session_started) - gdm_slave_session_stop (d->logged_in && login != NULL, - TRUE /* no_shutdown_check */); + /* just in case this was set to something else, like during + * server reinit */ + XSetIOErrorHandler (gdm_slave_xioerror_handler); + + if (in_session_stop > 0) { + /* the need_to_quit_after_session_stop is now set so things will + work out right */ + gdm_in_signal--; + return; } - gdm_debug ("gdm_slave_term_handler: Final cleanup"); + if (session_started) { + SIGNAL_EXIT_WITH_JMP (d, JMP_SESSION_STOP_AND_QUIT); + } else { + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); + } - gdm_slave_quick_exit (DISPLAY_ABORT); + /* never reached */ + gdm_in_signal--; } /* called on alarms to ping */ @@ -3298,6 +3459,9 @@ gdm_slave_alrm_handler (int sig) { static gboolean in_ping = FALSE; + if (already_in_slave_start_jmp) + return; + gdm_in_signal++; gdm_debug ("gdm_slave_alrm_handler: %s got ARLM signal, " @@ -3310,13 +3474,17 @@ gdm_slave_alrm_handler (int sig) } if (in_ping) { - /* darn, the last ping didn't succeed, wipe this display */ - gdm_slave_session_stop (d->logged_in && login != NULL, - FALSE /* no_shutdown_check */); - - gdm_slave_exit (DISPLAY_REMANAGE, - _("Ping to %s failed, whacking display!"), - d->name); + slave_start_jmp_error_to_print = + g_strdup_printf (_("Ping to %s failed, whacking display!"), + d->name); + need_to_quit_after_session_stop = TRUE; + exit_code_to_use = DISPLAY_REMANAGE; + + if (session_started) { + SIGNAL_EXIT_WITH_JMP (d, JMP_SESSION_STOP_AND_QUIT); + } else { + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); + } } in_ping = TRUE; @@ -3339,6 +3507,9 @@ gdm_slave_child_handler (int sig) pid_t pid; uid_t old; + if (already_in_slave_start_jmp) + return; + gdm_in_signal++; gdm_debug ("gdm_slave_child_handler"); @@ -3372,7 +3543,9 @@ gdm_slave_child_handler (int sig) if (pid == d->greetpid && greet) { if (WIFEXITED (status) && WEXITSTATUS (status) == DISPLAY_RESTARTGREETER) { - gdm_slave_desensitize_config (); + /* FIXME: shouldn't do this from + a signal handler */ + /*gdm_slave_desensitize_config ();*/ greet = FALSE; d->greetpid = 0; @@ -3401,16 +3574,19 @@ gdm_slave_child_handler (int sig) WEXITSTATUS (status) == DISPLAY_SUSPEND || WEXITSTATUS (status) == DISPLAY_RUN_CHOOSER || WEXITSTATUS (status) == DISPLAY_RESTARTGDM)) { - gdm_slave_quick_exit (WEXITSTATUS (status)); + exit_code_to_use = WEXITSTATUS (status); + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); } else { if (WIFSIGNALED (status) && (WTERMSIG (status) == SIGSEGV || WTERMSIG (status) == SIGABRT || WTERMSIG (status) == SIGPIPE || WTERMSIG (status) == SIGBUS)) { - gdm_slave_quick_exit (DISPLAY_GREETERFAILED); + exit_code_to_use = DISPLAY_GREETERFAILED; + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); } else { - gdm_slave_quick_exit (DISPLAY_REMANAGE); + exit_code_to_use = DISPLAY_REMANAGE; + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); } } } else if (pid != 0 && pid == d->sesspid) { @@ -3426,8 +3602,10 @@ gdm_slave_child_handler (int sig) /* if not handled there is no need for further formalities, * we just have to die */ - if ( ! d->handled) - gdm_slave_quick_exit (DISPLAY_REMANAGE); + if ( ! d->handled) { + exit_code_to_use = DISPLAY_REMANAGE; + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); + } gdm_slave_send_num (GDM_SOP_XPID, 0); @@ -3506,7 +3684,13 @@ gdm_slave_handle_usr2_message (void) if (d->type != TYPE_FLEXI_XNEST && d->type != TYPE_FLEXI) { if ( ! d->logged_in) { - gdm_slave_quick_exit (DISPLAY_REMANAGE); + if (gdm_in_signal > 0) { + exit_code_to_use = DISPLAY_REMANAGE; + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); + } else { + /* FIXME: are we ever not in signal here? */ + gdm_slave_quick_exit (DISPLAY_REMANAGE); + } } else { remanage_asap = TRUE; } @@ -3544,28 +3728,40 @@ gdm_slave_xerror_handler (Display *disp, XErrorEvent *evt) static gint gdm_slave_xioerror_handler (Display *disp) { + if (already_in_slave_start_jmp) { + /* eki eki eki, this is not good, + should only happen if we get some io error after + we have gotten a SIGTERM */ + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); + } + gdm_in_signal++; /* Display is all gone */ d->dsp = NULL; - gdm_debug ("gdm_slave_xioerror_handler: I/O error for display %s", d->name); - - gdm_slave_session_stop (d->logged_in && login != NULL, - FALSE /* no_shutdown_check */); - - gdm_error (_("%s: Fatal X error - Restarting %s"), - "gdm_slave_xioerror_handler", d->name); - if ((d->type == TYPE_LOCAL || d->type == TYPE_FLEXI) && (do_xfailed_on_xio_error || d->starttime + 5 >= time (NULL))) { - gdm_slave_quick_exit (DISPLAY_XFAILED); + exit_code_to_use = DISPLAY_XFAILED; } else { - gdm_slave_quick_exit (DISPLAY_REMANAGE); + exit_code_to_use = DISPLAY_REMANAGE; } + slave_start_jmp_error_to_print = + g_strdup_printf (_("%s: Fatal X error - Restarting %s"), + "gdm_slave_xioerror_handler", d->name); + + need_to_quit_after_session_stop = TRUE; + + if (session_started) { + SIGNAL_EXIT_WITH_JMP (d, JMP_SESSION_STOP_AND_QUIT); + } else { + SIGNAL_EXIT_WITH_JMP (d, JMP_JUST_QUIT_QUICKLY); + } + + /* never reached */ gdm_in_signal--; return 0; @@ -3694,6 +3890,10 @@ gdm_slave_quick_exit (gint status) * so no need doing XCloseDisplay which * may just get us an XIOError */ d->dsp = NULL; + /* just in case we do get the XIOError, + don't run session_stop since we've + requested a quick exit */ + session_started = FALSE; /* No need to send the PIDS to the daemon * since we'll just exit cleanly */ @@ -3714,13 +3914,13 @@ gdm_slave_quick_exit (gint status) kill (-(d->sesspid), SIGTERM); d->sesspid = 0; - gdm_server_stop (d); - gdm_verify_cleanup (d); - if (extra_process > 1) kill (-(extra_process), SIGTERM); extra_process = 0; + gdm_verify_cleanup (d); + gdm_server_stop (d); + if (d->servpid > 1) kill (d->servpid, SIGTERM); d->servpid = 0; @@ -3822,14 +4022,14 @@ gdm_slave_exec_script (GdmDisplay *d, const gchar *dir, const char *login, if (!d || ve_string_empty (dir)) return EXIT_SUCCESS; - script = g_strconcat (dir, "/", d->name, NULL); + script = g_build_filename (dir, d->name, NULL); if (access (script, R_OK|X_OK) != 0) { g_free (script); script = NULL; } if (script == NULL && ! ve_string_empty (d->hostname)) { - script = g_strconcat (dir, "/", d->hostname, NULL); + script = g_build_filename (dir, d->hostname, NULL); if (access (script, R_OK|X_OK) != 0) { g_free (script); script = NULL; @@ -3837,7 +4037,7 @@ gdm_slave_exec_script (GdmDisplay *d, const gchar *dir, const char *login, } if (script == NULL && d->type == TYPE_XDMCP) { - script = g_strconcat (dir, "/XDMCP", NULL); + script = g_build_filename (dir, "XDMCP", NULL); if (access (script, R_OK|X_OK) != 0) { g_free (script); script = NULL; @@ -3845,14 +4045,14 @@ gdm_slave_exec_script (GdmDisplay *d, const gchar *dir, const char *login, } if (script == NULL && SERVER_IS_FLEXI (d)) { - script = g_strconcat (dir, "/Flexi", NULL); + script = g_build_filename (dir, "Flexi", NULL); if (access (script, R_OK|X_OK) != 0) { g_free (script); script = NULL; } } if (script == NULL) { - script = g_strconcat (dir, "/Default", NULL); + script = g_build_filename (dir, "Default", NULL); if (access (script, R_OK|X_OK) != 0) { g_free (script); script = NULL; @@ -3906,7 +4106,10 @@ gdm_slave_exec_script (GdmDisplay *d, const gchar *dir, const char *login, } else { ve_setenv ("HOME", pwent->pw_dir, TRUE); ve_setenv ("PWD", pwent->pw_dir, TRUE); - chdir (pwent->pw_dir); + if (chdir (pwent->pw_dir) != 0) { + chdir ("/"); + ve_setenv ("PWD", "/", TRUE); + } } ve_setenv ("SHELL", pwent->pw_shell, TRUE); } else { diff --git a/daemon/verify-pam.c b/daemon/verify-pam.c index e0900de1..0446f5e5 100644 --- a/daemon/verify-pam.c +++ b/daemon/verify-pam.c @@ -122,10 +122,13 @@ gdm_verify_pam_conv (int num_msg, const struct pam_message **msg, char *s; struct pam_response *reply = NULL; const char *login; + + if (pamh == NULL) + return PAM_CONV_ERR; reply = malloc (sizeof (struct pam_response) * num_msg); - if (!reply) + if (reply == NULL) return PAM_CONV_ERR; memset (reply, 0, sizeof (struct pam_response) * num_msg); @@ -261,9 +264,12 @@ gdm_verify_standalone_pam_conv (int num_msg, const struct pam_message **msg, char *s, *text; struct pam_response *reply = NULL; + if (pamh == NULL) + return PAM_CONV_ERR; + reply = malloc (sizeof (struct pam_response) * num_msg); - if (!reply) + if (reply == NULL) return PAM_CONV_ERR; memset (reply, 0, sizeof (struct pam_response) * num_msg); @@ -894,16 +900,22 @@ gdm_verify_cleanup (GdmDisplay *d) if (pamh != NULL) { gint pamerr; + pam_handle_t *tmp_pamh; + + gdm_debug ("Running gdm_verify_cleanup and pamh != NULL"); + + gdm_sigterm_block_push (); + tmp_pamh = pamh; + pamh = NULL; + gdm_sigterm_block_pop (); /* Close the users session */ - pamerr = pam_close_session (pamh, 0); + pamerr = pam_close_session (tmp_pamh, 0); /* Throw away the credentials */ - pamerr = pam_setcred (pamh, PAM_DELETE_CRED); + pamerr = pam_setcred (tmp_pamh, PAM_DELETE_CRED); - if (pamh != NULL) - pam_end (pamh, pamerr); - pamh = NULL; + pam_end (tmp_pamh, pamerr); /* Workaround to avoid gdm messages being logged as PAM_pwdb */ closelog (); diff --git a/docs/C/gdm.xml b/docs/C/gdm.xml index 87629758..80d5841d 100644 --- a/docs/C/gdm.xml +++ b/docs/C/gdm.xml @@ -574,7 +574,9 @@ if you need to (though you should use the <filename>pam_mount</filename> module if you can for this). You have the $USER and $DISPLAY environment variables set for this script, - and again it is run as root. + and again it is run as root. The script should return 0 on success + as otherwise the user won't be logged in. This is not true for + failsafe session showever. </para> <para> @@ -585,9 +587,9 @@ script for local session management or accounting stuff. The $USER environment variable contains the login of the authenticated user and $DISPLAY is set to the current display. - The script should return 0 on success. Any + The script should return 0 on success. Any other value will cause GDM to terminate the current login - process. + process. This is not true for failsafe sessions however. Also $X_SERVERS environmental variable is set and this points to a fake generated x servers file for use with the sessreg accounting program. @@ -1014,7 +1016,7 @@ <synopsis>RootPath=/sbin:/usr/sbin:/bin:/usr/bin:/usr/bin/X11:/usr/local/bin</synopsis> <para> Specifies the path which will be set in the root's - session and the {Init,PreSession,PostSession} scripts + session and the {Init,PostLogin,PreSession,PostSession} scripts executed by GDM. </para> </listitem> @@ -1027,11 +1029,20 @@ <para> Directory containing the X authentication files for the individual displays. Should be owned by - <filename>gdm.gdm</filename> with permissions 750. + <filename>root.gdm</filename> with permissions 1770. + That is should be owned by root, with gdm group having + full write permissions and the directory should be + sticky and others should have no permission to the directory. + This way the gdm user can't remove files owned + by root in that directory, while still being able to + write its own files there. GDM will attempt to change + permissions for you when it's first run if the permissions + are not the above. This directory is also used for other private files that - the daemon needs to store. Other user should not + the daemon needs to store. Other users should not have any way to get into this directory and read/change - it's contents. + it's contents. Anybody who can read this directory can + connect to any display on this machine. </para> </listitem> </varlistentry> diff --git a/gdm.spec.in b/gdm.spec.in index a770ce6a..20623e77 100644 --- a/gdm.spec.in +++ b/gdm.spec.in @@ -134,7 +134,8 @@ exit 0 # %{_datadir}/pixmaps/* # %{_datadir}/gnome/help/gdm/* # %{_datadir}/gnome/help/gdmsetup/* -%attr(750, gdm, gdm) %dir %{localstatedir}/gdm +%attr(1770, root, gdm) %dir %{localstatedir}/gdm +%attr(755, root, root) %dir %{localstatedir}/log/gdm %changelog diff --git a/gui/gdmchooser.c b/gui/gdmchooser.c index 6681c297..aa71495b 100644 --- a/gui/gdmchooser.c +++ b/gui/gdmchooser.c @@ -83,6 +83,7 @@ static gint connection_type = 0; static void gdm_chooser_abort (const gchar *format, ...) G_GNUC_PRINTF (1, 2); static void gdm_chooser_warn (const gchar *format, ...) G_GNUC_PRINTF (1, 2); +static void set_background (void); /* Exported for glade */ void gdm_chooser_cancel (void); @@ -112,6 +113,10 @@ static guint scan_time_handler = 0; static int ping_tries = PING_TRIES; static guint ping_try_handler = 0; +/* set in the main function */ +static char **stored_argv = NULL; +static int stored_argc = 0; + /* Fixetyfix */ int XdmcpReallocARRAY8 (ARRAY8Ptr array, int length); @@ -142,6 +147,7 @@ static gchar *GdmHostIconDir; static gchar *GdmHostDefaultIcon; static gchar *GdmGtkRC; static gchar *GdmHosts; +static gchar *GdmHostsOrig; static gboolean GdmBroadcast; static gboolean GdmAllowAdd; static gchar *GdmBackgroundColor; @@ -1055,6 +1061,7 @@ gdm_chooser_parse_config (void) /* note that command line arguments will prevail over these */ GdmHosts = ve_config_get_string (cfg, GDM_KEY_HOSTS); + GdmHostsOrig = g_strdup (GdmHosts); GdmBroadcast = ve_config_get_bool (cfg, GDM_KEY_BROADCAST); /* if broadcasting, then append BROADCAST to hosts */ if (GdmBroadcast) { @@ -1250,24 +1257,111 @@ gdm_chooser_gui_init (void) } } +static gboolean +string_same (VeConfig *config, const char *cur, const char *key) +{ + char *val = ve_config_get_string (config, key); + if (strcmp (ve_sure_string (cur), ve_sure_string (val)) == 0) { + g_free (val); + return TRUE; + } else { + g_free (val); + return FALSE; + } +} + +static gboolean +bool_same (VeConfig *config, gboolean cur, const char *key) +{ + gboolean val = ve_config_get_bool (config, key); + if (ve_bool_equal (cur, val)) { + return TRUE; + } else { + return FALSE; + } +} + +static gboolean +int_same (VeConfig *config, int cur, const char *key) +{ + int val = ve_config_get_int (config, key); + if (cur == val) { + return TRUE; + } else { + return FALSE; + } +} + + +static gboolean +gdm_reread_config (int sig, gpointer data) +{ + VeConfig *config; + /* reparse config stuff here. At least ones we care about */ + + config = ve_config_get (GDM_CONFIG_FILE); + + /* FIXME: The following is evil, we should update on the fly rather + * then just restarting */ + /* Also we may not need to check ALL those keys but just a few */ + if ( ! string_same (config, GdmHostsOrig, GDM_KEY_HOSTS) || + ! string_same (config, GdmGtkRC, GDM_KEY_GTKRC) || + ! string_same (config, GdmHostDefaultIcon, GDM_KEY_HOST) || + ! string_same (config, GdmHostIconDir, GDM_KEY_HOSTDIR) || + ! int_same (config, + GdmXineramaScreen, GDM_KEY_XINERAMASCREEN) || + ! int_same (config, GdmIconMaxWidth, GDM_KEY_ICONWIDTH) || + ! int_same (config, GdmIconMaxHeight, GDM_KEY_ICONHEIGHT) || + ! int_same (config, GdmScanTime, GDM_KEY_SCAN) || + ! bool_same (config, GdmDebug, GDM_KEY_DEBUG)) { + if (RUNNING_UNDER_GDM) { + /* Set busy cursor */ + setup_cursor (GDK_WATCH); + + gdm_wm_save_wm_order (); + } + + /* we don't need to tell the slave that we're restarting + it doesn't care about our state. Unlike with the greeter */ + execvp (stored_argv[0], stored_argv); + _exit (DISPLAY_REMANAGE); + } + + /* we only use the color and do it for all types except NONE */ + if ( ! string_same (config, GdmBackgroundColor, GDM_KEY_BACKGROUNDCOLOR) || + ! int_same (config, GdmBackgroundType, GDM_KEY_BACKGROUNDTYPE)) { + set_background (); + } + + return TRUE; +} + static void gdm_chooser_signals_init (void) { struct sigaction hup; + struct sigaction term; sigset_t mask; - hup.sa_handler = (void *) gdm_chooser_cancel; + ve_signal_add (SIGHUP, gdm_reread_config, NULL); + + hup.sa_handler = ve_signal_notify; hup.sa_flags = 0; - sigemptyset (&hup.sa_mask); + sigemptyset(&hup.sa_mask); + sigaddset (&hup.sa_mask, SIGCHLD); + + term.sa_handler = (void *) gdm_chooser_cancel; + term.sa_flags = 0; + sigemptyset (&term.sa_mask); if (sigaction (SIGHUP, &hup, NULL) < 0) gdm_chooser_abort (_("gdm_signals_init: Error setting up HUP signal handler")); - if (sigaction (SIGINT, &hup, NULL) < 0) + if (sigaction (SIGINT, &term, NULL) < 0) gdm_chooser_abort (_("gdm_signals_init: Error setting up INT signal handler")); - if (sigaction (SIGTERM, &hup, NULL) < 0) + if (sigaction (SIGTERM, &term, NULL) < 0) gdm_chooser_abort (_("gdm_signals_init: Error setting up TERM signal handler")); sigfillset (&mask); @@ -1358,6 +1452,13 @@ main (int argc, char *argv[]) poptContext ctx; int nextopt; const char *gdm_version; + int i; + + stored_argv = g_new0 (char *, argc + 1); + for (i = 0; i < argc; i++) + stored_argv[i] = g_strdup (argv[i]); + stored_argv[i] = NULL; + stored_argc = argc; if (g_getenv ("RUNNING_UNDER_GDM") != NULL) RUNNING_UNDER_GDM = TRUE; @@ -1472,14 +1573,16 @@ main (int argc, char *argv[]) gdm_wm_focus_window (GDK_WINDOW_XWINDOW (chooser->window)); } - if (RUNNING_UNDER_GDM) - setup_cursor (GDK_LEFT_PTR); - if (GdmAllowAdd) gtk_widget_grab_focus (add_entry); gdm_chooser_add_entry_changed (); + if (RUNNING_UNDER_GDM) { + gdm_wm_restore_wm_order (); + setup_cursor (GDK_LEFT_PTR); + } + gtk_main(); exit (EXIT_SUCCESS); diff --git a/gui/gdmlogin.c b/gui/gdmlogin.c index e93277a0..b3423f78 100644 --- a/gui/gdmlogin.c +++ b/gui/gdmlogin.c @@ -2177,7 +2177,18 @@ gdm_login_browser_populate (void) for (li = users; li != NULL; li = li->next) { GdmLoginUser *usr = li->data; GtkTreeIter iter = {0}; - char *label = g_strdup_printf ("%s\n%s", usr->login, usr->gecos); + char *label; + char *login, *gecos; + + login = g_markup_escape_text (usr->login, -1); + gecos = g_markup_escape_text (usr->gecos, -1); + + label = g_strdup_printf ("<b>%s</b>\n%s", + login, + gecos); + + g_free (login); + g_free (gecos); gtk_list_store_append (GTK_LIST_STORE (browser_model), &iter); gtk_list_store_set (GTK_LIST_STORE (browser_model), &iter, GREETER_ULIST_ICON_COLUMN, usr->picture, @@ -2746,7 +2757,7 @@ gdm_login_gui_init (void) column = gtk_tree_view_column_new_with_attributes (_("Username"), gtk_cell_renderer_text_new (), - "text", GREETER_ULIST_LABEL_COLUMN, + "markup", GREETER_ULIST_LABEL_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (browser), column); diff --git a/gui/greeter/greeter_item_ulist.c b/gui/greeter/greeter_item_ulist.c index 12b08dbb..d0225471 100644 --- a/gui/greeter/greeter_item_ulist.c +++ b/gui/greeter/greeter_item_ulist.c @@ -298,7 +298,18 @@ greeter_populate_user_list (GtkTreeModel *tm) { GdmGreeterUser *usr = li->data; GtkTreeIter iter = {0}; - char *label = g_strdup_printf ("%s\n%s", usr->login, usr->gecos); + char *label; + char *login, *gecos; + + login = g_markup_escape_text (usr->login, -1); + gecos = g_markup_escape_text (usr->gecos, -1); + + label = g_strdup_printf ("<b>%s</b>\n%s", + login, + gecos); + + g_free (login); + g_free (gecos); gtk_list_store_append (GTK_LIST_STORE (tm), &iter); gtk_list_store_set (GTK_LIST_STORE (tm), &iter, GREETER_ULIST_ICON_COLUMN, usr->picture, @@ -402,7 +413,7 @@ greeter_generate_userlist (GtkWidget *tv) column = gtk_tree_view_column_new_with_attributes (_("Username"), gtk_cell_renderer_text_new (), - "text", GREETER_ULIST_LABEL_COLUMN, + "markup", GREETER_ULIST_LABEL_COLUMN, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column); |