This patch fixes an issue where gksudo would successfully authenticate the user, but fail to run the requested command. This was caused by gksudo forking a new PTY, meaning that different TTY was used for the "sudo -v" command and subsequent "sudo -n ..." command. Whilst this used to work, newer versions of sudo used per TTY authentication caching. Patch taken from Arch Linux: https://aur.archlinux.org/packages/libgksu/ --- libgksu/libgksu.c.orig 2009-06-29 13:48:24.000000000 -0400 +++ libgksu/libgksu.c 2010-01-12 07:32:10.450657456 -0500 @@ -1,7 +1,6 @@ /* * Gksu -- a library providing access to su functionality * Copyright (C) 2004-2009 Gustavo Noronha Silva - * Portions Copyright (C) 2009 VMware, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -56,9 +55,6 @@ static void gksu_context_launch_complete (GksuContext *context); -static void -read_line (int fd, gchar *buffer, int n); - GType gksu_error_get_type (void) { @@ -2009,8 +2005,6 @@ gksu_su_fuller (GksuContext *context, for (i = 0 ; cmd[i] != NULL ; i++) g_free (cmd[i]); g_free(cmd); - - _exit(1); } else if (pid == -1) { @@ -2125,10 +2119,10 @@ gksu_su_fuller (GksuContext *context, /* drop the \n echoed on password entry if su did request a password */ if (password_needed) - read_line (fdpty, buf, 255); + read (fdpty, buf, 255); if (context->debug) fprintf (stderr, "DEBUG (run:post-after-pass) buf: -%s-\n", buf); - read_line (fdpty, buf, 255); + read (fdpty, buf, 255); if (context->debug) fprintf (stderr, "DEBUG (run:post-after-pass) buf: -%s-\n", buf); } @@ -2142,9 +2136,7 @@ gksu_su_fuller (GksuContext *context, { int retval = 0; - /* Red Hat's su shows the full path to su in its error messages. */ - if (!strncmp (buf, "su:", 3) || - !strncmp (buf, "/bin/su:", 7)) + if (!strncmp (buf, "su", 2)) { gchar **strings; @@ -2155,11 +2147,7 @@ gksu_su_fuller (GksuContext *context, } strings = g_strsplit (buf, ":", 2); - - /* Red Hat and Fedora use 'incorrect password'. */ - if (strings[1] && - (g_str_has_prefix(strings[1], " Authentication failure") || - g_str_has_prefix(strings[1], " incorrect password"))) + if (strings[1] && !strncmp (strings[1], " Authentication failure", 23)) { if (used_gnome_keyring) g_set_error (error, gksu_quark, @@ -2473,12 +2461,6 @@ gksu_sudo_fuller (GksuContext *context, { char **cmd; char buffer[256] = {0}; - char *child_stderr = NULL; - /* This command is used to gain a token */ - char *const verifycmd[] = - { - "/usr/bin/sudo", "-p", "GNOME_SUDO_PASS", "-v", NULL - }; int argcount = 8; int i, j; @@ -2489,8 +2471,9 @@ gksu_sudo_fuller (GksuContext *context, pid_t pid; int status; - FILE *fdfile = NULL; - int fdpty = -1; + FILE *infile, *outfile; + int parent_pipe[2]; /* For talking to the parent */ + int child_pipe[2]; /* For talking to the child */ context->sudo_mode = TRUE; @@ -2565,10 +2548,6 @@ gksu_sudo_fuller (GksuContext *context, cmd[argcount] = g_strdup("-S"); argcount++; - /* Make sudo noninteractive (we should already have a token) */ - cmd[argcount] = g_strdup("-n"); - argcount++; - /* Make sudo use next arg as prompt */ cmd[argcount] = g_strdup("-p"); argcount++; @@ -2647,21 +2626,26 @@ gksu_sudo_fuller (GksuContext *context, fprintf (stderr, "cmd[%d]: %s\n", i, cmd[i]); } - pid = forkpty(&fdpty, NULL, NULL, NULL); - if (pid == 0) + if ((pipe(parent_pipe)) == -1) { - // Child - setsid(); // make us session leader - - execv(verifycmd[0], verifycmd); + g_set_error (error, gksu_quark, GKSU_ERROR_PIPE, + _("Error creating pipe: %s"), + strerror(errno)); + sudo_reset_xauth (context, xauth, xauth_env); + return FALSE; + } - g_set_error (error, gksu_quark, GKSU_ERROR_EXEC, - _("Failed to exec new process: %s"), + if ((pipe(child_pipe)) == -1) + { + g_set_error (error, gksu_quark, GKSU_ERROR_PIPE, + _("Error creating pipe: %s"), strerror(errno)); sudo_reset_xauth (context, xauth, xauth_env); return FALSE; } - else if (pid == -1) + + pid = fork(); + if (pid == -1) { g_set_error (error, gksu_quark, GKSU_ERROR_FORK, _("Failed to fork new process: %s"), @@ -2669,26 +2653,56 @@ gksu_sudo_fuller (GksuContext *context, sudo_reset_xauth (context, xauth, xauth_env); return FALSE; } + else if (pid == 0) + { + // Child + setsid(); // make us session leader + close(child_pipe[1]); + dup2(child_pipe[0], STDIN_FILENO); + dup2(parent_pipe[1], STDERR_FILENO); + execv(cmd[0], cmd); + + g_set_error (error, gksu_quark, GKSU_ERROR_EXEC, + _("Failed to exec new process: %s"), + strerror(errno)); + sudo_reset_xauth (context, xauth, xauth_env); + return FALSE; + } else { gint counter = 0; gchar *cmdline = NULL; - struct termios tio; // Parent - fdfile = fdopen(fdpty, "w+"); + close(parent_pipe[1]); - /* make sure we notice that ECHO is turned off, if it gets - turned off */ - tcgetattr (fdpty, &tio); - for (counter = 0; (tio.c_lflag & ECHO) && counter < 15; counter++) - { - usleep (1000); - tcgetattr (fdpty, &tio); - } + infile = fdopen(parent_pipe[0], "r"); + if (!infile) + { + g_set_error (error, gksu_quark, GKSU_ERROR_PIPE, + _("Error opening pipe: %s"), + strerror(errno)); + sudo_reset_xauth (context, xauth, xauth_env); + return FALSE; + } - fcntl (fdpty, F_SETFL, O_NONBLOCK); + outfile = fdopen(child_pipe[1], "w"); + if (!outfile) + { + g_set_error (error, gksu_quark, GKSU_ERROR_PIPE, + _("Error opening pipe: %s"), + strerror(errno)); + sudo_reset_xauth (context, xauth, xauth_env); + return FALSE; + } + + /* + we are expecting to receive a GNOME_SUDO_PASS + if we don't there are two possibilities: an error + or a password is not needed + */ + fcntl (parent_pipe[0], F_SETFL, O_NONBLOCK); { /* no matter if we can read, since we're using O_NONBLOCK; this is just to avoid the prompt @@ -2697,11 +2711,11 @@ gksu_sudo_fuller (GksuContext *context, struct timeval tv; FD_ZERO(&rfds); - FD_SET(fdpty, &rfds); + FD_SET(parent_pipe[0], &rfds); tv.tv_sec = 1; tv.tv_usec = 0; - select (fdpty + 1, &rfds, NULL, NULL, &tv); + select (parent_pipe[0] + 1, &rfds, NULL, NULL, &tv); } /* Try hard to find the prompt; it may happen that we're @@ -2713,7 +2727,7 @@ gksu_sudo_fuller (GksuContext *context, if (strncmp (buffer, "GNOME_SUDO_PASS", 15) == 0) break; - read_line (fdpty, buffer, 256); + read_line (parent_pipe[0], buffer, 256); if (context->debug) fprintf (stderr, "buffer: -%s-\n", buffer); @@ -2747,17 +2761,18 @@ gksu_sudo_fuller (GksuContext *context, usleep (1000); - write (fdpty, password, strlen(password) + 1); - write (fdpty, "\n", 1); + fprintf (outfile, "%s\n", password); + fclose (outfile); nullify_password (password); - fcntl(fdpty, F_SETFL, fcntl(fdpty, F_GETFL) & ~O_NONBLOCK); + /* turn NONBLOCK off */ + fcntl(parent_pipe[0], F_SETFL, fcntl(parent_pipe[0], F_GETFL) & ~O_NONBLOCK); /* ignore the first newline that comes right after sudo receives the password */ - fgets (buffer, 255, fdfile); - /* this is the status we are interested in */ - fgets (buffer, 255, fdfile); + fgets (buffer, 255, infile); + /* this is the status we are interessted in */ + fgets (buffer, 255, infile); } else { @@ -2766,7 +2781,7 @@ gksu_sudo_fuller (GksuContext *context, fprintf (stderr, "No password prompt found; we'll assume we don't need a password.\n"); /* turn NONBLOCK off, also if have no prompt */ - fcntl(fdpty, F_SETFL, fcntl(fdpty, F_GETFL) & ~O_NONBLOCK); + fcntl(parent_pipe[0], F_SETFL, fcntl(parent_pipe[0], F_GETFL) & ~O_NONBLOCK); should_display = gconf_client_get_bool (context->gconf_client, BASE_PATH "display-no-pass-info", NULL); @@ -2785,9 +2800,14 @@ gksu_sudo_fuller (GksuContext *context, fprintf (stderr, "%s", buffer); } - if (g_str_has_prefix (buffer, "Sorry, try again.")) + if (!strcmp (buffer, "Sorry, try again.\n")) g_set_error (error, gksu_quark, GKSU_ERROR_WRONGPASS, _("Wrong password.")); + else if (!strncmp (buffer, "Sorry, user ", 12)) + g_set_error (error, gksu_quark, GKSU_ERROR_NOT_ALLOWED, + _("The underlying authorization mechanism (sudo) " + "does not allow you to run this program. Contact " + "the system administrator.")); else { gchar *haystack = buffer; @@ -2805,10 +2825,6 @@ gksu_sudo_fuller (GksuContext *context, } } - /* If we have an error, let's just stop sudo right there. */ - if (error) - close(fdpty); - cmdline = g_strdup("sudo"); /* wait for the child process to end or become something other than sudo */ @@ -2825,23 +2841,17 @@ gksu_sudo_fuller (GksuContext *context, if (context->sn_context) gksu_context_launch_complete (context); + while (read (parent_pipe[0], buffer, 255) > 0) + { + fprintf (stderr, "%s", buffer); + bzero(buffer, 256); + } + /* if the process is still active waitpid() on it */ if (pid_exited != pid) waitpid(pid, &status, 0); sudo_reset_xauth (context, xauth, xauth_env); - /* - * Did token acquisition succeed? If so, spawn sudo in - * non-interactive mode. It should either succeed or die - * immediately if you're not allowed to run the command. - */ - if (WEXITSTATUS(status) == 0) - { - g_spawn_sync(NULL, cmd, NULL, 0, NULL, NULL, - NULL, &child_stderr, &status, - error); - } - if (exit_status) { if (WIFEXITED(status)) { @@ -2853,13 +2863,6 @@ gksu_sudo_fuller (GksuContext *context, if (WEXITSTATUS(status)) { - if (g_str_has_prefix(child_stderr, "Sorry, user ")) - { - g_set_error (error, gksu_quark, GKSU_ERROR_NOT_ALLOWED, - _("The underlying authorization mechanism (sudo) " - "does not allow you to run this program. Contact " - "the system administrator.")); - } if(cmdline) { /* sudo already exec()ed something else, don't report @@ -2868,7 +2871,6 @@ gksu_sudo_fuller (GksuContext *context, if (!g_str_has_suffix (cmdline, "sudo")) { g_free (cmdline); - g_free (child_stderr); return FALSE; } g_free (cmdline); @@ -2881,11 +2883,11 @@ gksu_sudo_fuller (GksuContext *context, } } - fprintf(stderr, child_stderr); - g_free(child_stderr); - /* if error is set we have found an error condition */ - return (error == NULL); + if (error) + return FALSE; + + return TRUE; } /**