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 /* GTK2 support by Nalin Dahyabhai <nalin@redhat.com> */ 26 27 /* 28 * This is a simple GNOME SSH passphrase grabber. To use it, set the 29 * environment variable SSH_ASKPASS to point to the location of 30 * gnome-ssh-askpass before calling "ssh-add < /dev/null". 31 * 32 * There is only two run-time options: if you set the environment variable 33 * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab 34 * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the 35 * pointer will be grabbed too. These may have some benefit to security if 36 * you don't trust your X server. We grab the keyboard always. 37 */ 38 39 #define GRAB_TRIES 16 40 #define GRAB_WAIT 250 /* milliseconds */ 41 42 /* 43 * Compile with: 44 * 45 * cc -Wall `pkg-config --cflags gtk+-2.0` \ 46 * gnome-ssh-askpass2.c -o gnome-ssh-askpass \ 47 * `pkg-config --libs gtk+-2.0` 48 * 49 */ 50 51 #include <stdlib.h> 52 #include <stdio.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <X11/Xlib.h> 56 #include <gtk/gtk.h> 57 #include <gdk/gdkx.h> 58 59 static void 60 report_failed_grab (GtkWidget *parent_window, const char *what) 61 { 62 GtkWidget *err; 63 64 err = gtk_message_dialog_new(GTK_WINDOW(parent_window), 0, 65 GTK_MESSAGE_ERROR, 66 GTK_BUTTONS_CLOSE, 67 "Could not grab %s. " 68 "A malicious client may be eavesdropping " 69 "on your session.", what); 70 gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER); 71 72 gtk_dialog_run(GTK_DIALOG(err)); 73 74 gtk_widget_destroy(err); 75 } 76 77 static void 78 ok_dialog(GtkWidget *entry, gpointer dialog) 79 { 80 g_return_if_fail(GTK_IS_DIALOG(dialog)); 81 gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); 82 } 83 84 static int 85 passphrase_dialog(char *message) 86 { 87 const char *failed; 88 char *passphrase, *local; 89 int result, grab_tries, grab_server, grab_pointer; 90 GtkWidget *parent_window, *dialog, *entry; 91 GdkGrabStatus status; 92 93 grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL); 94 grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL); 95 grab_tries = 0; 96 97 /* Create an invisible parent window so that GtkDialog doesn't 98 * complain. */ 99 parent_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 100 101 dialog = gtk_message_dialog_new(GTK_WINDOW(parent_window), 0, 102 GTK_MESSAGE_QUESTION, 103 GTK_BUTTONS_OK_CANCEL, 104 "%s", 105 message); 106 107 entry = gtk_entry_new(); 108 gtk_box_pack_start( 109 GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), entry, 110 FALSE, FALSE, 0); 111 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); 112 gtk_widget_grab_focus(entry); 113 gtk_widget_show(entry); 114 115 gtk_window_set_title(GTK_WINDOW(dialog), "OpenSSH"); 116 gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); 117 gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); 118 119 /* Make <enter> close dialog */ 120 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); 121 g_signal_connect(G_OBJECT(entry), "activate", 122 G_CALLBACK(ok_dialog), dialog); 123 124 gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); 125 126 /* Grab focus */ 127 gtk_widget_show_now(dialog); 128 if (grab_pointer) { 129 for(;;) { 130 status = gdk_pointer_grab( 131 (gtk_widget_get_window(GTK_WIDGET(dialog))), TRUE, 132 0, NULL, NULL, GDK_CURRENT_TIME); 133 if (status == GDK_GRAB_SUCCESS) 134 break; 135 usleep(GRAB_WAIT * 1000); 136 if (++grab_tries > GRAB_TRIES) { 137 failed = "mouse"; 138 goto nograb; 139 } 140 } 141 } 142 for(;;) { 143 status = gdk_keyboard_grab( 144 gtk_widget_get_window(GTK_WIDGET(dialog)), FALSE, 145 GDK_CURRENT_TIME); 146 if (status == GDK_GRAB_SUCCESS) 147 break; 148 usleep(GRAB_WAIT * 1000); 149 if (++grab_tries > GRAB_TRIES) { 150 failed = "keyboard"; 151 goto nograbkb; 152 } 153 } 154 if (grab_server) { 155 gdk_x11_grab_server(); 156 } 157 158 result = gtk_dialog_run(GTK_DIALOG(dialog)); 159 160 /* Ungrab */ 161 if (grab_server) 162 XUngrabServer(gdk_x11_get_default_xdisplay()); 163 if (grab_pointer) 164 gdk_pointer_ungrab(GDK_CURRENT_TIME); 165 gdk_keyboard_ungrab(GDK_CURRENT_TIME); 166 gdk_flush(); 167 168 /* Report passphrase if user selected OK */ 169 passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); 170 if (result == GTK_RESPONSE_OK) { 171 local = g_locale_from_utf8(passphrase, strlen(passphrase), 172 NULL, NULL, NULL); 173 if (local != NULL) { 174 puts(local); 175 memset(local, '\0', strlen(local)); 176 g_free(local); 177 } else { 178 puts(passphrase); 179 } 180 } 181 182 /* Zero passphrase in memory */ 183 memset(passphrase, '\b', strlen(passphrase)); 184 gtk_entry_set_text(GTK_ENTRY(entry), passphrase); 185 memset(passphrase, '\0', strlen(passphrase)); 186 g_free(passphrase); 187 188 gtk_widget_destroy(dialog); 189 return (result == GTK_RESPONSE_OK ? 0 : -1); 190 191 /* At least one grab failed - ungrab what we got, and report 192 the failure to the user. Note that XGrabServer() cannot 193 fail. */ 194 nograbkb: 195 gdk_pointer_ungrab(GDK_CURRENT_TIME); 196 nograb: 197 if (grab_server) 198 XUngrabServer(gdk_x11_get_default_xdisplay()); 199 gtk_widget_destroy(dialog); 200 201 report_failed_grab(parent_window, failed); 202 203 return (-1); 204 } 205 206 int 207 main(int argc, char **argv) 208 { 209 char *message; 210 int result; 211 212 gtk_init(&argc, &argv); 213 214 if (argc > 1) { 215 message = g_strjoinv(" ", argv + 1); 216 } else { 217 message = g_strdup("Enter your OpenSSH passphrase:"); 218 } 219 220 setvbuf(stdout, 0, _IONBF, 0); 221 result = passphrase_dialog(message); 222 g_free(message); 223 224 return (result); 225 } 226