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 (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_label_set_line_wrap(GTK_LABEL((GTK_MESSAGE_DIALOG(dialog))->label), 115 TRUE); 116 117 /* Make <enter> close dialog */ 118 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); 119 g_signal_connect(G_OBJECT(entry), "activate", 120 G_CALLBACK(ok_dialog), dialog); 121 122 /* Grab focus */ 123 gtk_widget_show_now(dialog); 124 if (grab_pointer) { 125 for(;;) { 126 status = gdk_pointer_grab( 127 (GTK_WIDGET(dialog))->window, TRUE, 0, NULL, 128 NULL, GDK_CURRENT_TIME); 129 if (status == GDK_GRAB_SUCCESS) 130 break; 131 usleep(GRAB_WAIT * 1000); 132 if (++grab_tries > GRAB_TRIES) { 133 failed = "mouse"; 134 goto nograb; 135 } 136 } 137 } 138 for(;;) { 139 status = gdk_keyboard_grab((GTK_WIDGET(dialog))->window, 140 FALSE, GDK_CURRENT_TIME); 141 if (status == GDK_GRAB_SUCCESS) 142 break; 143 usleep(GRAB_WAIT * 1000); 144 if (++grab_tries > GRAB_TRIES) { 145 failed = "keyboard"; 146 goto nograbkb; 147 } 148 } 149 if (grab_server) { 150 gdk_x11_grab_server(); 151 } 152 153 result = gtk_dialog_run(GTK_DIALOG(dialog)); 154 155 /* Ungrab */ 156 if (grab_server) 157 XUngrabServer(GDK_DISPLAY()); 158 if (grab_pointer) 159 gdk_pointer_ungrab(GDK_CURRENT_TIME); 160 gdk_keyboard_ungrab(GDK_CURRENT_TIME); 161 gdk_flush(); 162 163 /* Report passphrase if user selected OK */ 164 passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); 165 if (result == GTK_RESPONSE_OK) { 166 local = g_locale_from_utf8(passphrase, strlen(passphrase), 167 NULL, NULL, NULL); 168 if (local != NULL) { 169 puts(local); 170 memset(local, '\0', strlen(local)); 171 g_free(local); 172 } else { 173 puts(passphrase); 174 } 175 } 176 177 /* Zero passphrase in memory */ 178 memset(passphrase, '\b', strlen(passphrase)); 179 gtk_entry_set_text(GTK_ENTRY(entry), passphrase); 180 memset(passphrase, '\0', strlen(passphrase)); 181 g_free(passphrase); 182 183 gtk_widget_destroy(dialog); 184 return (result == GTK_RESPONSE_OK ? 0 : -1); 185 186 /* At least one grab failed - ungrab what we got, and report 187 the failure to the user. Note that XGrabServer() cannot 188 fail. */ 189 nograbkb: 190 gdk_pointer_ungrab(GDK_CURRENT_TIME); 191 nograb: 192 if (grab_server) 193 XUngrabServer(GDK_DISPLAY()); 194 gtk_widget_destroy(dialog); 195 196 report_failed_grab(failed); 197 198 return (-1); 199 } 200 201 int 202 main(int argc, char **argv) 203 { 204 char *message; 205 int result; 206 207 gtk_init(&argc, &argv); 208 209 if (argc > 1) { 210 message = g_strjoinv(" ", argv + 1); 211 } else { 212 message = g_strdup("Enter your OpenSSH passphrase:"); 213 } 214 215 setvbuf(stdout, 0, _IONBF, 0); 216 result = passphrase_dialog(message); 217 g_free(message); 218 219 return (result); 220 } 221