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 (at) 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 (const char *what) 61 { 62 GtkWidget *err; 63 64 err = gtk_message_dialog_new(NULL, 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 gtk_label_set_line_wrap(GTK_LABEL((GTK_MESSAGE_DIALOG(err))->label), 72 TRUE); 73 74 gtk_dialog_run(GTK_DIALOG(err)); 75 76 gtk_widget_destroy(err); 77 } 78 79 static void 80 ok_dialog(GtkWidget *entry, gpointer dialog) 81 { 82 g_return_if_fail(GTK_IS_DIALOG(dialog)); 83 gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); 84 } 85 86 static int 87 passphrase_dialog(char *message) 88 { 89 const char *failed; 90 char *passphrase, *local; 91 int result, grab_tries, grab_server, grab_pointer; 92 GtkWidget *dialog, *entry; 93 GdkGrabStatus status; 94 95 grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL); 96 grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL); 97 grab_tries = 0; 98 99 dialog = gtk_message_dialog_new(NULL, 0, 100 GTK_MESSAGE_QUESTION, 101 GTK_BUTTONS_OK_CANCEL, 102 "%s", 103 message); 104 105 entry = gtk_entry_new(); 106 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, FALSE, 107 FALSE, 0); 108 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); 109 gtk_widget_grab_focus(entry); 110 gtk_widget_show(entry); 111 112 gtk_window_set_title(GTK_WINDOW(dialog), "OpenSSH"); 113 gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); 114 gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); 115 gtk_label_set_line_wrap(GTK_LABEL((GTK_MESSAGE_DIALOG(dialog))->label), 116 TRUE); 117 118 /* Make <enter> close dialog */ 119 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); 120 g_signal_connect(G_OBJECT(entry), "activate", 121 G_CALLBACK(ok_dialog), dialog); 122 123 gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); 124 125 /* Grab focus */ 126 gtk_widget_show_now(dialog); 127 if (grab_pointer) { 128 for(;;) { 129 status = gdk_pointer_grab( 130 (GTK_WIDGET(dialog))->window, TRUE, 0, NULL, 131 NULL, GDK_CURRENT_TIME); 132 if (status == GDK_GRAB_SUCCESS) 133 break; 134 usleep(GRAB_WAIT * 1000); 135 if (++grab_tries > GRAB_TRIES) { 136 failed = "mouse"; 137 goto nograb; 138 } 139 } 140 } 141 for(;;) { 142 status = gdk_keyboard_grab((GTK_WIDGET(dialog))->window, 143 FALSE, GDK_CURRENT_TIME); 144 if (status == GDK_GRAB_SUCCESS) 145 break; 146 usleep(GRAB_WAIT * 1000); 147 if (++grab_tries > GRAB_TRIES) { 148 failed = "keyboard"; 149 goto nograbkb; 150 } 151 } 152 if (grab_server) { 153 gdk_x11_grab_server(); 154 } 155 156 result = gtk_dialog_run(GTK_DIALOG(dialog)); 157 158 /* Ungrab */ 159 if (grab_server) 160 XUngrabServer(GDK_DISPLAY()); 161 if (grab_pointer) 162 gdk_pointer_ungrab(GDK_CURRENT_TIME); 163 gdk_keyboard_ungrab(GDK_CURRENT_TIME); 164 gdk_flush(); 165 166 /* Report passphrase if user selected OK */ 167 passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); 168 if (result == GTK_RESPONSE_OK) { 169 local = g_locale_from_utf8(passphrase, strlen(passphrase), 170 NULL, NULL, NULL); 171 if (local != NULL) { 172 puts(local); 173 memset(local, '\0', strlen(local)); 174 g_free(local); 175 } else { 176 puts(passphrase); 177 } 178 } 179 180 /* Zero passphrase in memory */ 181 memset(passphrase, '\b', strlen(passphrase)); 182 gtk_entry_set_text(GTK_ENTRY(entry), passphrase); 183 memset(passphrase, '\0', strlen(passphrase)); 184 g_free(passphrase); 185 186 gtk_widget_destroy(dialog); 187 return (result == GTK_RESPONSE_OK ? 0 : -1); 188 189 /* At least one grab failed - ungrab what we got, and report 190 the failure to the user. Note that XGrabServer() cannot 191 fail. */ 192 nograbkb: 193 gdk_pointer_ungrab(GDK_CURRENT_TIME); 194 nograb: 195 if (grab_server) 196 XUngrabServer(GDK_DISPLAY()); 197 gtk_widget_destroy(dialog); 198 199 report_failed_grab(failed); 200 201 return (-1); 202 } 203 204 int 205 main(int argc, char **argv) 206 { 207 char *message; 208 int result; 209 210 gtk_init(&argc, &argv); 211 212 if (argc > 1) { 213 message = g_strjoinv(" ", argv + 1); 214 } else { 215 message = g_strdup("Enter your OpenSSH passphrase:"); 216 } 217 218 setvbuf(stdout, 0, _IONBF, 0); 219 result = passphrase_dialog(message); 220 g_free(message); 221 222 return (result); 223 } 224