1367fd86des/*
2367fd86des * Copyright (c) 2000-2002 Damien Miller.  All rights reserved.
3367fd86des *
4367fd86des * Redistribution and use in source and binary forms, with or without
5367fd86des * modification, are permitted provided that the following conditions
6367fd86des * are met:
7367fd86des * 1. Redistributions of source code must retain the above copyright
8367fd86des *    notice, this list of conditions and the following disclaimer.
9367fd86des * 2. Redistributions in binary form must reproduce the above copyright
10367fd86des *    notice, this list of conditions and the following disclaimer in the
11367fd86des *    documentation and/or other materials provided with the distribution.
12367fd86des *
13367fd86des * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14367fd86des * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15367fd86des * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16367fd86des * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17367fd86des * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18367fd86des * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19367fd86des * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20367fd86des * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21367fd86des * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22367fd86des * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23367fd86des */
24367fd86des
25367fd86des/*
26367fd86des * This is a simple GNOME SSH passphrase grabber. To use it, set the
27367fd86des * environment variable SSH_ASKPASS to point to the location of
28367fd86des * gnome-ssh-askpass before calling "ssh-add < /dev/null".
29367fd86des *
30367fd86des * There is only two run-time options: if you set the environment variable
31367fd86des * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab
32367fd86des * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the
33367fd86des * pointer will be grabbed too. These may have some benefit to security if
34367fd86des * you don't trust your X server. We grab the keyboard always.
35367fd86des */
36367fd86des
37367fd86des/*
38367fd86des * Compile with:
39367fd86des *
40367fd86des * cc `gnome-config --cflags gnome gnomeui` \
41367fd86des *    gnome-ssh-askpass1.c -o gnome-ssh-askpass \
42367fd86des *    `gnome-config --libs gnome gnomeui`
43367fd86des *
44367fd86des */
45367fd86des
46367fd86des#include <stdlib.h>
47367fd86des#include <stdio.h>
48367fd86des#include <string.h>
49367fd86des#include <gnome.h>
50367fd86des#include <X11/Xlib.h>
51367fd86des#include <gdk/gdkx.h>
52367fd86des
53367fd86desvoid
54367fd86desreport_failed_grab (void)
55367fd86des{
56367fd86des	GtkWidget *err;
57367fd86des
58367fd86des	err = gnome_message_box_new("Could not grab keyboard or mouse.\n"
59367fd86des		"A malicious client may be eavesdropping on your session.",
60367fd86des				    GNOME_MESSAGE_BOX_ERROR, "EXIT", NULL);
61367fd86des	gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER);
62367fd86des	gtk_object_set(GTK_OBJECT(err), "type", GTK_WINDOW_POPUP, NULL);
63367fd86des
64367fd86des	gnome_dialog_run_and_close(GNOME_DIALOG(err));
65367fd86des}
66367fd86des
67367fd86desint
68367fd86despassphrase_dialog(char *message)
69367fd86des{
70367fd86des	char *passphrase;
71367fd86des	char **messages;
72367fd86des	int result, i, grab_server, grab_pointer;
73367fd86des	GtkWidget *dialog, *entry, *label;
74367fd86des
75367fd86des	grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL);
76367fd86des	grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL);
77367fd86des
78367fd86des	dialog = gnome_dialog_new("OpenSSH", GNOME_STOCK_BUTTON_OK,
79367fd86des	    GNOME_STOCK_BUTTON_CANCEL, NULL);
80367fd86des
81367fd86des	messages = g_strsplit(message, "\\n", 0);
82367fd86des	if (messages)
83367fd86des		for(i = 0; messages[i]; i++) {
84367fd86des			label = gtk_label_new(messages[i]);
85367fd86des			gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox),
86367fd86des			    label, FALSE, FALSE, 0);
87367fd86des		}
88367fd86des
89367fd86des	entry = gtk_entry_new();
90367fd86des	gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox), entry, FALSE,
91367fd86des	    FALSE, 0);
92367fd86des	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
93367fd86des	gtk_widget_grab_focus(entry);
94367fd86des
95367fd86des	/* Center window and prepare for grab */
96367fd86des	gtk_object_set(GTK_OBJECT(dialog), "type", GTK_WINDOW_POPUP, NULL);
97367fd86des	gnome_dialog_set_default(GNOME_DIALOG(dialog), 0);
98367fd86des	gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
99367fd86des	gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, TRUE);
100367fd86des	gnome_dialog_close_hides(GNOME_DIALOG(dialog), TRUE);
101367fd86des	gtk_container_set_border_width(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox),
102367fd86des	    GNOME_PAD);
103367fd86des	gtk_widget_show_all(dialog);
104367fd86des
105367fd86des	/* Grab focus */
106367fd86des	if (grab_server)
107367fd86des		XGrabServer(GDK_DISPLAY());
108367fd86des	if (grab_pointer && gdk_pointer_grab(dialog->window, TRUE, 0,
109367fd86des	    NULL, NULL, GDK_CURRENT_TIME))
110367fd86des		goto nograb;
111367fd86des	if (gdk_keyboard_grab(dialog->window, FALSE, GDK_CURRENT_TIME))
112367fd86des		goto nograbkb;
113367fd86des
114367fd86des	/* Make <enter> close dialog */
115367fd86des	gnome_dialog_editable_enters(GNOME_DIALOG(dialog), GTK_EDITABLE(entry));
116367fd86des
117367fd86des	/* Run dialog */
118367fd86des	result = gnome_dialog_run(GNOME_DIALOG(dialog));
119367fd86des
120367fd86des	/* Ungrab */
121367fd86des	if (grab_server)
122367fd86des		XUngrabServer(GDK_DISPLAY());
123367fd86des	if (grab_pointer)
124367fd86des		gdk_pointer_ungrab(GDK_CURRENT_TIME);
125367fd86des	gdk_keyboard_ungrab(GDK_CURRENT_TIME);
126367fd86des	gdk_flush();
127367fd86des
128367fd86des	/* Report passphrase if user selected OK */
129367fd86des	passphrase = gtk_entry_get_text(GTK_ENTRY(entry));
130367fd86des	if (result == 0)
131367fd86des		puts(passphrase);
132367fd86des
133367fd86des	/* Zero passphrase in memory */
134367fd86des	memset(passphrase, '\0', strlen(passphrase));
135367fd86des	gtk_entry_set_text(GTK_ENTRY(entry), passphrase);
136367fd86des
137367fd86des	gnome_dialog_close(GNOME_DIALOG(dialog));
138367fd86des	return (result == 0 ? 0 : -1);
139367fd86des
140367fd86des	/* At least one grab failed - ungrab what we got, and report
141367fd86des	   the failure to the user.  Note that XGrabServer() cannot
142367fd86des	   fail.  */
143367fd86des nograbkb:
144367fd86des	gdk_pointer_ungrab(GDK_CURRENT_TIME);
145367fd86des nograb:
146367fd86des	if (grab_server)
147367fd86des		XUngrabServer(GDK_DISPLAY());
148367fd86des	gnome_dialog_close(GNOME_DIALOG(dialog));
149367fd86des
150367fd86des	report_failed_grab();
151367fd86des	return (-1);
152367fd86des}
153367fd86des
154367fd86desint
155367fd86desmain(int argc, char **argv)
156367fd86des{
157367fd86des	char *message;
158367fd86des	int result;
159367fd86des
160367fd86des	gnome_init("GNOME ssh-askpass", "0.1", argc, argv);
161367fd86des
162367fd86des	if (argc == 2)
163367fd86des		message = argv[1];
164367fd86des	else
165367fd86des		message = "Enter your OpenSSH passphrase:";
166367fd86des
167367fd86des	setvbuf(stdout, 0, _IONBF, 0);
168367fd86des	result = passphrase_dialog(message);
169367fd86des
170367fd86des	return (result);
171367fd86des}
172