ultimatepp/bazaar/SysExec/gksudo.cpp
koldo 306220b3b6 SysExec: Added compatibility with MinGW
git-svn-id: svn://ultimatepp.org/upp/trunk@4008 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2011-10-13 19:53:04 +00:00

475 lines
10 KiB
C++

gboolean
gksu_sudo_fuller (GksuContext *context,
GksuAskPassFunc ask_pass,
gpointer ask_pass_data,
GksuPassNotNeededFunc pass_not_needed,
gpointer pass_not_needed_data,
gint8 *exit_status,
GError **error)
{
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;
GQuark gksu_quark;
gchar *xauth = NULL,
*xauth_env = NULL;
pid_t pid;
int status;
FILE *fdfile = NULL;
int fdpty = -1;
context->sudo_mode = TRUE;
gksu_quark = g_quark_from_string (PACKAGE);
if (!context->command)
{
g_set_error (error, gksu_quark, GKSU_ERROR_NOCOMMAND,
_("gksu_sudo_run needs a command to be run, "
"none was provided."));
return FALSE;
}
if (!context->user)
context->user = g_strdup ("root");
if (ask_pass == NULL)
{
if (context->debug)
fprintf (stderr, "No ask_pass set, using default!\n");
ask_pass = su_ask_password;
}
if (pass_not_needed == NULL)
{
pass_not_needed = no_pass;
}
if (context->always_ask_password)
{
gint exit_status;
g_spawn_command_line_sync("/usr/bin/sudo -K", NULL, NULL, &exit_status, NULL);
}
/*
FIXME: need to set GError in a more detailed way
*/
if (!sudo_prepare_xauth (context))
{
g_set_error (error, gksu_quark, GKSU_ERROR_XAUTH,
_("Unable to copy the user's Xauthorization file."));
return FALSE;
}
/* sets XAUTHORITY */
xauth = g_strdup_printf ("%s/.Xauthority", context->dir);
xauth_env = getenv ("XAUTHORITY");
setenv ("XAUTHORITY", xauth, TRUE);
if (context->debug)
fprintf (stderr, "xauth: %s\n", xauth);
/* set startup id */
if (context->sn_context)
gksu_context_launch_initiate (context);
cmd = g_new (gchar *, argcount + 1);
argcount = 0;
/* sudo binary */
cmd[argcount] = g_strdup("/usr/bin/sudo");
argcount++;
if (!context->keep_env)
{
/* Make sudo set $HOME */
cmd[argcount] = g_strdup("-H");
argcount++;
}
/* Make sudo read from stdin */
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++;
/* prompt */
cmd[argcount] = g_strdup("GNOME_SUDO_PASS");
argcount++;
/* Make sudo use the selected user */
cmd[argcount] = g_strdup("-u");
argcount++;
/* user */
cmd[argcount] = g_strdup(context->user);
argcount++;
/* sudo does not understand this if we do not use -H
weird.
*/
if (!context->keep_env)
{
/* Make sudo stop processing options */
cmd[argcount] = g_strdup("--");
argcount++;
}
{
gchar *tmp_arg = g_malloc (sizeof(gchar) * 1);
gboolean inside_quotes = FALSE;
tmp_arg[0] = '\0';
for (i = j = 0; ; i++)
{
if ((context->command[i] == '\'') && (context->command[i-1] != '\\'))
{
i = i + 1;
inside_quotes = !inside_quotes;
}
if ((context->command[i] == ' ' && inside_quotes == FALSE)
|| context->command[i] == '\0')
{
tmp_arg = g_realloc (tmp_arg, sizeof(gchar) * (j + 1));
tmp_arg[j] = '\0';
cmd = g_realloc (cmd, sizeof(gchar*) * (argcount + 1));
cmd[argcount] = g_strdup (tmp_arg);
g_free (tmp_arg);
argcount = argcount + 1;
j = 0;
if (context->command[i] == '\0')
break;
tmp_arg = g_malloc (sizeof(gchar) * 1);
tmp_arg[0] = '\0';
}
else
{
if (context->command[i] == '\\' && context->command[i+1] != '\\')
i = i + 1;
tmp_arg = g_realloc (tmp_arg, sizeof(gchar) * (j + 1));
tmp_arg[j] = context->command[i];
j = j + 1;
}
}
}
cmd = g_realloc (cmd, sizeof(gchar*) * (argcount + 1));
cmd[argcount] = NULL;
if (context->debug)
{
for (i = 0; cmd[i] != NULL; i++)
fprintf (stderr, "cmd[%d]: %s\n", i, cmd[i]);
}
pid = forkpty(&fdpty, NULL, NULL, NULL);
if (pid == 0)
{
// Child
setsid(); // make us session leader
execv(verifycmd[0], verifycmd);
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
if (pid == -1)
{
g_set_error (error, gksu_quark, GKSU_ERROR_FORK,
_("Failed to fork 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+");
/* 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);
}
fcntl (fdpty, F_SETFL, O_NONBLOCK);
{ /* no matter if we can read, since we're using
O_NONBLOCK; this is just to avoid the prompt
showing up after the read */
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
FD_SET(fdpty, &rfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
select (fdpty + 1, &rfds, NULL, NULL, &tv);
}
/* Try hard to find the prompt; it may happen that we're
* seeing sudo's lecture, or that some pam module is spitting
* debugging stuff at the screen
*/
for (counter = 0; counter < 50; counter++)
{
if (strncmp (buffer, "GNOME_SUDO_PASS", 15) == 0)
break;
read_line (fdpty, buffer, 256);
if (context->debug)
fprintf (stderr, "buffer: -%s-\n", buffer);
usleep(1000);
}
if (context->debug)
fprintf (stderr, "brute force GNOME_SUDO_PASS ended...\n");
if (strncmp(buffer, "GNOME_SUDO_PASS", 15) == 0)
{
gchar *password = NULL;
gboolean prompt_grab;
if (context->debug)
fprintf (stderr, "Yeah, we're in...\n");
prompt_grab = gconf_client_get_bool (context->gconf_client, BASE_PATH "prompt",
NULL);
if (prompt_grab)
gksu_prompt_grab (context);
password = ask_pass (context, _("Password: "),
ask_pass_data, error);
if (password == NULL || (*error))
{
nullify_password (password);
return FALSE;
}
usleep (1000);
write (fdpty, password, strlen(password) + 1);
write (fdpty, "\n", 1);
nullify_password (password);
fcntl(fdpty, F_SETFL, fcntl(fdpty, 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);
}
else
{
gboolean should_display;
if (context->debug)
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);
should_display = gconf_client_get_bool (context->gconf_client,
BASE_PATH "display-no-pass-info", NULL);
/* configuration tells us to show this message */
if (should_display)
{
if (context->debug)
fprintf (stderr, "Calling pass_not_needed window...\n");
pass_not_needed (context, pass_not_needed_data);
/* make sure it is displayed */
while (gtk_events_pending ())
gtk_main_iteration ();
}
fprintf (stderr, "%s", buffer);
}
if (g_str_has_prefix (buffer, "Sorry, try again."))
g_set_error (error, gksu_quark, GKSU_ERROR_WRONGPASS,
_("Wrong password."));
else
{
gchar *haystack = buffer;
gchar *needle;
needle = g_strstr_len (haystack, strlen (haystack), " ");
if (needle && (needle + 1))
{
needle += 1;
if (!strncmp (needle, "is not in", 9))
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 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 */
pid_t pid_exited;
while ((!(pid_exited = waitpid (pid, &status, WNOHANG))) &&
(g_str_has_suffix(cmdline, "sudo")))
{
if (cmdline)
g_free (cmdline);
cmdline = get_process_name (pid);
usleep(100000);
}
if (context->sn_context)
gksu_context_launch_complete (context);
/* 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))
{
*exit_status = WEXITSTATUS(status);
}
else
if (WIFSIGNALED(status))
{
*exit_status = -1;
}
}
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
* exit status errors in that case
*/
if (!g_str_has_suffix (cmdline, "sudo"))
{
g_free (cmdline);
g_free (child_stderr);
return FALSE;
}
g_free (cmdline);
}
if (error == NULL)
g_set_error (error, gksu_quark,
GKSU_ERROR_CHILDFAILED,
_("sudo terminated with %d status"),
WEXITSTATUS(status));
}
}
fprintf(stderr, child_stderr);
g_free(child_stderr);
/* if error is set we have found an error condition */
return (error == NULL);
}