|
Packit |
b00eeb |
/*
|
|
Packit |
b00eeb |
* Copyright (C) 2014 Stefan Walter
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
b00eeb |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit |
b00eeb |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
b00eeb |
* the License, or (at your option) any later version.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
b00eeb |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
b00eeb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
b00eeb |
* Lesser General Public License for more details.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
b00eeb |
* License along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Auther: Stef Walter <stefw@gnome.org>
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include "config.h"
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include "gcr-ssh-askpass.h"
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include <glib-unix.h>
|
|
Packit |
b00eeb |
#include <glib/gi18n.h>
|
|
Packit |
b00eeb |
#include <glib/gstdio.h>
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include <sys/socket.h>
|
|
Packit |
b00eeb |
#include <sys/un.h>
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include <errno.h>
|
|
Packit |
b00eeb |
#include <unistd.h>
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Used from tests to override location */
|
|
Packit |
b00eeb |
const char *gcr_ssh_askpass_executable = LIBEXECDIR "/gcr-ssh-askpass";
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/**
|
|
Packit |
b00eeb |
* SECTION:gcr-ssh-askpass
|
|
Packit |
b00eeb |
* @title: GcrSshAskpass
|
|
Packit |
b00eeb |
* @short_description: Allows an ssh command to callback for a password
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* When used as the setup function while spawning an ssh command like ssh-add
|
|
Packit |
b00eeb |
* or ssh, this allows callbacks for passwords on the provided interaction.
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/**
|
|
Packit |
b00eeb |
* GcrSshAskpass:
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* An object containing the password prompting state.
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/**
|
|
Packit |
b00eeb |
* GcrSshAskpassClass:
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* The class for #GcrSshAskpass
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
enum {
|
|
Packit |
b00eeb |
PROP_0,
|
|
Packit |
b00eeb |
PROP_INTERACTION
|
|
Packit |
b00eeb |
};
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
struct _GcrSshAskpass {
|
|
Packit |
b00eeb |
GObject parent;
|
|
Packit |
b00eeb |
GTlsInteraction *interaction;
|
|
Packit |
b00eeb |
gchar *directory;
|
|
Packit |
b00eeb |
gchar *socket;
|
|
Packit |
b00eeb |
guint source;
|
|
Packit |
b00eeb |
gint fd;
|
|
Packit |
b00eeb |
GCancellable *cancellable;
|
|
Packit |
b00eeb |
GMainContext *context;
|
|
Packit |
b00eeb |
};
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
struct _GcrSshAskpassClass {
|
|
Packit |
b00eeb |
GObjectClass parent;
|
|
Packit |
b00eeb |
};
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
G_DEFINE_TYPE (GcrSshAskpass, gcr_ssh_askpass, G_TYPE_OBJECT);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static void
|
|
Packit |
b00eeb |
gcr_ssh_askpass_init (GcrSshAskpass *self)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
self->cancellable = g_cancellable_new ();
|
|
Packit |
b00eeb |
self->context = g_main_context_ref_thread_default ();
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static void
|
|
Packit |
b00eeb |
gcr_ssh_askpass_set_property (GObject *obj,
|
|
Packit |
b00eeb |
guint prop_id,
|
|
Packit |
b00eeb |
const GValue *value,
|
|
Packit |
b00eeb |
GParamSpec *pspec)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GcrSshAskpass *self = GCR_SSH_ASKPASS (obj);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
switch (prop_id) {
|
|
Packit |
b00eeb |
case PROP_INTERACTION:
|
|
Packit |
b00eeb |
self->interaction = g_value_dup_object (value);
|
|
Packit |
b00eeb |
g_return_if_fail (self->interaction != NULL);
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
default:
|
|
Packit |
b00eeb |
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static void
|
|
Packit |
b00eeb |
gcr_ssh_askpass_get_property (GObject *obj,
|
|
Packit |
b00eeb |
guint prop_id,
|
|
Packit |
b00eeb |
GValue *value,
|
|
Packit |
b00eeb |
GParamSpec *pspec)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GcrSshAskpass *self = GCR_SSH_ASKPASS (obj);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
switch (prop_id) {
|
|
Packit |
b00eeb |
case PROP_INTERACTION:
|
|
Packit |
b00eeb |
g_value_set_object (value, gcr_ssh_askpass_get_interaction (self));
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
default:
|
|
Packit |
b00eeb |
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
write_all (gint fd,
|
|
Packit |
b00eeb |
const guchar *buf,
|
|
Packit |
b00eeb |
gsize len)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
guint all = len;
|
|
Packit |
b00eeb |
int res;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
while (len > 0) {
|
|
Packit |
b00eeb |
res = write (fd, buf, len);
|
|
Packit |
b00eeb |
if (res <= 0) {
|
|
Packit |
b00eeb |
if (errno == EAGAIN || errno == EINTR)
|
|
Packit |
b00eeb |
continue;
|
|
Packit |
b00eeb |
if (errno != EPIPE)
|
|
Packit |
b00eeb |
g_warning ("couldn't write %u bytes to client: %s", all,
|
|
Packit |
b00eeb |
res < 0 ? g_strerror (errno) : "");
|
|
Packit |
b00eeb |
return FALSE;
|
|
Packit |
b00eeb |
} else {
|
|
Packit |
b00eeb |
len -= res;
|
|
Packit |
b00eeb |
buf += res;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static GString *
|
|
Packit |
b00eeb |
read_all_into_string (gint fd)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GString *input = g_string_new ("");
|
|
Packit |
b00eeb |
gsize len;
|
|
Packit |
b00eeb |
gssize ret;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
for (;;) {
|
|
Packit |
b00eeb |
len = input->len;
|
|
Packit |
b00eeb |
g_string_set_size (input, len + 256);
|
|
Packit |
b00eeb |
ret = read (fd, input->str + len, 256);
|
|
Packit |
b00eeb |
if (ret < 0) {
|
|
Packit |
b00eeb |
if (errno != EINTR && errno != EAGAIN) {
|
|
Packit |
b00eeb |
g_critical ("couldn't read from gcr-ssh-askpass: %s", g_strerror (errno));
|
|
Packit |
b00eeb |
g_string_free (input, TRUE);
|
|
Packit |
b00eeb |
return NULL;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
} else if (ret == 0) {
|
|
Packit |
b00eeb |
return input;
|
|
Packit |
b00eeb |
} else {
|
|
Packit |
b00eeb |
input->len = len + ret;
|
|
Packit |
b00eeb |
input->str[input->len] = '\0';
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
typedef struct {
|
|
Packit |
b00eeb |
gint fd;
|
|
Packit |
b00eeb |
GTlsInteraction *interaction;
|
|
Packit |
b00eeb |
GCancellable *cancellable;
|
|
Packit |
b00eeb |
} AskpassContext;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gpointer
|
|
Packit |
b00eeb |
askpass_thread (gpointer data)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
AskpassContext *ctx = data;
|
|
Packit |
b00eeb |
gboolean success = FALSE;
|
|
Packit |
b00eeb |
GTlsPassword *password = NULL;
|
|
Packit |
b00eeb |
GTlsInteractionResult res;
|
|
Packit |
b00eeb |
GError *error = NULL;
|
|
Packit |
b00eeb |
const guchar *value;
|
|
Packit |
b00eeb |
GString *input;
|
|
Packit |
b00eeb |
gsize length;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
input = read_all_into_string (ctx->fd);
|
|
Packit |
b00eeb |
if (!input)
|
|
Packit |
b00eeb |
goto out;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (input->len == 0)
|
|
Packit |
b00eeb |
g_string_append (input, _("Enter your OpenSSH passphrase"));
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_debug ("asking for ssh-askpass password: %s", input->str);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
password = g_tls_password_new (G_TLS_PASSWORD_NONE, input->str);
|
|
Packit |
b00eeb |
res = g_tls_interaction_invoke_ask_password (ctx->interaction, password, ctx->cancellable, &error);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_debug ("ask password returned %d", res);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
success = FALSE;
|
|
Packit |
b00eeb |
if (res == G_TLS_INTERACTION_HANDLED) {
|
|
Packit |
b00eeb |
value = g_tls_password_get_value (password, &length);
|
|
Packit |
b00eeb |
if (write_all (ctx->fd, (const guchar *)value, length))
|
|
Packit |
b00eeb |
g_debug ("password written to gcr-ssh-askpass");
|
|
Packit |
b00eeb |
else
|
|
Packit |
b00eeb |
g_message ("failed to write password to gcr-ssh-askpass");
|
|
Packit |
b00eeb |
success = TRUE;
|
|
Packit |
b00eeb |
} else if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
|
Packit |
b00eeb |
g_warning ("couldn't prompt for password: %s", error->message);
|
|
Packit |
b00eeb |
} else {
|
|
Packit |
b00eeb |
g_debug ("unhandled or cancelled ask password");
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
out:
|
|
Packit |
b00eeb |
if (!success) {
|
|
Packit |
b00eeb |
g_debug ("writing failure to gcr-ssh-askpass");
|
|
Packit |
b00eeb |
write_all (ctx->fd, (const guchar *)"\xff", 1);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
if (password)
|
|
Packit |
b00eeb |
g_object_unref (password);
|
|
Packit |
b00eeb |
if (input)
|
|
Packit |
b00eeb |
g_string_free (input, TRUE);
|
|
Packit |
b00eeb |
g_clear_error (&error);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_close (ctx->fd, NULL);
|
|
Packit |
b00eeb |
g_object_unref (ctx->interaction);
|
|
Packit |
b00eeb |
g_object_unref (ctx->cancellable);
|
|
Packit |
b00eeb |
g_free (ctx);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
return NULL;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static gboolean
|
|
Packit |
b00eeb |
askpass_accept (gint fd,
|
|
Packit |
b00eeb |
GIOCondition cond,
|
|
Packit |
b00eeb |
gpointer user_data)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GcrSshAskpass *self = user_data;
|
|
Packit |
b00eeb |
AskpassContext *ctx;
|
|
Packit |
b00eeb |
struct sockaddr_un addr;
|
|
Packit |
b00eeb |
socklen_t addrlen;
|
|
Packit |
b00eeb |
GThread *thread;
|
|
Packit |
b00eeb |
gint new_fd;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
addrlen = sizeof (addr);
|
|
Packit |
b00eeb |
new_fd = accept (fd, (struct sockaddr *) &addr, &addrlen);
|
|
Packit |
b00eeb |
if (new_fd < 0) {
|
|
Packit |
b00eeb |
if (errno != EAGAIN && errno != EINTR)
|
|
Packit |
b00eeb |
g_warning ("couldn't accept new control request: %s", g_strerror (errno));
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_debug ("accepted new connection from gcr-ssh-askpass");
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
ctx = g_new0 (AskpassContext, 1);
|
|
Packit |
b00eeb |
ctx->fd = new_fd;
|
|
Packit |
b00eeb |
ctx->interaction = g_object_ref (self->interaction);
|
|
Packit |
b00eeb |
ctx->cancellable = g_object_ref (self->cancellable);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
thread = g_thread_new ("ssh-askpass", askpass_thread, ctx);
|
|
Packit |
b00eeb |
g_thread_unref (thread);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
return TRUE;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static void
|
|
Packit |
b00eeb |
gcr_ssh_askpass_constructed (GObject *obj)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GcrSshAskpass *self = GCR_SSH_ASKPASS (obj);
|
|
Packit |
b00eeb |
struct sockaddr_un addr;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
G_OBJECT_CLASS (gcr_ssh_askpass_parent_class)->constructed (obj);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
self->directory = g_build_filename (g_get_user_runtime_dir (), "ssh-askpass.XXXXXX", NULL);
|
|
Packit |
b00eeb |
if (!g_mkdtemp_full (self->directory, 0700)) {
|
|
Packit |
b00eeb |
g_warning ("couldn't create temporary directory: %s: %s", self->directory, g_strerror (errno));
|
|
Packit |
b00eeb |
return;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
self->socket = g_build_filename (self->directory, "socket", NULL);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
self->fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
|
Packit |
b00eeb |
if (self->fd < 0) {
|
|
Packit |
b00eeb |
g_warning ("couldn't open socket: %s", g_strerror (errno));
|
|
Packit |
b00eeb |
return;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!g_unix_set_fd_nonblocking (self->fd, TRUE, NULL))
|
|
Packit |
b00eeb |
g_return_if_reached ();
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
memset (&addr, 0, sizeof (addr));
|
|
Packit |
b00eeb |
addr.sun_family = AF_UNIX;
|
|
Packit |
b00eeb |
g_strlcpy (addr.sun_path, self->socket, sizeof (addr.sun_path));
|
|
Packit |
b00eeb |
if (bind (self->fd, (struct sockaddr*) &addr, sizeof (addr)) < 0) {
|
|
Packit |
b00eeb |
g_warning ("couldn't bind to askpass socket: %s: %s", self->socket, g_strerror (errno));
|
|
Packit |
b00eeb |
return;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (listen (self->fd, 128) < 0) {
|
|
Packit |
b00eeb |
g_warning ("couldn't listen on askpass socket: %s: %s", self->socket, g_strerror (errno));
|
|
Packit |
b00eeb |
return;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_debug ("listening for gcr-ssh-askpass at: %s", self->socket);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
self->source = g_unix_fd_add (self->fd, G_IO_IN, askpass_accept, self);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static void
|
|
Packit |
b00eeb |
gcr_ssh_askpass_dispose (GObject *obj)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GcrSshAskpass *self = GCR_SSH_ASKPASS (obj);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_cancellable_cancel (self->cancellable);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (self->source) {
|
|
Packit |
b00eeb |
g_source_remove (self->source);
|
|
Packit |
b00eeb |
self->source = 0;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (self->fd >= 0) {
|
|
Packit |
b00eeb |
g_close (self->fd, NULL);
|
|
Packit |
b00eeb |
self->fd = -1;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (self->socket) {
|
|
Packit |
b00eeb |
g_unlink (self->socket);
|
|
Packit |
b00eeb |
g_free (self->socket);
|
|
Packit |
b00eeb |
self->socket = NULL;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (self->directory) {
|
|
Packit |
b00eeb |
g_rmdir (self->directory);
|
|
Packit |
b00eeb |
g_free (self->directory);
|
|
Packit |
b00eeb |
self->directory = NULL;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (self->interaction) {
|
|
Packit |
b00eeb |
g_object_unref (self->interaction);
|
|
Packit |
b00eeb |
self->interaction = NULL;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
G_OBJECT_CLASS (gcr_ssh_askpass_parent_class)->dispose (obj);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static void
|
|
Packit |
b00eeb |
gcr_ssh_askpass_finalize (GObject *obj)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GcrSshAskpass *self = GCR_SSH_ASKPASS (obj);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_object_unref (self->cancellable);
|
|
Packit |
b00eeb |
g_main_context_unref (self->context);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
G_OBJECT_CLASS (gcr_ssh_askpass_parent_class)->finalize (obj);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/**
|
|
Packit |
b00eeb |
* gcr_ssh_askpass_new:
|
|
Packit |
b00eeb |
* @interaction: the interaction to use for prompting paswords
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Create a new GcrSshAskpass object which can be used to spawn an
|
|
Packit |
b00eeb |
* ssh command and prompt for any necessary passwords.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Use the gcr_ssh_askpass_child_setup() function as a callback with
|
|
Packit |
b00eeb |
* g_spawn_sync(), g_spawn_async() or g_spawn_async_with_pipes().
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Returns: (transfer full): A new #GcrSshAskpass object
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
GcrSshAskpass *
|
|
Packit |
b00eeb |
gcr_ssh_askpass_new (GTlsInteraction *interaction)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
g_return_val_if_fail (G_IS_TLS_INTERACTION (interaction), NULL);
|
|
Packit |
b00eeb |
return g_object_new (GCR_TYPE_SSH_ASKPASS,
|
|
Packit |
b00eeb |
"interaction", interaction,
|
|
Packit |
b00eeb |
NULL);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/**
|
|
Packit |
b00eeb |
* gcr_ssh_askpass_get_interaction:
|
|
Packit |
b00eeb |
* @self: a #GcrSshAskpass object
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Get the interaction associated with this object.
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Returns: (transfer none): the interaction
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
GTlsInteraction *
|
|
Packit |
b00eeb |
gcr_ssh_askpass_get_interaction (GcrSshAskpass *self)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
g_return_val_if_fail (GCR_IS_SSH_ASKPASS (self), NULL);
|
|
Packit |
b00eeb |
return self->interaction;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/**
|
|
Packit |
b00eeb |
* gcr_ssh_askpass_child_setup:
|
|
Packit |
b00eeb |
* @askpass: a #GcrSshAskpass object
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* Use this function as a callback setup function passed to g_spawn_sync(),
|
|
Packit |
b00eeb |
* g_spawn_async(), g_spawn_async_with_pipes().
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
void
|
|
Packit |
b00eeb |
gcr_ssh_askpass_child_setup (gpointer askpass)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GcrSshAskpass *self = askpass;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
g_setenv ("SSH_ASKPASS", gcr_ssh_askpass_executable, TRUE);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* ssh wants DISPLAY set in order to use SSH_ASKPASS */
|
|
Packit |
b00eeb |
if (!g_getenv ("DISPLAY"))
|
|
Packit |
b00eeb |
g_setenv ("DISPLAY", "x", TRUE);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* For communicating back with ourselves */
|
|
Packit |
b00eeb |
if (self->socket)
|
|
Packit |
b00eeb |
g_setenv ("GCR_SSH_ASKPASS_SOCKET", self->socket, TRUE);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/* Close the control terminal */
|
|
Packit |
b00eeb |
setsid ();
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
static void
|
|
Packit |
b00eeb |
gcr_ssh_askpass_class_init (GcrSshAskpassClass *klass)
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
gobject_class->get_property = gcr_ssh_askpass_get_property;
|
|
Packit |
b00eeb |
gobject_class->set_property = gcr_ssh_askpass_set_property;
|
|
Packit |
b00eeb |
gobject_class->constructed = gcr_ssh_askpass_constructed;
|
|
Packit |
b00eeb |
gobject_class->dispose = gcr_ssh_askpass_dispose;
|
|
Packit |
b00eeb |
gobject_class->finalize = gcr_ssh_askpass_finalize;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
/**
|
|
Packit |
b00eeb |
* GcrSshAskpass:interaction:
|
|
Packit |
b00eeb |
*
|
|
Packit |
b00eeb |
* The interaction used to prompt for passwords.
|
|
Packit |
b00eeb |
*/
|
|
Packit |
b00eeb |
g_object_class_install_property (gobject_class, PROP_INTERACTION,
|
|
Packit |
b00eeb |
g_param_spec_object ("interaction", "Interaction", "Interaction",
|
|
Packit |
b00eeb |
G_TYPE_TLS_INTERACTION,
|
|
Packit |
b00eeb |
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#ifdef GCR_SSH_ASKPASS_TOOL
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#include "egg/egg-secure-memory.h"
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
EGG_SECURE_DEFINE_GLIB_GLOBALS ();
|
|
Packit |
b00eeb |
EGG_SECURE_DECLARE ("ssh-askpass");
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
int
|
|
Packit |
b00eeb |
main (int argc,
|
|
Packit |
b00eeb |
char *argv[])
|
|
Packit |
b00eeb |
{
|
|
Packit |
b00eeb |
GString *message;
|
|
Packit |
b00eeb |
struct sockaddr_un addr;
|
|
Packit |
b00eeb |
const gchar *path;
|
|
Packit |
b00eeb |
guchar *buf;
|
|
Packit |
b00eeb |
gint count;
|
|
Packit |
b00eeb |
gint i;
|
|
Packit |
b00eeb |
int ret;
|
|
Packit |
b00eeb |
int fd;
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
path = g_getenv ("GCR_SSH_ASKPASS_SOCKET");
|
|
Packit |
b00eeb |
if (path == NULL) {
|
|
Packit |
b00eeb |
g_printerr ("gcr-ssh-askpass: this program is not meant to be run directly");
|
|
Packit |
b00eeb |
return 2;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
|
Packit |
b00eeb |
if (fd < 0) {
|
|
Packit |
b00eeb |
g_warning ("couldn't open socket: %s", g_strerror (errno));
|
|
Packit |
b00eeb |
return -1;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
memset (&addr, 0, sizeof (addr));
|
|
Packit |
b00eeb |
addr.sun_family = AF_UNIX;
|
|
Packit |
b00eeb |
g_strlcpy (addr.sun_path, path, sizeof (addr.sun_path));
|
|
Packit |
b00eeb |
if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0) {
|
|
Packit |
b00eeb |
g_warning ("couldn't connect to askpass socket: %s: %s", path, g_strerror (errno));
|
|
Packit |
b00eeb |
return -1;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
message = g_string_new ("");
|
|
Packit |
b00eeb |
if (argc > 1) {
|
|
Packit |
b00eeb |
for (i = 1; i < argc; i++) {
|
|
Packit |
b00eeb |
if (i == 1)
|
|
Packit |
b00eeb |
g_string_append_c (message, ' ');
|
|
Packit |
b00eeb |
g_string_append (message, argv[i]);
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (!write_all (fd, (const guchar *)message->str, message->len)) {
|
|
Packit |
b00eeb |
g_string_free (message, TRUE);
|
|
Packit |
b00eeb |
return -1;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
g_string_free (message, TRUE);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (shutdown (fd, SHUT_WR) < 0) {
|
|
Packit |
b00eeb |
g_warning ("couldn't shutdown socket: %s", g_strerror (errno));
|
|
Packit |
b00eeb |
return -1;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
count = 0;
|
|
Packit |
b00eeb |
buf = egg_secure_alloc (128);
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
for (;;) {
|
|
Packit |
b00eeb |
ret = read (fd, buf, 128);
|
|
Packit |
b00eeb |
if (ret < 0) {
|
|
Packit |
b00eeb |
if (errno != EINTR && errno != EAGAIN) {
|
|
Packit |
b00eeb |
if (errno != ECONNRESET) {
|
|
Packit |
b00eeb |
g_critical ("couldn't read from ssh-askpass socket: %s",
|
|
Packit |
b00eeb |
g_strerror (errno));
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
egg_secure_free (buf);
|
|
Packit |
b00eeb |
return -1;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
ret = 0;
|
|
Packit |
b00eeb |
} else if (ret == 0) {
|
|
Packit |
b00eeb |
break;
|
|
Packit |
b00eeb |
} else if (!write_all (1, buf, ret)) {
|
|
Packit |
b00eeb |
egg_secure_free (buf);
|
|
Packit |
b00eeb |
return -1;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
count += ret;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
if (count == 1 && buf[0] == 0xff) {
|
|
Packit |
b00eeb |
egg_secure_free (buf);
|
|
Packit |
b00eeb |
return -1;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
egg_secure_free (buf);
|
|
Packit |
b00eeb |
return 0;
|
|
Packit |
b00eeb |
}
|
|
Packit |
b00eeb |
|
|
Packit |
b00eeb |
#endif /* GCR_SSH_ASKPASS_TOOL */
|