116359Sasami/*
216359Sasami * Copyright (c) 2000-2002 Damien Miller.  All rights reserved.
316359Sasami *
416359Sasami * Redistribution and use in source and binary forms, with or without
516359Sasami * modification, are permitted provided that the following conditions
630665Skato * are met:
716359Sasami * 1. Redistributions of source code must retain the above copyright
816359Sasami *    notice, this list of conditions and the following disclaimer.
916359Sasami * 2. Redistributions in binary form must reproduce the above copyright
1016359Sasami *    notice, this list of conditions and the following disclaimer in the
1116359Sasami *    documentation and/or other materials provided with the distribution.
1216359Sasami *
1316359Sasami * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1416359Sasami * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1516359Sasami * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1616359Sasami * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1716359Sasami * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1816359Sasami * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1916359Sasami * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2016359Sasami * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2130665Skato * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2230665Skato * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2330665Skato */
2425088Skato
2525571Skato/*
2625088Skato * This is a simple GNOME SSH passphrase grabber. To use it, set the
2725088Skato * environment variable SSH_ASKPASS to point to the location of
2827690Skato * gnome-ssh-askpass before calling "ssh-add < /dev/null".
2927690Skato *
3016359Sasami * There is only two run-time options: if you set the environment variable
3116359Sasami * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab
3216359Sasami * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the
3316359Sasami * pointer will be grabbed too. These may have some benefit to security if
3416359Sasami * you don't trust your X server. We grab the keyboard always.
3516359Sasami */
3616359Sasami
3716359Sasami/*
3816359Sasami * Compile with:
3918846Sasami *
4016359Sasami * cc `gnome-config --cflags gnome gnomeui` \
4116359Sasami *    gnome-ssh-askpass1.c -o gnome-ssh-askpass \
4216359Sasami *    `gnome-config --libs gnome gnomeui`
4316359Sasami *
4416359Sasami */
4516359Sasami
4625256Skato#include <stdlib.h>
4716359Sasami#include <stdio.h>
4816359Sasami#include <string.h>
4927101Skato#include <gnome.h>
5020494Skato#include <X11/Xlib.h>
5127101Skato#include <gdk/gdkx.h>
5220494Skato
5320494Skatovoid
5420494Skatoreport_failed_grab (void)
5520494Skato{
5620494Skato	GtkWidget *err;
5716359Sasami
5816359Sasami	err = gnome_message_box_new("Could not grab keyboard or mouse.\n"
5916359Sasami		"A malicious client may be eavesdropping on your session.",
6016359Sasami				    GNOME_MESSAGE_BOX_ERROR, "EXIT", NULL);
6116359Sasami	gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER);
6216359Sasami	gtk_object_set(GTK_OBJECT(err), "type", GTK_WINDOW_POPUP, NULL);
6316359Sasami
6416359Sasami	gnome_dialog_run_and_close(GNOME_DIALOG(err));
6516359Sasami}
6616359Sasami
6726058Skatoint
6826058Skatopassphrase_dialog(char *message)
6926058Skato{
7026058Skato	char *passphrase;
7126058Skato	char **messages;
7224112Skato	int result, i, grab_server, grab_pointer;
7316359Sasami	GtkWidget *dialog, *entry, *label;
7416359Sasami
7516359Sasami	grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL);
7616359Sasami	grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL);
7725088Skato
7825088Skato	dialog = gnome_dialog_new("OpenSSH", GNOME_STOCK_BUTTON_OK,
7926058Skato	    GNOME_STOCK_BUTTON_CANCEL, NULL);
8025088Skato
8116359Sasami	messages = g_strsplit(message, "\\n", 0);
8216359Sasami	if (messages)
8316359Sasami		for(i = 0; messages[i]; i++) {
8425088Skato			label = gtk_label_new(messages[i]);
8525571Skato			gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox),
8626058Skato			    label, FALSE, FALSE, 0);
8725571Skato		}
8825571Skato
8925571Skato	entry = gtk_entry_new();
9025571Skato	gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox), entry, FALSE,
9125571Skato	    FALSE, 0);
9225088Skato	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
9326058Skato	gtk_widget_grab_focus(entry);
9426058Skato
9526058Skato	/* Center window and prepare for grab */
9626058Skato	gtk_object_set(GTK_OBJECT(dialog), "type", GTK_WINDOW_POPUP, NULL);
9725088Skato	gnome_dialog_set_default(GNOME_DIALOG(dialog), 0);
9825088Skato	gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
9916359Sasami	gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, TRUE);
10016359Sasami	gnome_dialog_close_hides(GNOME_DIALOG(dialog), TRUE);
10116359Sasami	gtk_container_set_border_width(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox),
10216359Sasami	    GNOME_PAD);
10316359Sasami	gtk_widget_show_all(dialog);
10416359Sasami
10516359Sasami	/* Grab focus */
10616359Sasami	if (grab_server)
10716359Sasami		XGrabServer(GDK_DISPLAY());
10816359Sasami	if (grab_pointer && gdk_pointer_grab(dialog->window, TRUE, 0,
10916359Sasami	    NULL, NULL, GDK_CURRENT_TIME))
11016359Sasami		goto nograb;
11116359Sasami	if (gdk_keyboard_grab(dialog->window, FALSE, GDK_CURRENT_TIME))
11216359Sasami		goto nograbkb;
11326058Skato
11426058Skato	/* Make <enter> close dialog */
11516359Sasami	gnome_dialog_editable_enters(GNOME_DIALOG(dialog), GTK_EDITABLE(entry));
11616359Sasami
11716359Sasami	/* Run dialog */
11816359Sasami	result = gnome_dialog_run(GNOME_DIALOG(dialog));
11924112Skato
12016359Sasami	/* Ungrab */
12116359Sasami	if (grab_server)
12216359Sasami		XUngrabServer(GDK_DISPLAY());
12316359Sasami	if (grab_pointer)
12416359Sasami		gdk_pointer_ungrab(GDK_CURRENT_TIME);
12516359Sasami	gdk_keyboard_ungrab(GDK_CURRENT_TIME);
12616359Sasami	gdk_flush();
12716359Sasami
12816359Sasami	/* Report passphrase if user selected OK */
12916359Sasami	passphrase = gtk_entry_get_text(GTK_ENTRY(entry));
13016359Sasami	if (result == 0)
13124112Skato		puts(passphrase);
13216359Sasami
13316359Sasami	/* Zero passphrase in memory */
13426058Skato	memset(passphrase, '\0', strlen(passphrase));
13525088Skato	gtk_entry_set_text(GTK_ENTRY(entry), passphrase);
13625088Skato
13726058Skato	gnome_dialog_close(GNOME_DIALOG(dialog));
13825088Skato	return (result == 0 ? 0 : -1);
13925088Skato
14026058Skato	/* At least one grab failed - ungrab what we got, and report
14126058Skato	   the failure to the user.  Note that XGrabServer() cannot
14226058Skato	   fail.  */
14326058Skato nograbkb:
14426058Skato	gdk_pointer_ungrab(GDK_CURRENT_TIME);
14526058Skato nograb:
14626058Skato	if (grab_server)
14726058Skato		XUngrabServer(GDK_DISPLAY());
14826058Skato	gnome_dialog_close(GNOME_DIALOG(dialog));
14916359Sasami
15016359Sasami	report_failed_grab();
15116359Sasami	return (-1);
15216359Sasami}
15316359Sasami
15416359Sasamiint
15516359Sasamimain(int argc, char **argv)
15616359Sasami{
15716359Sasami	char *message;
15816359Sasami	int result;
15916359Sasami
16016359Sasami	gnome_init("GNOME ssh-askpass", "0.1", argc, argv);
16116359Sasami
16216359Sasami	if (argc == 2)
16316359Sasami		message = argv[1];
16416359Sasami	else
16516359Sasami		message = "Enter your OpenSSH passphrase:";
16616359Sasami
16716359Sasami	setvbuf(stdout, 0, _IONBF, 0);
16816359Sasami	result = passphrase_dialog(message);
16916359Sasami
17016359Sasami	return (result);
17130330Skato}
17216359Sasami