Home | History | Annotate | Download | only in contrib
      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