开始
/*
* Gksu -- a library providing access to su functionality
* Copyright (C) 2004 Gustavo Noronha Silva
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <pty.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <errno.h>
#include <glibtop.h>
#include <glibtop/procstate.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#define SN_API_NOT_YET_FROZEN
#include <libsn/sn.h>
#include <gtk/gtk.h>
#include <locale.h>
#include <gconf/gconf-client.h>
#include <gnome-keyring.h>
#include "defines.h"
#include "../config.h"
#include "libgksu.h"
#include "../libgksuui/gksuui-dialog.h"
static void
gksu_context_launch_complete (GksuContext *context);
GType
gksu_error_get_type (void)
{
static GType etype = 0;
if (etype == 0) {
static const GEnumValue values[] = {
{ GKSU_ERROR_HELPER, "GKSU_ERROR_HELPER", "helper" },
{ GKSU_ERROR_NOCOMMAND, "GKSU_ERROR_NOCOMMAND", "nocommand" },
{ GKSU_ERROR_NOPASSWORD, "GKSU_ERROR_NOPASSWORD", "nopassword" },
{ GKSU_ERROR_FORK, "GKSU_ERROR_FORK", "fork" },
{ GKSU_ERROR_EXEC, "GKSU_ERROR_EXEC", "exec" },
{ GKSU_ERROR_PIPE, "GKSU_ERROR_PIPE", "pipe" },
{ GKSU_ERROR_PIPEREAD, "GKSU_ERROR_PIPEREAD", "piperead" },
{ GKSU_ERROR_WRONGPASS, "GKSU_ERROR_WRONGPASS", "wrongpass" },
{ GKSU_ERROR_CHILDFAILED, "GKSU_ERROR_CHILDFAILED", "childfailed" },
{ GKSU_ERROR_CANCELED, "GKSU_ERROR_CANCELED", "canceled" },
{ GKSU_ERROR_WRONGAUTOPASS, "GKSU_ERROR_WRONGAUTOPASS", "wrongautopass" },
{ 0, NULL, NULL }
};
etype = g_enum_register_static ("GksuError", values);
}
return etype;
}
static pid_t
test_lock(const char* fname)
{
int FD = open(fname, 0);
if(FD < 0) {
if(errno == ENOENT) {
// File does not exist
return 0;
} else {
perror("open");
return(-1);
}
}
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
if (fcntl(FD, F_GETLK, &fl) < 0) {
g_critical("fcntl error");
close(FD);
return(-1);
}
close(FD);
// lock is available
if(fl.l_type == F_UNLCK)
return(0);
// file is locked by another process
return (fl.l_pid);
}
static int
get_lock(const char *File)
{
int FD = open(File,O_RDWR | O_CREAT | O_TRUNC,0640);
if (FD < 0)
{
// Read only .. cant have locking problems there.
if (errno == EROFS)
{
g_warning(_("Not using locking for read only lock file %s"),File);
return dup(0); // Need something for the caller to close
}
// Feh.. We do this to distinguish the lock vs open case..
errno = EPERM;
return -1;
}
fcntl(FD,F_SETFD, FD_CLOEXEC);
// Aquire a write lock
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
if (fcntl(FD,F_SETLK,&fl) == -1)
{
if (errno == ENOLCK)
{
g_warning(_("Not using locking for nfs mounted lock file %s"), File);
unlink(File);
close(FD);
return dup(0); // Need something for the caller to close
}
int Tmp = errno;
close(FD);
errno = Tmp;
return -1;
}
return FD;
}
/*
* code 'stolen' from gnome-session's logout.c
*
* Written by Owen Taylor <otaylor@redhat.com>
* Copyright (C) Red Hat
*/
typedef struct {
GdkScreen *screen;
int monitor;
GdkRectangle area;
int rowstride;
GdkWindow *root_window;
GdkWindow *draw_window;
GdkPixbuf *start_pb, *end_pb, *frame;
guchar *start_p, *end_p, *frame_p;
GTimeVal start_time;
GdkGC *gc;
} FadeoutData;
FadeoutData *fade_data = NULL;
static GList *fadeout_windows = NULL;
#define FADE_DURATION 500.0
int
gsm_screen_get_width (GdkScreen *screen,
int monitor)
{
GdkRectangle geometry;
gdk_screen_get_monitor_geometry (screen, monitor, &geometry);
return geometry.width;
}
int
gsm_screen_get_height (GdkScreen *screen,
int monitor)
{
GdkRectangle geometry;
gdk_screen_get_monitor_geometry (screen, monitor, &geometry);
return geometry.height;
}
int
gsm_screen_get_x (GdkScreen *screen,
int monitor)
{
GdkRectangle geometry;
gdk_screen_get_monitor_geometry (screen, monitor, &geometry);
return geometry.x;
}
int
gsm_screen_get_y (GdkScreen *screen,
int monitor)
{
GdkRectangle geometry;
gdk_screen_get_monitor_geometry (screen, monitor, &geometry);
return geometry.y;
}
static void
get_current_frame (FadeoutData *fadeout,
double sat)
{
guchar *sp, *ep, *fp;
int i, j, width, offset;
width = fadeout->area.width * 3;
offset = 0;
for (i = 0; i < fadeout->area.height; i++)
{
sp = fadeout->start_p + offset;
ep = fadeout->end_p + offset;
fp = fadeout->frame_p + offset;
for (j = 0; j < width; j += 3)
{
guchar r = abs (*(sp++) - ep[0]);
guchar g = abs (*(sp++) - ep[1]);
guchar b = abs (*(sp++) - ep[2]);
*(fp++) = *(ep++) + r * sat;
*(fp++) = *(ep++) + g * sat;
*(fp++) = *(ep++) + b * sat;
}
offset += fadeout->rowstride;
}
}
static void
darken_pixbuf (GdkPixbuf *pb)
{
int width, height, rowstride;
int i, j;
guchar *p, *pixels;
width = gdk_pixbuf_get_width (pb) * 3;
height = gdk_pixbuf_get_height (pb);
rowstride = gdk_pixbuf_get_rowstride (pb);
pixels = gdk_pixbuf_get_pixels (pb);
for (i = 0; i < height; i++)
{
p = pixels + (i * rowstride);
for (j = 0; j < width; j++)
p [j] >>= 1;
}
}
static gboolean
fadeout_callback (FadeoutData *fadeout)
{
GTimeVal current_time;
double elapsed, percent;
g_get_current_time (¤t_time);
elapsed = ((((double)current_time.tv_sec - fadeout->start_time.tv_sec) * G_USEC_PER_SEC +
(current_time.tv_usec - fadeout->start_time.tv_usec))) / 1000.0;
if (elapsed < 0)
{
g_warning ("System clock seemed to go backwards?");
elapsed = G_MAXDOUBLE;
}
if (elapsed > FADE_DURATION)
{
gdk_draw_pixbuf (fadeout->draw_window,
fadeout->gc,
fadeout->end_pb,
0, 0,
0, 0,
fadeout->area.width,
fadeout->area.height,
GDK_RGB_DITHER_NONE,
0, 0);
return FALSE;
}
percent = elapsed / FADE_DURATION;
get_current_frame (fadeout, 1.0 - percent);
gdk_draw_pixbuf (fadeout->draw_window,
fadeout->gc,
fadeout->frame,
0, 0,
0, 0,
fadeout->area.width,
fadeout->area.height,
GDK_RGB_DITHER_NONE,
0, 0);
gdk_flush ();
return TRUE;
}
static void
hide_fadeout_windows (void)
{
GList *l;
for (l = fadeout_windows; l; l = l->next)
{
gdk_window_hide (GDK_WINDOW (l->data));
g_object_unref (l->data);
}
g_list_free (fadeout_windows);
fadeout_windows = NULL;
}
static gboolean
fadein_callback (FadeoutData *fadeout)
{
GTimeVal current_time;
double elapsed, percent;
g_get_current_time (¤t_time);
elapsed = ((((double)current_time.tv_sec - fadeout->start_time.tv_sec) * G_USEC_PER_SEC +
(current_time.tv_usec - fadeout->start_time.tv_usec))) / 1000.0;
if (elapsed < 0)
{
g_warning ("System clock seemed to go backwards?");
elapsed = G_MAXDOUBLE;
}
if (elapsed > FADE_DURATION)
{
gdk_draw_pixbuf (fadeout->draw_window,
fadeout->gc,
fadeout->end_pb,
0, 0,
0, 0,
fadeout->area.width,
fadeout->area.height,
GDK_RGB_DITHER_NONE,
0, 0);
g_object_unref (fadeout->gc);
g_object_unref (fadeout->start_pb);
g_object_unref (fadeout->end_pb);
g_object_unref (fadeout->frame);
g_free (fadeout);
hide_fadeout_windows ();
return FALSE;
}
percent = elapsed / FADE_DURATION;
get_current_frame (fadeout, percent);
gdk_draw_pixbuf (fadeout->draw_window,
fadeout->gc,
fadeout->frame,
0, 0,
0, 0,
fadeout->area.width,
fadeout->area.height,
GDK_RGB_DITHER_NONE,
0, 0);
gdk_flush ();
return TRUE;
}
static void
fadeout_screen (GdkScreen *screen,
int monitor)
{
GdkWindowAttr attr;
int attr_mask;
GdkGCValues values;
FadeoutData *fadeout;
fadeout = g_new (FadeoutData, 1);
fadeout->screen = screen;
fadeout->monitor = monitor;
fadeout->area.x = gsm_screen_get_x (screen, monitor);
fadeout->area.y = gsm_screen_get_y (screen, monitor);
fadeout->area.width = gsm_screen_get_width (screen, monitor);
fadeout->area.height = gsm_screen_get_height (screen, monitor);
fadeout->root_window = gdk_screen_get_root_window (screen);
attr.window_type = GDK_WINDOW_CHILD;
attr.x = fadeout->area.x;
attr.y = fadeout->area.y;
attr.width = fadeout->area.width;
attr.height = fadeout->area.height;
attr.wclass = GDK_INPUT_OUTPUT;
attr.visual = gdk_screen_get_system_visual (fadeout->screen);
attr.colormap = gdk_screen_get_default_colormap (fadeout->screen);
attr.override_redirect = TRUE;
attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_NOREDIR;
fadeout->draw_window = gdk_window_new (fadeout->root_window, &attr, attr_mask);
fadeout_windows = g_list_prepend (fadeout_windows, fadeout->draw_window);
fadeout->start_pb = gdk_pixbuf_get_from_drawable (NULL,
fadeout->root_window,
NULL,
fadeout->area.x,
fadeout->area.y,
0, 0,
fadeout->area.width,
fadeout->area.height);
fadeout->end_pb = gdk_pixbuf_copy (fadeout->start_pb);
darken_pixbuf (fadeout->end_pb);
fadeout->frame = gdk_pixbuf_copy (fadeout->start_pb);
fadeout->rowstride = gdk_pixbuf_get_rowstride (fadeout->start_pb);
fadeout->start_p = gdk_pixbuf_get_pixels (fadeout->start_pb);
fadeout->end_p = gdk_pixbuf_get_pixels (fadeout->end_pb);
fadeout->frame_p = gdk_pixbuf_get_pixels (fadeout->frame);
values.subwindow_mode = GDK_INCLUDE_INFERIORS;
fadeout->gc = gdk_gc_new_with_values (fadeout->root_window, &values, GDK_GC_SUBWINDOW);
gdk_window_set_back_pixmap (fadeout->draw_window, NULL, FALSE);
gdk_window_show (fadeout->draw_window);
gdk_draw_pixbuf (fadeout->draw_window,
fadeout->gc,
fadeout->frame,
0, 0,
0, 0,
fadeout->area.width,
fadeout->area.height,
GDK_RGB_DITHER_NONE,
0, 0);
g_get_current_time (&fadeout->start_time);
g_idle_add ((GSourceFunc) fadeout_callback, fadeout);
fade_data = fadeout;
}
/* End of 'stolen' code */
#define GRAB_TRIES 16
#define GRAB_WAIT 250 /* milliseconds */
typedef enum
{
FAILED_GRAB_MOUSE,
FAILED_GRAB_KEYBOARD
} FailedGrabWhat;
void
report_failed_grab (FailedGrabWhat what)
{
GtkWidget *dialog;
dialog = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
"message-type", GTK_MESSAGE_WARNING,
"buttons", GTK_BUTTONS_CLOSE,
NULL);
switch (what)
{
case FAILED_GRAB_MOUSE:
gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG(dialog),
_("<b><big>Could not grab your mouse.</big></b>"
"/n/n"
"A malicious client may be eavesdropping "
"on your session or you may have just clicked "
"a menu or some application just decided to get "
"focus."
"/n/n"
"Try again."));
break;
case FAILED_GRAB_KEYBOARD:
gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG(dialog),
_("<b><big>Could not grab your keyboard.</big></b>"
"/n/n"
"A malicious client may be eavesdropping "
"on your session or you may have just clicked "
"a menu or some application just decided to get "
"focus."
"/n/n"
"Try again."));
break;
}
gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
gtk_dialog_run (GTK_DIALOG(dialog));
gtk_widget_destroy (dialog);
while (gtk_events_pending ())
gtk_main_iteration ();
}
int
grab_keyboard_and_mouse (GtkWidget *dialog)
{
GdkGrabStatus status;
gint grab_tries = 0;
gint lock = -1;
gchar *fname = g_strdup_printf ("%s/.gksu.lock", getenv ("HOME"));
pid_t pid = test_lock (fname);
if (pid != 0)
{
g_warning ("Lock taken by pid: %i. Exiting.", pid);
exit (0);
}
lock = get_lock(fname);
if( lock < 0)
g_warning ("Unable to create lock file.");
g_free (fname);
fadeout_screen (gdk_screen_get_default (), 0);
gtk_widget_show_all (dialog);
/* reset cursor */
gdk_window_set_cursor(dialog->window, gdk_cursor_new(GDK_LEFT_PTR));
for(;;)
{
status = gdk_pointer_grab ((GTK_WIDGET(dialog))->window, TRUE, 0, NULL,
NULL, GDK_CURRENT_TIME);
if (status == GDK_GRAB_SUCCESS)
break;
usleep (GRAB_WAIT * 1000);
if (++grab_tries > GRAB_TRIES)
{
gtk_widget_hide (dialog);
g_get_current_time (&fade_data->start_time);
while (fadein_callback (fade_data) != FALSE);
report_failed_grab (FAILED_GRAB_MOUSE);
exit (1);
break;
}
}
for(;;)
{
status = gdk_keyboard_grab ((GTK_WIDGET(dialog))->window,
FALSE, GDK_CURRENT_TIME);
if (status == GDK_GRAB_SUCCESS)
break;
usleep(GRAB_WAIT * 1000);
if (++grab_tries > GRAB_TRIES)
{
gtk_widget_hide (dialog);
g_get_current_time (&fade_data->start_time);
while (fadein_callback (fade_data) != FALSE);
report_failed_grab (FAILED_GRAB_KEYBOARD);
exit (1);
break;
}
}
/* we "raise" the window because there is a race here for
* focus-follow-mouse and auto-raise WMs that may put the window
* in the background and confuse users
*/
gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
while (gtk_events_pending ())
gtk_main_iteration ();
return lock;
}
void
ungrab_keyboard_and_mouse (int lock)
{
/* Ungrab */
gdk_pointer_ungrab(GDK_CURRENT_TIME);
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
gdk_flush();
g_get_current_time (&fade_data->start_time);
while (fadein_callback (fade_data) != FALSE);
close(lock);
}
static gchar*
get_gnome_keyring_password (GksuContext *context)
{
GnomeKeyringAttributeList *attributes;
GnomeKeyringAttribute attribute;
GnomeKeyringResult result;
GList *list;
attributes = gnome_keyring_attribute_list_new ();
attribute.name = g_strdup ("user");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup (gksu_context_get_user (context));
g_array_append_val (attributes, attribute);
attribute.name = g_strdup ("type");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup ("local");
g_array_append_val (attributes, attribute);
attribute.name = g_strdup ("creator");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup ("gksu");
g_array_append_val (attributes, attribute);
list = g_list_alloc();
result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
attributes,
&list);
gnome_keyring_attribute_list_free (attributes);
if (
(result == GNOME_KEYRING_RESULT_OK) &&
(g_list_length(list) >= 1)
)
{
GnomeKeyringFound *found = list->data;
gint password_length = strlen (found->secret);
gchar *password;
password = g_locale_from_utf8 (found->secret,
password_length,
NULL, NULL, NULL);
password_length = strlen (password);
if (password[password_length-1] == '/n')
password[password_length-1] = '/0';
return password;
}
return NULL;
}
static void
keyring_create_item_cb (GnomeKeyringResult result,
guint32 id, gpointer keyring_loop)
{
g_main_loop_quit (keyring_loop);
}
static void
set_gnome_keyring_password (GksuContext *context, gchar *password)
{
GConfClient *gconf_client;
gboolean save_to_keyring;
gconf_client = context->gconf_client;
save_to_keyring = gconf_client_get_bool (gconf_client, BASE_PATH"save-to-keyring", NULL);
if (password && save_to_keyring)
{
static GMainLoop *keyring_loop = NULL;
GnomeKeyringAttributeList *attributes;
GnomeKeyringAttribute attribute;
GnomeKeyringResult result;
gchar *keyring_name;
gchar *key_name;
attributes = gnome_keyring_attribute_list_new ();
attribute.name = g_strdup ("user");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup (gksu_context_get_user (context));
g_array_append_val (attributes, attribute);
attribute.name = g_strdup ("type");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup ("local");
g_array_append_val (attributes, attribute);
attribute.name = g_strdup ("creator");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup ("gksu");
g_array_append_val (attributes, attribute);
key_name = g_strdup_printf ("Local password for user %s",
gksu_context_get_user (context));
keyring_loop = g_main_loop_new (NULL, FALSE);
keyring_name = gconf_client_get_string (gconf_client, BASE_PATH"save-keyring", NULL);
if (keyring_name == NULL)
keyring_name = g_strdup ("session");
/* make sure the keyring exists; if an error occurs, use
the session keyring */
result = gnome_keyring_create_sync(keyring_name, NULL);
if ((result != GNOME_KEYRING_RESULT_OK) &&
(result != GNOME_KEYRING_RESULT_ALREADY_EXISTS))
keyring_name = g_strdup ("session");
gnome_keyring_item_create (keyring_name,
GNOME_KEYRING_ITEM_GENERIC_SECRET,
key_name,
attributes,
password,
TRUE,
keyring_create_item_cb,
keyring_loop, NULL);
gnome_keyring_attribute_list_free (attributes);
g_free (keyring_name);
g_main_loop_run (keyring_loop);
}
}
static void
unset_gnome_keyring_password (GksuContext *context)
{
GConfClient *gconf_client;
gboolean save_to_keyring;
GnomeKeyringAttributeList *attributes;
GnomeKeyringAttribute attribute;
GnomeKeyringResult result;
GList *list;
gconf_client = context->gconf_client;
save_to_keyring = gconf_client_get_bool (gconf_client, BASE_PATH"save-to-keyring", NULL);
if (save_to_keyring)
{
attributes = gnome_keyring_attribute_list_new ();
attribute.name = g_strdup ("user");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup (gksu_context_get_user (context));
g_array_append_val (attributes, attribute);
attribute.name = g_strdup ("type");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup ("local");
g_array_append_val (attributes, attribute);
attribute.name = g_strdup ("creator");
attribute.type = GNOME_KEYRING_ATTRIBUTE_TYPE_STRING;
attribute.value.string = g_strdup ("gksu");
g_array_append_val (attributes, attribute);
list = g_list_alloc();
result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_GENERIC_SECRET,
attributes,
&list);
gnome_keyring_attribute_list_free (attributes);
if (
(result == GNOME_KEYRING_RESULT_OK) &&
(g_list_length(list) == 1)
)
{
GnomeKeyringFound *found = list->data;
gnome_keyring_item_delete_sync (found->keyring,
found->item_id);
}
}
}
void
get_configuration_options (GksuContext *context)
{
GConfClient *gconf_client = context->gconf_client;
gboolean force_grab;
context->grab = !gconf_client_get_bool (gconf_client, BASE_PATH "disable-grab",
NULL);
force_grab = gconf_client_get_bool (gconf_client, BASE_PATH "force-grab",
NULL);
if (force_grab)
context->grab = TRUE;
context->sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
NULL);
}
/**
* su_ask_password:
* @context: a #GksuContext
* @prompt: the prompt that should be used instead of "Password: "
* @data: data that is passed by gksu_*_full
* @error: a pointer to pointer #GError that will be filled with
* data if an error happens.
*
* This is a convenience function to create a #GksuuiDialog and
* request the password.
*
* Returns: a newly allocated gchar containing the password
* or NULL if an error happens or the user cancels the action
*/
static gchar*
su_ask_password (GksuContext *context, gchar *prompt,
gpointer data, GError **error)
{
GtkWidget *dialog = NULL;
gchar *msg;
gchar *password = NULL, *tmp = NULL;
int retvalue = 0;
int lock = 0;
GQuark gksu_quark;
gksu_quark = g_quark_from_string (PACKAGE);
if (context->grab)
dialog = g_object_new (GKSUUI_TYPE_DIALOG,
"type", GTK_WINDOW_POPUP,
"sudo-mode", context->sudo_mode,
NULL);
else
dialog = gksuui_dialog_new (context->sudo_mode);
if (prompt)
gksuui_dialog_set_prompt (GKSUUI_DIALOG(dialog), _(prompt));
if (context->message)
gksuui_dialog_set_message (GKSUUI_DIALOG(dialog), context->message);
else
{
gchar *command = NULL;
if (context->description)
command = context->description;
else
command = context->command;
if (context->sudo_mode)
{
if (!strcmp(context->user, "root"))
msg = g_strdup_printf (_("<b><big>Enter your password to perform"
" administrative tasks</big></b>/n/n"
"The application '%s' lets you "
"modify essential parts of your "
"system."),
command);
else
msg = g_strdup_printf (_("<b><big>Enter your password to run "
"the application '%s' as user %s"
"</big></b>"),
command, context->user);
}
else
{
if (strcmp(gksu_context_get_user (context), "root") == 0)
msg = g_strdup_printf (_("<b><big>Enter the administrative password"
"</big></b>/n/n"
"The application '%s' lets you "
"modify essential parts of your "
"system."),
command);
else
msg = g_strdup_printf (_("<b><big>Enter the password of %s to run "
"the application '%s'"
"</big></b>"),
command, context->user);
}
gksuui_dialog_set_message (GKSUUI_DIALOG(dialog), msg);
g_free (msg);
}
if (context->alert)
gksuui_dialog_set_alert (GKSUUI_DIALOG(dialog), context->alert);
if (context->grab)
lock = grab_keyboard_and_mouse (dialog);
retvalue = gtk_dialog_run (GTK_DIALOG(dialog));
gtk_widget_hide (dialog);
if (context->grab)
ungrab_keyboard_and_mouse (lock);
while (gtk_events_pending ())
gtk_main_iteration ();
if (retvalue != GTK_RESPONSE_OK)
{
switch (retvalue)
{
case GTK_RESPONSE_CANCEL:
case GTK_RESPONSE_DELETE_EVENT:
g_set_error (error, gksu_quark,
GKSU_ERROR_CANCELED,
_("Password prompt canceled."));
if (context->sn_context)
gksu_context_launch_complete (context);
}
gtk_widget_destroy (dialog);
while (gtk_events_pending ())
gtk_main_iteration ();
return NULL;
}
tmp = gksuui_dialog_get_password (GKSUUI_DIALOG(dialog));
password = g_locale_from_utf8 (tmp, strlen (tmp), NULL, NULL, NULL);
g_free (tmp);
gtk_widget_destroy (dialog);
while (gtk_events_pending ())
gtk_main_iteration ();
return password;
}
static void
cb_toggled_cb (GtkWidget *button, gpointer data)
{
GConfClient *gconf_client;
gchar *key;
gboolean toggled;
gchar *key_name;
g_return_if_fail (data != NULL);
key_name = (gchar*)data;
gconf_client = gconf_client_get_default ();
toggled = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button));
key = g_strdup_printf (BASE_PATH "%s", key_name);
if (!strcmp (key_name, "display-no-pass-info"))
{
/* the meaning of the key is the exact opposite of the meaning
of the answer - when the check button is checked the key must
be off
*/
gconf_client_set_bool (gconf_client, key, !toggled, NULL);
}
else
gconf_client_set_bool (gconf_client, key, toggled, NULL);
g_object_unref (gconf_client);
g_free (key);
}
void
no_pass (GksuContext *context, gpointer data)
{
GtkWidget *dialog;
GtkWidget *alignment;
GtkWidget *check_button;
gchar *command = NULL;
if (context->description)
command = context->description;
else
command = context->command;
dialog = gtk_message_dialog_new_with_markup (NULL, 0,
GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
_("<b><big>Granted permissions without asking "
"for password</big></b>"
"/n/n"
"The '%s' program was started with "
"the privileges of the %s user without "
"the need to ask for a password, due to "
"your system's authentication mechanism "
"setup."
"/n/n"
"It is possible that you are being allowed "
"to run specific programs as user %s "
"without the need for a password, or that "
"the password is cached."
"/n/n"
"This is not a problem report; it's "
"simply a notification to make sure "
"you are aware of this."),
command,
context->user,
context->user);
alignment = gtk_alignment_new (0.5, 0.5, 0.6, 1);
gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), alignment, TRUE, TRUE, 2);
check_button = gtk_check_button_new_with_mnemonic (_("Do _not display this message again"));
g_signal_connect (G_OBJECT(check_button), "toggled",
G_CALLBACK(cb_toggled_cb), "display-no-pass-info");
gtk_container_add (GTK_CONTAINER(alignment), check_button);
gtk_widget_show_all (dialog);
gtk_dialog_run (GTK_DIALOG(dialog));
gtk_widget_destroy (GTK_WIDGET(dialog));
while (gtk_events_pending ())
gtk_main_iteration ();
}
static void
gksu_prompt_grab (GksuContext *context)
{
GtkWidget *d;
d = gtk_message_dialog_new_with_markup (NULL, 0, GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
_("<b>Would you like your screen to be /"grabbed/"/n"
"while you enter the password?</b>"
"/n/n"
"This means all applications will be paused to avoid/n"
"the eavesdropping of your password by a a malicious/n"
"application while you type it."));
if (gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_NO)
context->grab = FALSE;
else
context->grab = TRUE;
gtk_widget_destroy (d);
}
static void
nullify_password (gchar *pass)
{
if (pass)
{
memset(pass, 0, strlen(pass));
g_free (pass);
}
pass = NULL;
}
static gchar *
get_process_name (pid_t pid)
{
static gboolean init;
glibtop_proc_state buf;
if (!init) {
glibtop_init();
init = TRUE;
}
glibtop_get_proc_state (&buf, pid);
return strdup(buf.cmd);
}
static gchar *
get_xauth_token (GksuContext *context, gchar *display)
{
gchar *xauth_bin = NULL;
FILE *xauth_output;
gchar *tmp = NULL;
gchar *xauth = g_new0 (gchar, 256);
/* find out where the xauth binary is located */
if (g_file_test ("/usr/bin/xauth", G_FILE_TEST_IS_EXECUTABLE))
xauth_bin = "/usr/bin/xauth";
else if (g_file_test ("/usr/X11R6/bin/xauth", G_FILE_TEST_IS_EXECUTABLE))
xauth_bin = "/usr/X11R6/bin/xauth";
else
{
fprintf (stderr,
"Failed to obtain xauth key: xauth binary not found "
"at usual locations");
return NULL;
}
/* get the authorization token */
tmp = g_strdup_printf ("%s list %s | "
"head -1 | awk '{ print $3 }'",
xauth_bin,
display);
if ((xauth_output = popen (tmp, "r")) == NULL)
{
fprintf (stderr,
"Failed to obtain xauth key: %s",
strerror(errno));
return NULL;
}
fread (xauth, sizeof(char), 255, xauth_output);
pclose (xauth_output);
g_free (tmp);
if (context->debug)
{
fprintf(stderr,
"xauth: -%s-/n"
"display: -%s-/n",
xauth, display);
}
return xauth;
}
/**
* prepare_xauth:
*
* Sets up the variables with values for the $DISPLAY
* environment variable and xauth-related stuff. Also
* creates a temporary directory to hold a .Xauthority
*
* Returns: TRUE if it suceeds, FALSE if it fails.
*/
static int
prepare_xauth (GksuContext *context)
{
gchar *display = NULL;
gchar *xauth = NULL;
display = g_strdup (getenv ("DISPLAY"));
xauth = get_xauth_token (context, display);
if (xauth == NULL)
{
g_free (display);
return FALSE;
}
/* If xauth is the empty string, then try striping the
* hostname part of the DISPLAY string for getting the
* auth token; this is needed for ssh-forwarded usage
*/
if (!strcmp ("", xauth))
{
gchar *cut_display = NULL;
g_free (xauth);
cut_display = g_strdup (g_strrstr (display, ":"));
xauth = get_xauth_token (context, cut_display);
g_free (display);
display = cut_display;
}
context->xauth = xauth;
context->display = display;
if (context->debug)
{
fprintf(stderr,
"final xauth: -%s-/n"
"final display: -%s-/n",
context->xauth, context->display);
}
return TRUE;
}
/* Write all of buf, even if write(2) is interrupted. */
static ssize_t
full_write (int d, const char *buf, size_t nbytes)
{
ssize_t r, w = 0;
/* Loop until nbytes of buf have been written. */
while (w < nbytes) {
/* Keep trying until write succeeds without interruption. */
do {
r = write(d, buf + w, nbytes - w);
} while (r < 0 && errno == EINTR);
if (r < 0) {
return -1;
}
w += r;
}
return w;
}
static gboolean
copy (const char *fn, const char *dir)
{
int in, out;
int r;
char *newfn;
char buf[BUFSIZ] = "";
newfn = g_strdup_printf("%s/.Xauthority", dir);
out = open(newfn, O_WRONLY | O_CREAT | O_EXCL, 0600);
if (out == -1)
{
if (errno == EEXIST)
fprintf (stderr,
"Impossible to create the .Xauthority file: a file "
"already exists. This might be a security issue; "
"please investigate.");
else
fprintf (stderr,
"Error copying '%s' to '%s': %s",
fn, dir, strerror(errno));
return FALSE;
}
in = open(fn, O_RDONLY);
if (in == -1)
{
fprintf (stderr,
"Error copying '%s' to '%s': %s",
fn, dir, strerror(errno));
return FALSE;
}
while ((r = read(in, buf, BUFSIZ)) > 0)
{
if (full_write(out, buf, r) == -1)
{
fprintf (stderr,
"Error copying '%s' to '%s': %s",
fn, dir, strerror(errno));
return FALSE;
}
}
if (r == -1)
{
fprintf (stderr,
"Error copying '%s' to '%s': %s",
fn, dir, strerror(errno));
return FALSE;
}
return TRUE;
}
static gboolean
sudo_prepare_xauth (GksuContext *context)
{
gchar template[] = "/tmp/" PACKAGE "-XXXXXX";
gboolean error_copying = FALSE;
gchar *xauth = NULL;
context->dir = g_strdup (mkdtemp(template));
if (!context->dir)
{
fprintf (stderr, strerror(errno));
return FALSE;
}
xauth = g_strdup(g_getenv ("XAUTHORITY"));
if (xauth == NULL)
xauth = g_strdup_printf ("%s/.Xauthority", g_get_home_dir());
error_copying = !copy (xauth, context->dir);
g_free (xauth);
if (error_copying)
return FALSE;
return TRUE;
}
static void
sudo_reset_xauth (GksuContext *context, gchar *xauth,
gchar *xauth_env)
{
/* reset the env var as it was before or clean it */
if (xauth_env)
setenv ("XAUTHORITY", xauth_env, TRUE);
else
unsetenv ("XAUTHORITY");
if (context->debug)
fprintf (stderr, "xauth: %s/nxauth_env: %s/ndir: %s/n",
xauth, xauth_env, context->dir);
unlink (xauth);
rmdir (context->dir);
g_free (xauth);
}
static void
startup_notification_initialize (GksuContext *context)
{
SnDisplay *sn_display;
sn_display = sn_display_new (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
NULL, NULL);
context->sn_context = sn_launcher_context_new (sn_display, gdk_screen_get_number (gdk_display_get_default_screen (gdk_display_get_default ())));
sn_launcher_context_set_description (context->sn_context, _("Granting Rights"));
sn_launcher_context_set_name (context->sn_context, g_get_prgname ());
}
/**
* gksu_context_new
*
* This function should be used when creating a new #GksuContext to
* pass to gksu_su_full or gksu_sudo_full. The #GksuContext must be
* freed with gksu_context_free.
*
* Returns: a newly allocated #GksuContext
*/
GksuContext*
gksu_context_new ()
{
GksuContext *context;
context = g_new (GksuContext, 1);
context->xauth = NULL;
context->dir = NULL;
context->display = NULL;
context->gconf_client = gconf_client_get_default ();
context->sudo_mode = FALSE;
context->user = g_strdup ("root");
context->command = NULL;
context->login_shell = FALSE;
context->keep_env = FALSE;
context->description = NULL;
context->message = NULL;
context->alert = NULL;
context->grab = TRUE;
context->debug = FALSE;
context->sn_context = NULL;
context->sn_id = NULL;
context->ref_count = 1;
get_configuration_options (context);
startup_notification_initialize (context);
return context;
}
/**
* gksu_context_set_user:
* @context: the #GksuContext you want to modify
* @username: the target username
*
* Sets up what user the command will be run as. The default
* is root, but you can run the command as any user.
*
*/
void
gksu_context_set_user (GksuContext *context, gchar *username)
{
g_assert (username != NULL);
if (context->user)
g_free (context->user);
context->user = g_strdup (username);
}
/**
* gksu_context_get_user:
* @context: the #GksuContext from which to grab the information
*
* Gets the user the command will be run as, as set
* by gksu_context_set_user.
*
* Returns: a string with the user or NULL if not set.
*/
const gchar*
gksu_context_get_user (GksuContext *context)
{
return context->user;
}
/**
* gksu_context_set_command:
* @context: the #GksuContext you want to modify
* @command: the command that shall be ran
*
* Sets up what command will run with the target user.
*
*/
void
gksu_context_set_command (GksuContext *context, gchar *command)
{
g_assert (command != NULL);
if (context->command)
g_free (context->command);
context->command = g_strdup (command);
/* startup notification */
sn_launcher_context_set_binary_name (context->sn_context,
command);
}
/**
* gksu_context_get_command:
* @context: the #GksuContext from which to grab the information
*
* Gets the command that will be run, as set by
* gksu_context_set_command.
*
* Returns: a string with the command or NULL if not set.
*/
const gchar*
gksu_context_get_command (GksuContext *context)
{
return context->command;
}
/**
* gksu_context_set_login_shell:
* @context: the #GksuContext you want to modify
* @value: TRUE or FALSE
*
* Should the shell in which the command will be run be
* a login shell?
*/
void
gksu_context_set_login_shell (GksuContext *context, gboolean value)
{
context->login_shell = value;
}
/**
* gksu_context_get_login_shell:
* @context: the #GksuContext from which to grab the information
*
* Finds out if the shell created by the underlying su process
* will be a login shell.
*
* Returns: TRUE if the shell will be a login shell, FALSE otherwise.
*/
gboolean
gksu_context_get_login_shell (GksuContext *context)
{
return context->login_shell;
}
/**
* gksu_context_set_keep_env:
* @context: the #GksuContext you want to modify
* @value: TRUE or FALSE
*
* Should the environment be kept as it is? Defaults to
* TRUE. Notice that setting this to FALSE may cause the
* X authorization stuff to fail.
*/
void
gksu_context_set_keep_env (GksuContext *context, gboolean value)
{
context->keep_env = value;
}
/**
* gksu_context_get_keep_env:
* @context: the #GksuContext from which to grab the information
*
* Finds out if the environment in which the program will be
* run will be reset.
*
* Returns: TRUE if the environment is going to be kept,
* FALSE otherwise.
*/
gboolean
gksu_context_get_keep_env (GksuContext *context)
{
return context->keep_env;
}
/**
* gksu_context_set_description:
* @context: the #GksuContext you want to modify
* @description: a string to set the description for
*
* Set the nice name for the action that is being run that the window
* that asks for the password will have. This is only meant to be
* used if the default window is used, of course.
*/
void
gksu_context_set_description (GksuContext *context, gchar *description)
{
if (context->description)
g_free (context->description);
context->description = g_strdup (description);
}
/**
* gksu_context_get_description:
* @context: the #GksuContext you want to get the description from.
*
* Get the description that the window will have when the
* default function for requesting the password is
* called.
*
* Returns: a string with the description or NULL if not set.
*/
gchar*
gksu_context_get_description (GksuContext *context)
{
return context->description;
}
/**
* gksu_context_set_message:
* @context: the #GksuContext you want to modify
* @message: a string to set the message for
*
* Set the message that the window that asks for the password will have.
* This is only meant to be used if the default window is used, of course.
*/
void
gksu_context_set_message (GksuContext *context, gchar *message)
{
if (context->message)
g_free (context->message);
context->message = g_strdup (message);
}
/**
* gksu_context_get_message:
* @context: the #GksuContext you want to get the message from.
*
* Get the message that the window will have when the
* default function for requesting the password is
* called.
*
* Returns: a string with the message or NULL if not set.
*/
gchar*
gksu_context_get_message (GksuContext *context)
{
return context->message;
}
/**
* gksu_context_set_alert:
* @context: the #GksuContext you want to modify
* @alert: a string to set the alert for
*
* Set the alert that the window that asks for the password will have.
* This is only meant to be used if the default window is used, of course.
* This alert should be used to display messages such as 'incorrect password',
* for instance.
*/
void
gksu_context_set_alert (GksuContext *context, gchar *alert)
{
if (context->alert)
g_free (context->alert);
context->alert = g_strdup (alert);
}
/**
* gksu_context_get_alert:
* @context: the #GksuContext you want to get the alert from.
*
* Get the alert that the window will have when the
* default function for requesting the password is
* called.
*
* Returns: a string with the alert or NULL if not set.
*/
gchar*
gksu_context_get_alert (GksuContext *context)
{
return context->alert;
}
/**
* gksu_context_set_debug:
* @context: the #GksuContext you want to modify
* @value: TRUE or FALSE
*
* Set up if debuging information should be printed.
*/
void
gksu_context_set_grab (GksuContext *context, gboolean value)
{
context->grab = value;
}
/**
* gksu_context_get_grab:
* @context: the #GksuContext you want to ask whether a grab will be done.
*
* Returns TRUE if gksu has been asked to do a grab on keyboard and mouse
* when asking for the password.
*
* Returns: TRUE if yes, FALSE otherwise.
*/
gboolean
gksu_context_get_grab (GksuContext *context)
{
return context->grab;
}
/**
* gksu_context_set_launcher_context:
* @context: the #GksuContext you want to set the sn context in
* @sn_context: the #SnLauncherContext you want to set
*
* Tell libgksu to use the given #SnLauncherContext for startup notification.
* Currently the library will use this to set DESKTOP_STARTUP_ID in the
* environment of the child and to issue initiate and complete events.
* Notice that you don't need to use this function unless you want to
* override gksu's default behavior on startup notification, since the
* library will create its own context.
*
* Returns: the #SnLauncherContext which is set, or NULL if none was set
*/
void
gksu_context_set_launcher_context (GksuContext *context,
SnLauncherContext *sn_context)
{
if (context->sn_context)
sn_launcher_context_unref (context->sn_context);
context->sn_context = sn_context;
}
/**
* gksu_context_get_launcher_context:
* @context: the #GksuContext you want to get the sn context from
*
* Gets the current startup notification launcher context
*
* Returns: the #SnLauncherContext which is set, or NULL if none was set
*/
SnLauncherContext*
gksu_context_get_launcher_context (GksuContext *context)
{
return context->sn_context;
}
/**
* gksu_context_set_launcher_id:
* @context: the #GksuContext you want to set the sn id in
* @sn_context: the sn_id you want to set, as a #gchar
*/
void
gksu_context_set_launcher_id (GksuContext *context,
gchar *sn_id)
{
if (context->sn_id)
g_free (context->sn_id);
context->sn_id = g_strdup(sn_id);
}
/**
* gksu_context_launch_initiate:
* @context: the #GksuContext you want to initiate the launch for
*
* Initiates the launch, as far as Startup Notification is concerned;
* This will only be used internally, usually.
*/
static void
gksu_context_launch_initiate (GksuContext *context)
{
gchar *sid = NULL;
guint32 launch_time = gdk_x11_display_get_user_time ((GdkDisplay*)GDK_DISPLAY());
static gboolean initiated = FALSE;
if (!initiated)
initiated = TRUE;
else
return;
sn_launcher_context_initiate (context->sn_context,
g_get_prgname (),
gksu_context_get_command (context),
launch_time);
sid = g_strdup_printf ("%s", sn_launcher_context_get_startup_id (context->sn_context));
gksu_context_set_launcher_id (context, sid);
if (context->debug)
fprintf (stderr, "STARTUP_ID: %s/n", sid);
setenv ("DESKTOP_STARTUP_ID", sid, TRUE);
g_free(sid);
}
/**
* gksu_context_launch_complete:
* @context: the #GksuContext you want to complete the launch for
*
* Completes the launch, as far as Startup Notification is concerned;
* This will only be used internally, usually.
*/
static void
gksu_context_launch_complete (GksuContext *context)
{
sn_launcher_context_complete(context->sn_context);
}
/**
* gksu_context_set_debug:
* @context: the #GksuContext you want to modify
* @value: TRUE or FALSE
*
* Set up if debuging information should be printed.
*/
void
gksu_context_set_debug (GksuContext *context, gboolean value)
{
context->debug = value;
}
/**
* gksu_context_get_debug:
* @context: the #GksuContext from which to grab the information
*
* Finds out if the library is configured to print debuging
* information.
*
* Returns: TRUE if it is, FALSE otherwise.
*/
gboolean
gksu_context_get_debug (GksuContext *context)
{
return context->debug;
}
/**
* gksu_context_free
* @context: the #GksuContext to be freed.
*
* Frees the given #GksuContext.
*/
void
gksu_context_free (GksuContext *context)
{
g_free (context->xauth);
g_free (context->dir);
g_free (context->display);
g_object_unref (context->gconf_client);
g_free (context->description);
g_free (context->message);
g_free (context->user);
g_free (context->command);
g_free (context);
}
/**
* gksu_context_ref
* @context: A #GksuContext struct.
*
* Increments the reference count of the given #GksuContext.
*/
GksuContext*
gksu_context_ref (GksuContext *context)
{
context->ref_count++;
return context;
}
/**
* gksu_context_unref
* @context: A #GksuContext struct.
*
* Decrements the reference count of the given #GksuContext struct,
* freeing it if the reference count falls to zero.
*/
void
gksu_context_unref (GksuContext *context)
{
if (--context->ref_count == 0)
{
gksu_context_free (context);
}
}
GType
gksu_context_get_type (void)
{
static GType type_gksu_context = 0;
if (!type_gksu_context)
type_gksu_context = g_boxed_type_register_static
("GksuContext",
(GBoxedCopyFunc) gksu_context_ref,
(GBoxedFreeFunc) gksu_context_unref);
return type_gksu_context;
}
/**
* gksu_su_full:
* @context: a #GksuContext
* @ask_pass: a #GksuAskPassFunc to be called when the lib determines
* requesting a password is necessary; it may be NULL, in which case
* the standard password request dialog will be used
* @ask_pass_data: a #gpointer with user data to be passed to the
* #GksuAskPasswordFunc
* @pass_not_needed: a #GksuPassNotNeededFunc that will be called
* when the command is being run without the need for requesting
* a password; it will only be called if the display-no-pass-info
* gconf key is enabled; NULL will have the standard dialog be shown
* @pass_not_needed_data: a #gpointer with the user data to be passed to the
* #GksuPasswordNotNeededFunc
* @error: a #GError object to be filled with the error code or NULL
*
* This could be considered one of the main functions in GKSu.
* it is responsible for doing the 'user changing' magic calling
* the #GksuAskPassFunc function to request a password if needed.
* and the #GksuPassNotNeededFunc function if a password won't be
* needed, so the application has the oportunity of warning the user
* what it's doing.
*
* This function uses su as backend.
*
* Returns: TRUE if all went fine, FALSE if failed
*/
gboolean
gksu_su_full (GksuContext *context,
GksuAskPassFunc ask_pass,
gpointer ask_pass_data,
GksuPassNotNeededFunc pass_not_needed,
gpointer pass_not_needed_data,
GError **error)
{
GQuark gksu_quark;
int i = 0;
gchar auxcommand[] = PREFIX "/lib/" PACKAGE "/gksu-run-helper";
int fdpty;
pid_t pid;
context->sudo_mode = FALSE;
gksu_quark = g_quark_from_string (PACKAGE);
if (!context->command)
{
g_set_error (error, gksu_quark, GKSU_ERROR_NOCOMMAND,
_("gksu_run needs a command to be run, "
"none was provided."));
return FALSE;
}
if (!context->user)
context->user = g_strdup ("root");
if (!g_file_test (auxcommand, G_FILE_TEST_IS_EXECUTABLE))
{
g_set_error (error, gksu_quark, GKSU_ERROR_HELPER,
_("The gksu-run-helper command was not found or "
"is not executable."));
return FALSE;
}
if (!prepare_xauth (context))
{
g_set_error (error, gksu_quark, GKSU_ERROR_XAUTH,
_("Unable to copy the user's Xauthorization file."));
return FALSE;
}
if (context->sn_context)
gksu_context_launch_initiate (context);
pid = forkpty(&fdpty, NULL, NULL, NULL);
if (pid == 0)
{
gchar **cmd = g_malloc (sizeof(gchar*)*7);
setsid(); // make us session leader
cmd[i] = g_strdup ("/bin/su"); i++;
if (context->login_shell)
{
cmd[i] = g_strdup ("-"); i++;
}
cmd[i] = g_strdup (context->user); i++;
if (context->keep_env)
{
cmd[i] = g_strdup ("-p"); i++;
}
cmd[i] = g_strdup ("-c"); i++;
/* needs to get X authorization prior to running the program */
cmd[i] = g_strdup_printf ("%s /"%s/"", auxcommand,
context->command); i++;
cmd[i] = NULL;
/* executes the command */
if (execv (cmd[0], cmd) == -1)
{
fprintf (stderr,
"Unable to run /bin/su: %s",
strerror(errno));
}
for (i = 0 ; cmd[i] != NULL ; i++)
g_free (cmd[i]);
g_free(cmd);
}
else if (pid == -1)
{
g_set_error (error, gksu_quark, GKSU_ERROR_FORK,
_("Failed to fork new process: %s"),
strerror(errno));
return FALSE;
}
else
{
fd_set rfds;
struct timeval tv;
struct passwd *pwd = NULL;
gint target_uid = -1;
gint my_uid = 0;
gchar buf[256] = {0};
gint status;
gchar *password = NULL;
gchar *cmdline = NULL;
gboolean password_needed = FALSE;
gboolean used_gnome_keyring = FALSE;
my_uid = getuid();
pwd = getpwnam (context->user);
if (pwd)
target_uid = pwd->pw_uid;
if (ask_pass == NULL)
{
ask_pass = su_ask_password;
}
if (pass_not_needed == NULL)
{
pass_not_needed = no_pass;
}
/* no need to ask for password if we're already root */
if (my_uid != target_uid && my_uid)
{
gint count;
struct termios tio;
read (fdpty, buf, 255);
if (context->debug)
fprintf (stderr, "gksu_context_run: buf: -%s-/n", buf);
/* make sure we notice that ECHO is turned off, if it gets
turned off */
tcgetattr (fdpty, &tio);
for (count = 0; (tio.c_lflag & ECHO) && count < 15; count++)
{
usleep (1000);
tcgetattr (fdpty, &tio);
}
if (!(tio.c_lflag & ECHO))
{
gboolean prompt_grab;
prompt_grab = gconf_client_get_bool (context->gconf_client, BASE_PATH "prompt",
NULL);
if (prompt_grab)
gksu_prompt_grab (context);
/* try to get the password from the GNOME Keyring first */
password = get_gnome_keyring_password (context);
if (password == NULL)
{
password = ask_pass (context, buf, ask_pass_data, error);
if (context->debug)
{
fprintf (stderr, "no password on keyring/n");
if (password == NULL)
fprintf (stderr, "no password from ask_pass!/n");
}
}
else
{
if (context->debug)
fprintf (stderr, "password from keyring found/n");
used_gnome_keyring = TRUE;
}
if (password == NULL || (error && (*error)))
{
if (context->debug)
fprintf (stderr, "gksu_su_full: problem getting password - getting out/n");
if (context->debug && error)
fprintf (stderr, "error: %s/n", (*error)->message);
nullify_password (password);
return TRUE;
}
write (fdpty, password, strlen(password) + 1);
write (fdpty, "/n", 1);
password_needed = TRUE;
}
}
if (context->debug)
fprintf (stderr, "DEBUG (run:after-pass) buf: -%s-/n", buf);
if (strncmp (buf, "gksu", 4) && strncmp (buf, "su", 2))
{
/* drop the /n echoed on password entry if su did request
a password */
if (password_needed)
read (fdpty, buf, 255);
if (context->debug)
fprintf (stderr, "DEBUG (run:post-after-pass) buf: -%s-/n", buf);
read (fdpty, buf, 255);
if (context->debug)
fprintf (stderr, "DEBUG (run:post-after-pass) buf: -%s-/n", buf);
}
FD_ZERO (&rfds);
FD_SET (fdpty, &rfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
int loop_count = 0;
while (TRUE)
{
int retval = 0;
if (!strncmp (buf, "su", 2))
{
gchar **strings;
if (password)
{
nullify_password (password);
unset_gnome_keyring_password (context);
}
strings = g_strsplit (buf, ":", 2);
if (strings[1] && !strncmp (strings[1], " Authentication failure", 23))
{
if (used_gnome_keyring)
g_set_error (error, gksu_quark,
GKSU_ERROR_WRONGAUTOPASS,
_("Wrong password got from keyring."));
else
g_set_error (error, gksu_quark,
GKSU_ERROR_WRONGPASS,
_("Wrong password."));
}
g_strfreev (strings);
if (context->debug)
fprintf (stderr, "DEBUG (auth_failed) buf: -%s-/n", buf);
break;
}
else if (!strncmp (buf, "gksu: waiting", 13))
{
gchar *line;
if (password)
{
set_gnome_keyring_password (context, password);
nullify_password (password);
}
if (context->debug)
fprintf (stderr, "DEBUG (gksu: waiting) buf: -%s-/n", buf);
line = g_strdup_printf ("gksu-run: %s/n", context->display);
write (fdpty, line, strlen(line));
g_free (line);
line = g_strdup_printf ("gksu-run: %s/n", context->sn_id);
write (fdpty, line, strlen(line));
g_free (line);
line = g_strdup_printf ("gksu-run: %s/n", context->xauth);
write (fdpty, line, strlen(line));
g_free (line);
tcdrain (fdpty);
bzero (buf, 256);
read (fdpty, buf, 255);
break;
}
retval = select (fdpty + 1, &rfds, NULL, NULL, &tv);
if ((loop_count > 50) || (!retval))
{
gchar *emsg = NULL;
gchar *converted_str = NULL;
GError *converr = NULL;
if (password)
nullify_password (password);
converted_str = g_locale_to_utf8 (buf, -1, NULL, NULL, &converr);
if (converr)
{
g_warning (_("Failed to communicate with "
"gksu-run-helper./n/n"
"Received:/n"
" %s/n"
"While expecting:/n"
" %s"), buf, "gksu: waiting");
emsg = g_strdup_printf (_("Failed to communicate with "
"gksu-run-helper./n/n"
"Received bad string "
"while expecting:/n"
" %s"), "gksu: waiting");
g_error_free (converr);
}
else
emsg = g_strdup_printf (_("Failed to communicate with "
"gksu-run-helper./n/n"
"Received:/n"
" %s/n"
"While expecting:/n"
" %s"), converted_str, "gksu: waiting");
g_free (converted_str);
g_set_error (error, gksu_quark, GKSU_ERROR_HELPER, emsg);
g_free (emsg);
if (context->debug)
fprintf (stderr, "DEBUG (failed!) buf: -%s-/n", buf);
return FALSE;
}
else if (retval == -1)
{
if (context->debug)
fprintf (stderr, "DEBUG (select failed!) buf: %s/n", buf);
return FALSE;
}
read (fdpty, buf, 255);
if (context->debug)
fprintf (stderr, "DEBUG (run:after-pass) buf: -%s-/n", buf);
loop_count++;
}
if (!password_needed || used_gnome_keyring)
{
gboolean should_display;
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 ();
}
}
cmdline = g_strdup("bin/su");
/* wait for the child process to end or become something other
than su */
while ((!waitpid (pid, &status, WNOHANG)) &&
(g_str_has_suffix(cmdline, "bin/su")))
{
if (cmdline)
g_free (cmdline);
cmdline = get_process_name (pid);
usleep(100000);
}
if (context->sn_context)
gksu_context_launch_complete (context);
bzero(buf, 256);
while (read (fdpty, buf, 255) > 0)
{
fprintf (stderr, "%s", buf);
bzero(buf, 256);
}
if (!WIFEXITED(status))
waitpid(pid, &status, 0);
if (WEXITSTATUS(status))
{
if(cmdline)
{
/* su already exec()ed something else, don't report
* exit status errors in that case
*/
if (!g_str_has_suffix (cmdline, "su"))
{
g_free (cmdline);
return FALSE;
}
g_free (cmdline);
}
if (error == NULL)
g_set_error (error, gksu_quark,
GKSU_ERROR_CHILDFAILED,
_("su terminated with %d status"),
WEXITSTATUS(status));
}
}
if (error)
return FALSE;
return TRUE;
}
/**
* gksu_su
* @command_line: the command line that will be executed as other user
* @error: a #GError to be set with the error condition, if an error
* happens
*
* This function is a wrapper for gksu_su_run_full. It will call it
* without giving the callback functions, which leads to the standard
* ones being called. A simple #GksuContext is created to hold the
* user name and the command.
*
* Returns: TRUE if all went well, FALSE if an error happend.
*/
gboolean
gksu_su (gchar *command_line, GError **error)
{
GksuContext *context = gksu_context_new ();
gboolean retval;
context->command = g_strdup (command_line);
context->user = g_strdup ("root");
retval = gksu_su_full (context,
NULL, NULL,
NULL, NULL,
error);
gksu_context_free (context);
return retval;
}
static void
read_line (int fd, gchar *buffer, int n)
{
gint counter = 0;
gchar tmp[2] = {0};
for (; counter < (n - 1); counter++)
{
read (fd, tmp, 1);
if (tmp[0] == '/n')
break;
buffer[counter] = tmp[0];
}
buffer[counter+1] = '/0';
}
/**
* gksu_sudo_full:
* @context: a #GksuContext
* @ask_pass: a #GksuAskPassFunc to be called when the lib determines
* requesting a password is necessary; it may be NULL, in which case
* the standard password request dialog will be used
* @ask_pass_data: a #gpointer with user data to be passed to the
* #GksuAskPasswordFunc
* @pass_not_needed: a #GksuPassNotNeededFunc that will be called
* when the command is being run without the need for requesting
* a password; it will only be called if the display-no-pass-info
* gconf key is enabled; NULL will have the standard dialog be shown
* @pass_not_needed_data: a #gpointer with the user data to be passed to the
* #GksuPasswordNotNeededFunc
* @error: a #GError object to be filled with the error code or NULL
*
* This could be considered one of the main functions in GKSu.
* it is responsible for doing the 'user changing' magic calling
* the #GksuAskPassFunc function to request a password if needed.
* and the #GksuPassNotNeededFunc function if a password won't be
* needed, so the application has the oportunity of warning the user
* what it's doing.
*
* This function uses the sudo backend.
*
* Returns: TRUE if all went fine, FALSE if failed
*/
gboolean
gksu_sudo_full (GksuContext *context,
GksuAskPassFunc ask_pass,
gpointer ask_pass_data,
GksuPassNotNeededFunc pass_not_needed,
gpointer pass_not_needed_data,
GError **error)
{
char **cmd;
char buffer[256] = {0};
int argcount = 8;
int i, j;
GQuark gksu_quark;
gchar *xauth = NULL,
*xauth_env = NULL;
pid_t pid;
int status;
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;
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;
}
/*
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 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]);
}
if ((pipe(parent_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;
}
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;
}
pid = fork();
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 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;
// Parent
close(parent_pipe[1]);
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;
}
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
showing up after the read */
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
FD_SET(parent_pipe[0], &rfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
select (parent_pipe[0] + 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 (parent_pipe[0], buffer, 256);
if (context->debug)
fprintf (stderr, "buffer: -%s-/n", buffer);
}
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);
fprintf (outfile, "%s/n", password);
fclose (outfile);
nullify_password (password);
/* 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, infile);
/* this is the status we are interessted in */
fgets (buffer, 255, infile);
}
else
{
gboolean should_display;
if (context->debug)
fprintf (stderr, "No password prompt found; we'll assume we don't need a password./n");
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 (!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;
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."));
}
}
cmdline = g_strdup("sudo");
/* wait for the child process to end or become something other
then sudo */
while ((!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);
while (read (parent_pipe[0], buffer, 255) > 0)
{
fprintf (stderr, "%s", buffer);
bzero(buffer, 256);
}
if (!WIFEXITED(status))
waitpid(pid, &status, 0);
sudo_reset_xauth (context, xauth, xauth_env);
if (WEXITSTATUS(status))
{
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);
return FALSE;
}
g_free (cmdline);
}
if (error == NULL)
g_set_error (error, gksu_quark,
GKSU_ERROR_CHILDFAILED,
_("sudo terminated with %d status"),
WEXITSTATUS(status));
}
}
/* if error is set we have found an error condition */
if (error)
return FALSE;
return TRUE;
}
/**
* gksu_sudo
* @command_line: the command line that will be executed as other user
* @error: a #GError to be set with the error condition, if an error
* happens
*
* This function is a wrapper for gksu_sudo_run_full. It will call it
* without giving the callback functions, which leads to the standard
* ones being called. A simple #GksuContext is created to hold the
* user name and the command.
*
* Returns: TRUE if all went well, FALSE if an error happend.
*/
gboolean
gksu_sudo (gchar *command_line,
GError **error)
{
GksuContext *context = gksu_context_new ();
gboolean retval;
context->command = g_strdup (command_line);
context->user = g_strdup ("root");
retval = gksu_sudo_full (context,
NULL, NULL,
NULL, NULL,
error);
gksu_context_free (context);
return retval;
}
/**
* gksu_run_full:
* @context: a #GksuContext
* @ask_pass: a #GksuAskPassFunc to be called when the lib determines
* requesting a password is necessary; it may be NULL, in which case
* the standard password request dialog will be used
* @ask_pass_data: a #gpointer with user data to be passed to the
* #GksuAskPasswordFunc
* @pass_not_needed: a #GksuPassNotNeededFunc that will be called
* when the command is being run without the need for requesting
* a password; it will only be called if the display-no-pass-info
* gconf key is enabled; NULL will have the standard dialog be shown
* @pass_not_needed_data: a #gpointer with the user data to be passed to the
* #GksuPasswordNotNeededFunc
* @error: a #GError object to be filled with the error code or NULL
*
* This function is a wrapper for gksu_sudo_full/gksu_su_full. It will
* call one of them, depending on the GConf key that defines whether
* the default behavior for gksu is su or sudo mode. This is the
* recommended way of using the library functionality.
*
* Returns: TRUE if all went fine, FALSE if failed
*/
gboolean
gksu_run_full (GksuContext *context,
GksuAskPassFunc ask_pass,
gpointer ask_pass_data,
GksuPassNotNeededFunc pass_not_needed,
gpointer pass_not_needed_data,
GError **error)
{
GConfClient *gconf_client;
gboolean sudo_mode;
gconf_client = gconf_client_get_default ();
sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
NULL);
g_object_unref (gconf_client);
if (sudo_mode)
return gksu_sudo_full (context, ask_pass, ask_pass_data,
pass_not_needed, pass_not_needed_data,
error);
return gksu_su_full (context, ask_pass, ask_pass_data,
pass_not_needed, pass_not_needed_data,
error);
}
/**
* gksu_run
* @command_line: the command line that will be executed as other user
* @error: a #GError to be set with the error condition, if an error
* happens
*
* This function is a wrapper for gksu_sudo/gksu_su. It will call one
* of them, depending on the GConf key that defines whether the default
* behavior for gksu is su or sudo mode. This is the recommended way of
* using the library functionality.
*
* Returns: FALSE if all went well, TRUE if an error happend.
*/
gboolean
gksu_run (gchar *command_line,
GError **error)
{
GConfClient *gconf_client;
gboolean sudo_mode;
gconf_client = gconf_client_get_default ();
sudo_mode = gconf_client_get_bool (gconf_client, BASE_PATH "sudo-mode",
NULL);
g_object_unref (gconf_client);
if (sudo_mode)
return gksu_sudo (command_line, error);
return gksu_su (command_line, error);
}
/**
* gksu_ask_password_full:
* @context: a #GksuContext
* @prompt: a prompt different from Password:
* @error: a #GError object to be filled with the error code or NULL
*
* This function uses the gksu infra-structure to request for a
* password, but instead of passing it to su or sudo to run a command
* it simply returns the password.
*
* Returns: a newly allocated string with the password;
*/
gchar*
gksu_ask_password_full (GksuContext *context, gchar *prompt,
GError **error)
{
gchar *ret_value = su_ask_password (context, _(prompt), NULL, error);
if (context->sn_context)
gksu_context_launch_complete (context);
return ret_value;
}
/**
* gksu_ask_password
* @error: a #GError to be set with the error condition, if an error
* happens
*
* This function uses the gksu infra-structure to request for a
* password, but instead of passing it to su or sudo to run a command
* it simply returns the password. This is just a convenience wrapper
* for gksu_ask_password_full.
*
* Returns: a newly allocated string with the password;
*/
gchar*
gksu_ask_password (GError **error)
{
GksuContext *context = gksu_context_new ();
gchar* retval;
context->user = g_strdup ("root");
retval = gksu_ask_password_full (context, NULL, error);
gksu_context_free (context);
return retval;
}
结束