mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
475 lines
10 KiB
C++
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);
|
|
}
|