1 /*
2  * Copyright (c) 2000-2002 Damien Miller.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 /*
26  * This is a simple GNOME SSH passphrase grabber. To use it, set the
27  * environment variable SSH_ASKPASS to point to the location of
28  * gnome-ssh-askpass before calling "ssh-add < /dev/null".
29  *
30  * There is only two run-time options: if you set the environment variable
31  * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab
32  * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the
33  * pointer will be grabbed too. These may have some benefit to security if
34  * you don't trust your X server. We grab the keyboard always.
35  */
36 
37 /*
38  * Compile with:
39  *
40  * cc `gnome-config --cflags gnome gnomeui` \
41  *    gnome-ssh-askpass1.c -o gnome-ssh-askpass \
42  *    `gnome-config --libs gnome gnomeui`
43  *
44  */
45 
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <gnome.h>
50 #include <X11/Xlib.h>
51 #include <gdk/gdkx.h>
52 
53 void
54 report_failed_grab (void)
55 {
56 	GtkWidget *err;
57 
58 	err = gnome_message_box_new("Could not grab keyboard or mouse.\n"
59 		"A malicious client may be eavesdropping on your session.",
60 				    GNOME_MESSAGE_BOX_ERROR, "EXIT", NULL);
61 	gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER);
62 	gtk_object_set(GTK_OBJECT(err), "type", GTK_WINDOW_POPUP, NULL);
63 
64 	gnome_dialog_run_and_close(GNOME_DIALOG(err));
65 }
66 
67 int
68 passphrase_dialog(char *message)
69 {
70 	char *passphrase;
71 	char **messages;
72 	int result, i, grab_server, grab_pointer;
73 	GtkWidget *dialog, *entry, *label;
74 
75 	grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL);
76 	grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL);
77 
78 	dialog = gnome_dialog_new("OpenSSH", GNOME_STOCK_BUTTON_OK,
79 	    GNOME_STOCK_BUTTON_CANCEL, NULL);
80 
81 	messages = g_strsplit(message, "\\n", 0);
82 	if (messages)
83 		for(i = 0; messages[i]; i++) {
84 			label = gtk_label_new(messages[i]);
85 			gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox),
86 			    label, FALSE, FALSE, 0);
87 		}
88 
89 	entry = gtk_entry_new();
90 	gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox), entry, FALSE,
91 	    FALSE, 0);
92 	gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
93 	gtk_widget_grab_focus(entry);
94 
95 	/* Center window and prepare for grab */
96 	gtk_object_set(GTK_OBJECT(dialog), "type", GTK_WINDOW_POPUP, NULL);
97 	gnome_dialog_set_default(GNOME_DIALOG(dialog), 0);
98 	gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
99 	gtk_window_set_policy(GTK_WINDOW(dialog), FALSE, FALSE, TRUE);
100 	gnome_dialog_close_hides(GNOME_DIALOG(dialog), TRUE);
101 	gtk_container_set_border_width(GTK_CONTAINER(GNOME_DIALOG(dialog)->vbox),
102 	    GNOME_PAD);
103 	gtk_widget_show_all(dialog);
104 
105 	/* Grab focus */
106 	if (grab_server)
107 		XGrabServer(GDK_DISPLAY());
108 	if (grab_pointer && gdk_pointer_grab(dialog->window, TRUE, 0,
109 	    NULL, NULL, GDK_CURRENT_TIME))
110 		goto nograb;
111 	if (gdk_keyboard_grab(dialog->window, FALSE, GDK_CURRENT_TIME))
112 		goto nograbkb;
113 
114 	/* Make <enter> close dialog */
115 	gnome_dialog_editable_enters(GNOME_DIALOG(dialog), GTK_EDITABLE(entry));
116 
117 	/* Run dialog */
118 	result = gnome_dialog_run(GNOME_DIALOG(dialog));
119 
120 	/* Ungrab */
121 	if (grab_server)
122 		XUngrabServer(GDK_DISPLAY());
123 	if (grab_pointer)
124 		gdk_pointer_ungrab(GDK_CURRENT_TIME);
125 	gdk_keyboard_ungrab(GDK_CURRENT_TIME);
126 	gdk_flush();
127 
128 	/* Report passphrase if user selected OK */
129 	passphrase = gtk_entry_get_text(GTK_ENTRY(entry));
130 	if (result == 0)
131 		puts(passphrase);
132 
133 	/* Zero passphrase in memory */
134 	memset(passphrase, '\0', strlen(passphrase));
135 	gtk_entry_set_text(GTK_ENTRY(entry), passphrase);
136 
137 	gnome_dialog_close(GNOME_DIALOG(dialog));
138 	return (result == 0 ? 0 : -1);
139 
140 	/* At least one grab failed - ungrab what we got, and report
141 	   the failure to the user.  Note that XGrabServer() cannot
142 	   fail.  */
143  nograbkb:
144 	gdk_pointer_ungrab(GDK_CURRENT_TIME);
145  nograb:
146 	if (grab_server)
147 		XUngrabServer(GDK_DISPLAY());
148 	gnome_dialog_close(GNOME_DIALOG(dialog));
149 
150 	report_failed_grab();
151 	return (-1);
152 }
153 
154 int
155 main(int argc, char **argv)
156 {
157 	char *message;
158 	int result;
159 
160 	gnome_init("GNOME ssh-askpass", "0.1", argc, argv);
161 
162 	if (argc == 2)
163 		message = argv[1];
164 	else
165 		message = "Enter your OpenSSH passphrase:";
166 
167 	setvbuf(stdout, 0, _IONBF, 0);
168 	result = passphrase_dialog(message);
169 
170 	return (result);
171 }
172