xref: /freebsd/crypto/openssh/contrib/gnome-ssh-askpass2.c (revision ce3adf4362fcca6a43e500b2531f0038adbfbd21)
1*ce3adf43SDag-Erling Smørgrav /*
2*ce3adf43SDag-Erling Smørgrav  * Copyright (c) 2000-2002 Damien Miller.  All rights reserved.
3*ce3adf43SDag-Erling Smørgrav  *
4*ce3adf43SDag-Erling Smørgrav  * Redistribution and use in source and binary forms, with or without
5*ce3adf43SDag-Erling Smørgrav  * modification, are permitted provided that the following conditions
6*ce3adf43SDag-Erling Smørgrav  * are met:
7*ce3adf43SDag-Erling Smørgrav  * 1. Redistributions of source code must retain the above copyright
8*ce3adf43SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer.
9*ce3adf43SDag-Erling Smørgrav  * 2. Redistributions in binary form must reproduce the above copyright
10*ce3adf43SDag-Erling Smørgrav  *    notice, this list of conditions and the following disclaimer in the
11*ce3adf43SDag-Erling Smørgrav  *    documentation and/or other materials provided with the distribution.
12*ce3adf43SDag-Erling Smørgrav  *
13*ce3adf43SDag-Erling Smørgrav  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14*ce3adf43SDag-Erling Smørgrav  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15*ce3adf43SDag-Erling Smørgrav  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16*ce3adf43SDag-Erling Smørgrav  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17*ce3adf43SDag-Erling Smørgrav  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18*ce3adf43SDag-Erling Smørgrav  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19*ce3adf43SDag-Erling Smørgrav  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20*ce3adf43SDag-Erling Smørgrav  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21*ce3adf43SDag-Erling Smørgrav  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22*ce3adf43SDag-Erling Smørgrav  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23*ce3adf43SDag-Erling Smørgrav  */
24*ce3adf43SDag-Erling Smørgrav 
25*ce3adf43SDag-Erling Smørgrav /* GTK2 support by Nalin Dahyabhai <nalin@redhat.com> */
26*ce3adf43SDag-Erling Smørgrav 
27*ce3adf43SDag-Erling Smørgrav /*
28*ce3adf43SDag-Erling Smørgrav  * This is a simple GNOME SSH passphrase grabber. To use it, set the
29*ce3adf43SDag-Erling Smørgrav  * environment variable SSH_ASKPASS to point to the location of
30*ce3adf43SDag-Erling Smørgrav  * gnome-ssh-askpass before calling "ssh-add < /dev/null".
31*ce3adf43SDag-Erling Smørgrav  *
32*ce3adf43SDag-Erling Smørgrav  * There is only two run-time options: if you set the environment variable
33*ce3adf43SDag-Erling Smørgrav  * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab
34*ce3adf43SDag-Erling Smørgrav  * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the
35*ce3adf43SDag-Erling Smørgrav  * pointer will be grabbed too. These may have some benefit to security if
36*ce3adf43SDag-Erling Smørgrav  * you don't trust your X server. We grab the keyboard always.
37*ce3adf43SDag-Erling Smørgrav  */
38*ce3adf43SDag-Erling Smørgrav 
39*ce3adf43SDag-Erling Smørgrav #define GRAB_TRIES	16
40*ce3adf43SDag-Erling Smørgrav #define GRAB_WAIT	250 /* milliseconds */
41*ce3adf43SDag-Erling Smørgrav 
42*ce3adf43SDag-Erling Smørgrav /*
43*ce3adf43SDag-Erling Smørgrav  * Compile with:
44*ce3adf43SDag-Erling Smørgrav  *
45*ce3adf43SDag-Erling Smørgrav  * cc -Wall `pkg-config --cflags gtk+-2.0` \
46*ce3adf43SDag-Erling Smørgrav  *    gnome-ssh-askpass2.c -o gnome-ssh-askpass \
47*ce3adf43SDag-Erling Smørgrav  *    `pkg-config --libs gtk+-2.0`
48*ce3adf43SDag-Erling Smørgrav  *
49*ce3adf43SDag-Erling Smørgrav  */
50*ce3adf43SDag-Erling Smørgrav 
51*ce3adf43SDag-Erling Smørgrav #include <stdlib.h>
52*ce3adf43SDag-Erling Smørgrav #include <stdio.h>
53*ce3adf43SDag-Erling Smørgrav #include <string.h>
54*ce3adf43SDag-Erling Smørgrav #include <unistd.h>
55*ce3adf43SDag-Erling Smørgrav #include <X11/Xlib.h>
56*ce3adf43SDag-Erling Smørgrav #include <gtk/gtk.h>
57*ce3adf43SDag-Erling Smørgrav #include <gdk/gdkx.h>
58*ce3adf43SDag-Erling Smørgrav 
59*ce3adf43SDag-Erling Smørgrav static void
60*ce3adf43SDag-Erling Smørgrav report_failed_grab (const char *what)
61*ce3adf43SDag-Erling Smørgrav {
62*ce3adf43SDag-Erling Smørgrav 	GtkWidget *err;
63*ce3adf43SDag-Erling Smørgrav 
64*ce3adf43SDag-Erling Smørgrav 	err = gtk_message_dialog_new(NULL, 0,
65*ce3adf43SDag-Erling Smørgrav 				     GTK_MESSAGE_ERROR,
66*ce3adf43SDag-Erling Smørgrav 				     GTK_BUTTONS_CLOSE,
67*ce3adf43SDag-Erling Smørgrav 				     "Could not grab %s. "
68*ce3adf43SDag-Erling Smørgrav 				     "A malicious client may be eavesdropping "
69*ce3adf43SDag-Erling Smørgrav 				     "on your session.", what);
70*ce3adf43SDag-Erling Smørgrav 	gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER);
71*ce3adf43SDag-Erling Smørgrav 	gtk_label_set_line_wrap(GTK_LABEL((GTK_MESSAGE_DIALOG(err))->label),
72*ce3adf43SDag-Erling Smørgrav 				TRUE);
73*ce3adf43SDag-Erling Smørgrav 
74*ce3adf43SDag-Erling Smørgrav 	gtk_dialog_run(GTK_DIALOG(err));
75*ce3adf43SDag-Erling Smørgrav 
76*ce3adf43SDag-Erling Smørgrav 	gtk_widget_destroy(err);
77*ce3adf43SDag-Erling Smørgrav }
78*ce3adf43SDag-Erling Smørgrav 
79*ce3adf43SDag-Erling Smørgrav static void
80*ce3adf43SDag-Erling Smørgrav ok_dialog(GtkWidget *entry, gpointer dialog)
81*ce3adf43SDag-Erling Smørgrav {
82*ce3adf43SDag-Erling Smørgrav 	g_return_if_fail(GTK_IS_DIALOG(dialog));
83*ce3adf43SDag-Erling Smørgrav 	gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
84*ce3adf43SDag-Erling Smørgrav }
85*ce3adf43SDag-Erling Smørgrav 
86*ce3adf43SDag-Erling Smørgrav static int
87*ce3adf43SDag-Erling Smørgrav passphrase_dialog(char *message)
88*ce3adf43SDag-Erling Smørgrav {
89*ce3adf43SDag-Erling Smørgrav 	const char *failed;
90*ce3adf43SDag-Erling Smørgrav 	char *passphrase, *local;
91*ce3adf43SDag-Erling Smørgrav 	int result, grab_tries, grab_server, grab_pointer;
92*ce3adf43SDag-Erling Smørgrav 	GtkWidget *dialog, *entry;
93*ce3adf43SDag-Erling Smørgrav 	GdkGrabStatus status;
94*ce3adf43SDag-Erling Smørgrav 
95*ce3adf43SDag-Erling Smørgrav 	grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL);
96*ce3adf43SDag-Erling Smørgrav 	grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL);
97*ce3adf43SDag-Erling Smørgrav 	grab_tries = 0;
98*ce3adf43SDag-Erling Smørgrav 
99*ce3adf43SDag-Erling Smørgrav 	dialog = gtk_message_dialog_new(NULL, 0,
100*ce3adf43SDag-Erling Smørgrav 					GTK_MESSAGE_QUESTION,
101*ce3adf43SDag-Erling Smørgrav 					GTK_BUTTONS_OK_CANCEL,
102*ce3adf43SDag-Erling Smørgrav 					"%s",
103*ce3adf43SDag-Erling Smørgrav 					message);
104*ce3adf43SDag-Erling Smørgrav 
105*ce3adf43SDag-Erling Smørgrav 	entry = gtk_entry_new();
106*ce3adf43SDag-Erling Smørgrav 	gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, FALSE,
107*ce3adf43SDag-Erling Smørgrav 	    FALSE, 0);
108*ce3adf43SDag-Erling Smørgrav 	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
109*ce3adf43SDag-Erling Smørgrav 	gtk_widget_grab_focus(entry);
110*ce3adf43SDag-Erling Smørgrav 	gtk_widget_show(entry);
111*ce3adf43SDag-Erling Smørgrav 
112*ce3adf43SDag-Erling Smørgrav 	gtk_window_set_title(GTK_WINDOW(dialog), "OpenSSH");
113*ce3adf43SDag-Erling Smørgrav 	gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
114*ce3adf43SDag-Erling Smørgrav 	gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
115*ce3adf43SDag-Erling Smørgrav 	gtk_label_set_line_wrap(GTK_LABEL((GTK_MESSAGE_DIALOG(dialog))->label),
116*ce3adf43SDag-Erling Smørgrav 				TRUE);
117*ce3adf43SDag-Erling Smørgrav 
118*ce3adf43SDag-Erling Smørgrav 	/* Make <enter> close dialog */
119*ce3adf43SDag-Erling Smørgrav 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
120*ce3adf43SDag-Erling Smørgrav 	g_signal_connect(G_OBJECT(entry), "activate",
121*ce3adf43SDag-Erling Smørgrav 			 G_CALLBACK(ok_dialog), dialog);
122*ce3adf43SDag-Erling Smørgrav 
123*ce3adf43SDag-Erling Smørgrav 	gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
124*ce3adf43SDag-Erling Smørgrav 
125*ce3adf43SDag-Erling Smørgrav 	/* Grab focus */
126*ce3adf43SDag-Erling Smørgrav 	gtk_widget_show_now(dialog);
127*ce3adf43SDag-Erling Smørgrav 	if (grab_pointer) {
128*ce3adf43SDag-Erling Smørgrav 		for(;;) {
129*ce3adf43SDag-Erling Smørgrav 			status = gdk_pointer_grab(
130*ce3adf43SDag-Erling Smørgrav 			   (GTK_WIDGET(dialog))->window, TRUE, 0, NULL,
131*ce3adf43SDag-Erling Smørgrav 			   NULL, GDK_CURRENT_TIME);
132*ce3adf43SDag-Erling Smørgrav 			if (status == GDK_GRAB_SUCCESS)
133*ce3adf43SDag-Erling Smørgrav 				break;
134*ce3adf43SDag-Erling Smørgrav 			usleep(GRAB_WAIT * 1000);
135*ce3adf43SDag-Erling Smørgrav 			if (++grab_tries > GRAB_TRIES) {
136*ce3adf43SDag-Erling Smørgrav 				failed = "mouse";
137*ce3adf43SDag-Erling Smørgrav 				goto nograb;
138*ce3adf43SDag-Erling Smørgrav 			}
139*ce3adf43SDag-Erling Smørgrav 		}
140*ce3adf43SDag-Erling Smørgrav 	}
141*ce3adf43SDag-Erling Smørgrav 	for(;;) {
142*ce3adf43SDag-Erling Smørgrav 		status = gdk_keyboard_grab((GTK_WIDGET(dialog))->window,
143*ce3adf43SDag-Erling Smørgrav 		   FALSE, GDK_CURRENT_TIME);
144*ce3adf43SDag-Erling Smørgrav 		if (status == GDK_GRAB_SUCCESS)
145*ce3adf43SDag-Erling Smørgrav 			break;
146*ce3adf43SDag-Erling Smørgrav 		usleep(GRAB_WAIT * 1000);
147*ce3adf43SDag-Erling Smørgrav 		if (++grab_tries > GRAB_TRIES) {
148*ce3adf43SDag-Erling Smørgrav 			failed = "keyboard";
149*ce3adf43SDag-Erling Smørgrav 			goto nograbkb;
150*ce3adf43SDag-Erling Smørgrav 		}
151*ce3adf43SDag-Erling Smørgrav 	}
152*ce3adf43SDag-Erling Smørgrav 	if (grab_server) {
153*ce3adf43SDag-Erling Smørgrav 		gdk_x11_grab_server();
154*ce3adf43SDag-Erling Smørgrav 	}
155*ce3adf43SDag-Erling Smørgrav 
156*ce3adf43SDag-Erling Smørgrav 	result = gtk_dialog_run(GTK_DIALOG(dialog));
157*ce3adf43SDag-Erling Smørgrav 
158*ce3adf43SDag-Erling Smørgrav 	/* Ungrab */
159*ce3adf43SDag-Erling Smørgrav 	if (grab_server)
160*ce3adf43SDag-Erling Smørgrav 		XUngrabServer(GDK_DISPLAY());
161*ce3adf43SDag-Erling Smørgrav 	if (grab_pointer)
162*ce3adf43SDag-Erling Smørgrav 		gdk_pointer_ungrab(GDK_CURRENT_TIME);
163*ce3adf43SDag-Erling Smørgrav 	gdk_keyboard_ungrab(GDK_CURRENT_TIME);
164*ce3adf43SDag-Erling Smørgrav 	gdk_flush();
165*ce3adf43SDag-Erling Smørgrav 
166*ce3adf43SDag-Erling Smørgrav 	/* Report passphrase if user selected OK */
167*ce3adf43SDag-Erling Smørgrav 	passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
168*ce3adf43SDag-Erling Smørgrav 	if (result == GTK_RESPONSE_OK) {
169*ce3adf43SDag-Erling Smørgrav 		local = g_locale_from_utf8(passphrase, strlen(passphrase),
170*ce3adf43SDag-Erling Smørgrav 					   NULL, NULL, NULL);
171*ce3adf43SDag-Erling Smørgrav 		if (local != NULL) {
172*ce3adf43SDag-Erling Smørgrav 			puts(local);
173*ce3adf43SDag-Erling Smørgrav 			memset(local, '\0', strlen(local));
174*ce3adf43SDag-Erling Smørgrav 			g_free(local);
175*ce3adf43SDag-Erling Smørgrav 		} else {
176*ce3adf43SDag-Erling Smørgrav 			puts(passphrase);
177*ce3adf43SDag-Erling Smørgrav 		}
178*ce3adf43SDag-Erling Smørgrav 	}
179*ce3adf43SDag-Erling Smørgrav 
180*ce3adf43SDag-Erling Smørgrav 	/* Zero passphrase in memory */
181*ce3adf43SDag-Erling Smørgrav 	memset(passphrase, '\b', strlen(passphrase));
182*ce3adf43SDag-Erling Smørgrav 	gtk_entry_set_text(GTK_ENTRY(entry), passphrase);
183*ce3adf43SDag-Erling Smørgrav 	memset(passphrase, '\0', strlen(passphrase));
184*ce3adf43SDag-Erling Smørgrav 	g_free(passphrase);
185*ce3adf43SDag-Erling Smørgrav 
186*ce3adf43SDag-Erling Smørgrav 	gtk_widget_destroy(dialog);
187*ce3adf43SDag-Erling Smørgrav 	return (result == GTK_RESPONSE_OK ? 0 : -1);
188*ce3adf43SDag-Erling Smørgrav 
189*ce3adf43SDag-Erling Smørgrav 	/* At least one grab failed - ungrab what we got, and report
190*ce3adf43SDag-Erling Smørgrav 	   the failure to the user.  Note that XGrabServer() cannot
191*ce3adf43SDag-Erling Smørgrav 	   fail.  */
192*ce3adf43SDag-Erling Smørgrav  nograbkb:
193*ce3adf43SDag-Erling Smørgrav 	gdk_pointer_ungrab(GDK_CURRENT_TIME);
194*ce3adf43SDag-Erling Smørgrav  nograb:
195*ce3adf43SDag-Erling Smørgrav 	if (grab_server)
196*ce3adf43SDag-Erling Smørgrav 		XUngrabServer(GDK_DISPLAY());
197*ce3adf43SDag-Erling Smørgrav 	gtk_widget_destroy(dialog);
198*ce3adf43SDag-Erling Smørgrav 
199*ce3adf43SDag-Erling Smørgrav 	report_failed_grab(failed);
200*ce3adf43SDag-Erling Smørgrav 
201*ce3adf43SDag-Erling Smørgrav 	return (-1);
202*ce3adf43SDag-Erling Smørgrav }
203*ce3adf43SDag-Erling Smørgrav 
204*ce3adf43SDag-Erling Smørgrav int
205*ce3adf43SDag-Erling Smørgrav main(int argc, char **argv)
206*ce3adf43SDag-Erling Smørgrav {
207*ce3adf43SDag-Erling Smørgrav 	char *message;
208*ce3adf43SDag-Erling Smørgrav 	int result;
209*ce3adf43SDag-Erling Smørgrav 
210*ce3adf43SDag-Erling Smørgrav 	gtk_init(&argc, &argv);
211*ce3adf43SDag-Erling Smørgrav 
212*ce3adf43SDag-Erling Smørgrav 	if (argc > 1) {
213*ce3adf43SDag-Erling Smørgrav 		message = g_strjoinv(" ", argv + 1);
214*ce3adf43SDag-Erling Smørgrav 	} else {
215*ce3adf43SDag-Erling Smørgrav 		message = g_strdup("Enter your OpenSSH passphrase:");
216*ce3adf43SDag-Erling Smørgrav 	}
217*ce3adf43SDag-Erling Smørgrav 
218*ce3adf43SDag-Erling Smørgrav 	setvbuf(stdout, 0, _IONBF, 0);
219*ce3adf43SDag-Erling Smørgrav 	result = passphrase_dialog(message);
220*ce3adf43SDag-Erling Smørgrav 	g_free(message);
221*ce3adf43SDag-Erling Smørgrav 
222*ce3adf43SDag-Erling Smørgrav 	return (result);
223*ce3adf43SDag-Erling Smørgrav }
224