1180740Sdes/* 2180740Sdes * Copyright (c) 2000-2002 Damien Miller. All rights reserved. 3180740Sdes * 4180740Sdes * Redistribution and use in source and binary forms, with or without 5180740Sdes * modification, are permitted provided that the following conditions 6180740Sdes * are met: 7180740Sdes * 1. Redistributions of source code must retain the above copyright 8180740Sdes * notice, this list of conditions and the following disclaimer. 9180740Sdes * 2. Redistributions in binary form must reproduce the above copyright 10180740Sdes * notice, this list of conditions and the following disclaimer in the 11180740Sdes * documentation and/or other materials provided with the distribution. 12180740Sdes * 13180740Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14180740Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15180740Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16180740Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17180740Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18180740Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19180740Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20180740Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21180740Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22180740Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23180740Sdes */ 24180740Sdes 25180740Sdes/* GTK2 support by Nalin Dahyabhai <nalin@redhat.com> */ 26180740Sdes 27180740Sdes/* 28180740Sdes * This is a simple GNOME SSH passphrase grabber. To use it, set the 29180740Sdes * environment variable SSH_ASKPASS to point to the location of 30180740Sdes * gnome-ssh-askpass before calling "ssh-add < /dev/null". 31180740Sdes * 32180740Sdes * There is only two run-time options: if you set the environment variable 33180740Sdes * "GNOME_SSH_ASKPASS_GRAB_SERVER=true" then gnome-ssh-askpass will grab 34180740Sdes * the X server. If you set "GNOME_SSH_ASKPASS_GRAB_POINTER=true", then the 35180740Sdes * pointer will be grabbed too. These may have some benefit to security if 36180740Sdes * you don't trust your X server. We grab the keyboard always. 37180740Sdes */ 38180740Sdes 39180740Sdes#define GRAB_TRIES 16 40180740Sdes#define GRAB_WAIT 250 /* milliseconds */ 41180740Sdes 42180740Sdes/* 43180740Sdes * Compile with: 44180740Sdes * 45180740Sdes * cc -Wall `pkg-config --cflags gtk+-2.0` \ 46180740Sdes * gnome-ssh-askpass2.c -o gnome-ssh-askpass \ 47180740Sdes * `pkg-config --libs gtk+-2.0` 48180740Sdes * 49180740Sdes */ 50180740Sdes 51180740Sdes#include <stdlib.h> 52180740Sdes#include <stdio.h> 53180740Sdes#include <string.h> 54180740Sdes#include <unistd.h> 55180740Sdes#include <X11/Xlib.h> 56180740Sdes#include <gtk/gtk.h> 57180740Sdes#include <gdk/gdkx.h> 58180740Sdes 59180740Sdesstatic void 60180740Sdesreport_failed_grab (const char *what) 61180740Sdes{ 62180740Sdes GtkWidget *err; 63180740Sdes 64180740Sdes err = gtk_message_dialog_new(NULL, 0, 65180740Sdes GTK_MESSAGE_ERROR, 66180740Sdes GTK_BUTTONS_CLOSE, 67180740Sdes "Could not grab %s. " 68180740Sdes "A malicious client may be eavesdropping " 69180740Sdes "on your session.", what); 70180740Sdes gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER); 71180740Sdes gtk_label_set_line_wrap(GTK_LABEL((GTK_MESSAGE_DIALOG(err))->label), 72180740Sdes TRUE); 73180740Sdes 74180740Sdes gtk_dialog_run(GTK_DIALOG(err)); 75180740Sdes 76180740Sdes gtk_widget_destroy(err); 77180740Sdes} 78180740Sdes 79180740Sdesstatic void 80180740Sdesok_dialog(GtkWidget *entry, gpointer dialog) 81180740Sdes{ 82180740Sdes g_return_if_fail(GTK_IS_DIALOG(dialog)); 83180740Sdes gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); 84180740Sdes} 85180740Sdes 86180740Sdesstatic int 87180740Sdespassphrase_dialog(char *message) 88180740Sdes{ 89180740Sdes const char *failed; 90180740Sdes char *passphrase, *local; 91180740Sdes int result, grab_tries, grab_server, grab_pointer; 92180740Sdes GtkWidget *dialog, *entry; 93180740Sdes GdkGrabStatus status; 94180740Sdes 95180740Sdes grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL); 96180740Sdes grab_pointer = (getenv("GNOME_SSH_ASKPASS_GRAB_POINTER") != NULL); 97180740Sdes grab_tries = 0; 98180740Sdes 99180740Sdes dialog = gtk_message_dialog_new(NULL, 0, 100180740Sdes GTK_MESSAGE_QUESTION, 101180740Sdes GTK_BUTTONS_OK_CANCEL, 102180740Sdes "%s", 103180740Sdes message); 104180740Sdes 105180740Sdes entry = gtk_entry_new(); 106180740Sdes gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, FALSE, 107180740Sdes FALSE, 0); 108180740Sdes gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); 109180740Sdes gtk_widget_grab_focus(entry); 110180740Sdes gtk_widget_show(entry); 111180740Sdes 112180740Sdes gtk_window_set_title(GTK_WINDOW(dialog), "OpenSSH"); 113180740Sdes gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); 114180746Sdes gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); 115180740Sdes gtk_label_set_line_wrap(GTK_LABEL((GTK_MESSAGE_DIALOG(dialog))->label), 116180740Sdes TRUE); 117180740Sdes 118180740Sdes /* Make <enter> close dialog */ 119180740Sdes gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); 120180740Sdes g_signal_connect(G_OBJECT(entry), "activate", 121180740Sdes G_CALLBACK(ok_dialog), dialog); 122180740Sdes 123204861Sdes gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); 124204861Sdes 125180740Sdes /* Grab focus */ 126180740Sdes gtk_widget_show_now(dialog); 127180740Sdes if (grab_pointer) { 128180740Sdes for(;;) { 129180740Sdes status = gdk_pointer_grab( 130180740Sdes (GTK_WIDGET(dialog))->window, TRUE, 0, NULL, 131180740Sdes NULL, GDK_CURRENT_TIME); 132180740Sdes if (status == GDK_GRAB_SUCCESS) 133180740Sdes break; 134180740Sdes usleep(GRAB_WAIT * 1000); 135180740Sdes if (++grab_tries > GRAB_TRIES) { 136180740Sdes failed = "mouse"; 137180740Sdes goto nograb; 138180740Sdes } 139180740Sdes } 140180740Sdes } 141180740Sdes for(;;) { 142180740Sdes status = gdk_keyboard_grab((GTK_WIDGET(dialog))->window, 143180740Sdes FALSE, GDK_CURRENT_TIME); 144180740Sdes if (status == GDK_GRAB_SUCCESS) 145180740Sdes break; 146180740Sdes usleep(GRAB_WAIT * 1000); 147180740Sdes if (++grab_tries > GRAB_TRIES) { 148180740Sdes failed = "keyboard"; 149180740Sdes goto nograbkb; 150180740Sdes } 151180740Sdes } 152180740Sdes if (grab_server) { 153180740Sdes gdk_x11_grab_server(); 154180740Sdes } 155180740Sdes 156180740Sdes result = gtk_dialog_run(GTK_DIALOG(dialog)); 157180740Sdes 158180740Sdes /* Ungrab */ 159180740Sdes if (grab_server) 160180740Sdes XUngrabServer(GDK_DISPLAY()); 161180740Sdes if (grab_pointer) 162180740Sdes gdk_pointer_ungrab(GDK_CURRENT_TIME); 163180740Sdes gdk_keyboard_ungrab(GDK_CURRENT_TIME); 164180740Sdes gdk_flush(); 165180740Sdes 166180740Sdes /* Report passphrase if user selected OK */ 167180740Sdes passphrase = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); 168180740Sdes if (result == GTK_RESPONSE_OK) { 169180740Sdes local = g_locale_from_utf8(passphrase, strlen(passphrase), 170180740Sdes NULL, NULL, NULL); 171180740Sdes if (local != NULL) { 172180740Sdes puts(local); 173180740Sdes memset(local, '\0', strlen(local)); 174180740Sdes g_free(local); 175180740Sdes } else { 176180740Sdes puts(passphrase); 177180740Sdes } 178180740Sdes } 179180740Sdes 180180740Sdes /* Zero passphrase in memory */ 181180740Sdes memset(passphrase, '\b', strlen(passphrase)); 182180740Sdes gtk_entry_set_text(GTK_ENTRY(entry), passphrase); 183180740Sdes memset(passphrase, '\0', strlen(passphrase)); 184180740Sdes g_free(passphrase); 185180740Sdes 186180740Sdes gtk_widget_destroy(dialog); 187180740Sdes return (result == GTK_RESPONSE_OK ? 0 : -1); 188180740Sdes 189180740Sdes /* At least one grab failed - ungrab what we got, and report 190180740Sdes the failure to the user. Note that XGrabServer() cannot 191180740Sdes fail. */ 192180740Sdes nograbkb: 193180740Sdes gdk_pointer_ungrab(GDK_CURRENT_TIME); 194180740Sdes nograb: 195180740Sdes if (grab_server) 196180740Sdes XUngrabServer(GDK_DISPLAY()); 197180740Sdes gtk_widget_destroy(dialog); 198180740Sdes 199180740Sdes report_failed_grab(failed); 200180740Sdes 201180740Sdes return (-1); 202180740Sdes} 203180740Sdes 204180740Sdesint 205180740Sdesmain(int argc, char **argv) 206180740Sdes{ 207180740Sdes char *message; 208180740Sdes int result; 209180740Sdes 210180740Sdes gtk_init(&argc, &argv); 211180740Sdes 212180740Sdes if (argc > 1) { 213180740Sdes message = g_strjoinv(" ", argv + 1); 214180740Sdes } else { 215180740Sdes message = g_strdup("Enter your OpenSSH passphrase:"); 216180740Sdes } 217180740Sdes 218180740Sdes setvbuf(stdout, 0, _IONBF, 0); 219180740Sdes result = passphrase_dialog(message); 220180740Sdes g_free(message); 221180740Sdes 222180740Sdes return (result); 223180740Sdes} 224