1170530Ssam/*
2178354Ssam * Copyright (c) 2000-2002 Damien Miller.  All rights reserved.
3170530Ssam *
4170530Ssam * Redistribution and use in source and binary forms, with or without
5170530Ssam * modification, are permitted provided that the following conditions
6170530Ssam * are met:
7170530Ssam * 1. Redistributions of source code must retain the above copyright
8170530Ssam *    notice, this list of conditions and the following disclaimer.
9170530Ssam * 2. Redistributions in binary form must reproduce the above copyright
10170530Ssam *    notice, this list of conditions and the following disclaimer in the
11170530Ssam *    documentation and/or other materials provided with the distribution.
12170530Ssam *
13170530Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14170530Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15170530Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16170530Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17170530Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18170530Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19170530Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20170530Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21170530Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22170530Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23170530Ssam */
24170530Ssam
25170530Ssam/*
26170530Ssam * This is a simple GNOME SSH passphrase grabber. To use it, set the
27170530Ssam * environment variable SSH_ASKPASS to point to the location of
28170530Ssam * gnome-ssh-askpass before calling "ssh-add < /dev/null".
29170530Ssam *
30170530Ssam * There is only two run-time options: if you set the environment variable
31170530Ssam * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab
32170530Ssam * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the
33170530Ssam * pointer will be grabbed too. These may have some benefit to security if
34170530Ssam * you don't trust your X server. We grab the keyboard always.
35170530Ssam */
36178354Ssam
37170530Ssam/*
38170530Ssam * Compile with:
39170530Ssam *
40170530Ssam * cc `gnome-config --cflags gnome gnomeui` \
41170530Ssam *    gnome-ssh-askpass1.c -o gnome-ssh-askpass \
42170530Ssam *    `gnome-config --libs gnome gnomeui`
43170530Ssam *
44170530Ssam */
45170530Ssam
46170530Ssam#include <stdlib.h>
47170530Ssam#include <stdio.h>
48170530Ssam#include <string.h>
49170530Ssam#include <gnome.h>
50178354Ssam#include <X11/Xlib.h>
51170530Ssam#include <gdk/gdkx.h>
52170530Ssam
53170530Ssamvoid
54170530Ssamreport_failed_grab (void)
55170530Ssam{
56178354Ssam	GtkWidget *err;
57178354Ssam
58178354Ssam	err = gnome_message_box_new("Could not grab keyboard or mouse.\n"
59178354Ssam		"A malicious client may be eavesdropping on your session.",
60178354Ssam				    GNOME_MESSAGE_BOX_ERROR, "EXIT", NULL);
61178354Ssam	gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER);
62178354Ssam	gtk_object_set(GTK_OBJECT(err), "type", GTK_WINDOW_POPUP, NULL);
63178354Ssam
64178354Ssam	gnome_dialog_run_and_close(GNOME_DIALOG(err));
65178354Ssam}
66178354Ssam
67178354Ssamint
68178354Ssampassphrase_dialog(char *message)
69178354Ssam{
70178354Ssam	char *passphrase;
71178354Ssam	char **messages;
72178354Ssam	int result, i, grab_server, grab_pointer;
73170530Ssam	GtkWidget *dialog, *entry, *label;
74170530Ssam
75170530Ssam	grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL);
76170530Ssam	grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL);
77170530Ssam
78170530Ssam	dialog = gnome_dialog_new("OpenSSH", GNOME_STOCK_BUTTON_OK,
79170530Ssam	    GNOME_STOCK_BUTTON_CANCEL, NULL);
80170530Ssam
81173273Ssam	messages = g_strsplit(message, "\\n", 0);
82173273Ssam	if (messages)
83173273Ssam		for(i = 0; messages[i]; i++) {
84173273Ssam			label = gtk_label_new(messages[i]);
85173273Ssam			gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox),
86178354Ssam			    label, FALSE, FALSE, 0);
87178354Ssam		}
88178354Ssam
89173273Ssam	entry = gtk_entry_new();
90178354Ssam	gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox), entry, FALSE,
91178354Ssam	    FALSE, 0);
92178354Ssam	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
93178354Ssam	gtk_widget_grab_focus(entry);
94178354Ssam
95178354Ssam	/* Center window and prepare for grab */
96178354Ssam	gtk_object_set(GTK_OBJECT(dialog), "type", GTK_WINDOW_POPUP, NULL);
97178354Ssam	gnome_dialog_set_default(GNOME_DIALOG(dialog), 0);
98178354Ssam	gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
99178354Ssam	gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, TRUE);
100178354Ssam	gnome_dialog_close_hides(GNOME_DIALOG(dialog), TRUE);
101178354Ssam	gtk_container_set_border_width(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox),
102178354Ssam	    GNOME_PAD);
103170530Ssam	gtk_widget_show_all(dialog);
104178354Ssam
105178354Ssam	/* Grab focus */
106170530Ssam	if (grab_server)
107170530Ssam		XGrabServer(GDK_DISPLAY());
108170530Ssam	if (grab_pointer && gdk_pointer_grab(dialog->window, TRUE, 0,
109170530Ssam	    NULL, NULL, GDK_CURRENT_TIME))
110170530Ssam		goto nograb;
111170530Ssam	if (gdk_keyboard_grab(dialog->window, FALSE, GDK_CURRENT_TIME))
112170530Ssam		goto nograbkb;
113170530Ssam
114170530Ssam	/* Make <enter> close dialog */
115170530Ssam	gnome_dialog_editable_enters(GNOME_DIALOG(dialog), GTK_EDITABLE(entry));
116170530Ssam
117170530Ssam	/* Run dialog */
118170530Ssam	result = gnome_dialog_run(GNOME_DIALOG(dialog));
119170530Ssam
120170530Ssam	/* Ungrab */
121170530Ssam	if (grab_server)
122170530Ssam		XUngrabServer(GDK_DISPLAY());
123178354Ssam	if (grab_pointer)
124170530Ssam		gdk_pointer_ungrab(GDK_CURRENT_TIME);
125170530Ssam	gdk_keyboard_ungrab(GDK_CURRENT_TIME);
126170530Ssam	gdk_flush();
127170530Ssam
128173273Ssam	/* Report passphrase if user selected OK */
129173273Ssam	passphrase = gtk_entry_get_text(GTK_ENTRY(entry));
130178354Ssam	if (result == 0)
131173273Ssam		puts(passphrase);
132178354Ssam
133178354Ssam	/* Zero passphrase in memory */
134178354Ssam	memset(passphrase, '\0', strlen(passphrase));
135178354Ssam	gtk_entry_set_text(GTK_ENTRY(entry), passphrase);
136173273Ssam
137178354Ssam	gnome_dialog_close(GNOME_DIALOG(dialog));
138178354Ssam	return (result == 0 ? 0 : -1);
139178354Ssam
140178354Ssam	/*
141178354Ssam	 * At least one grab failed - ungrab what we got, and report the
142178354Ssam	 * failure to the user. Note that XGrabServer() cannot fail.
143178354Ssam	 */
144178354Ssam nograbkb:
145178354Ssam	gdk_pointer_ungrab(GDK_CURRENT_TIME);
146178354Ssam nograb:
147178354Ssam	if (grab_server)
148178354Ssam		XUngrabServer(GDK_DISPLAY());
149178354Ssam	gnome_dialog_close(GNOME_DIALOG(dialog));
150178354Ssam
151178354Ssam	report_failed_grab();
152178354Ssam	return (-1);
153170530Ssam}
154173273Ssam
155173273Ssamint
156170530Ssammain(int argc, char **argv)
157170530Ssam{
158178354Ssam	char *message;
159178354Ssam	int result;
160178354Ssam
161178354Ssam	gnome_init("GNOME ssh-askpass", "0.1", argc, argv);
162178354Ssam
163173273Ssam	if (argc == 2)
164178354Ssam		message = argv[1];
165178354Ssam	else
166178354Ssam		message = "Enter your OpenSSH passphrase:";
167178354Ssam
168170530Ssam	setvbuf(stdout, 0, _IONBF, 0);
169170530Ssam	result = passphrase_dialog(message);
170178354Ssam
171178354Ssam	return (result);
172178354Ssam}
173178354Ssam