/*
Silky - A GTK+ client for SILC.
Copyright (C) 2003,2004 Toni Willberg
- SILC keys management functions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
http://silky.sourceforge.net/
*/
#include <glib.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <silcincludes.h>
#include <silcclient.h>
#include <sys/types.h>
#include <dirent.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "support.h"
#include "keys.h"
#include "files.h"
#include "buddylist.h"
#include "ui_console.h"
#include "gui.h"
#include "timer.h" /* silky_progressbar_update() */
#include "log.h"
#include "common.h"
extern silkyStruct *silky;
extern gchar *silkydir;
extern gchar *pubkey; /* own key */
extern gchar *privkey; /* own key */
extern gchar *silkypath; /* from main.c */
extern GladeXML *xmlmain;
/* (internal) Utility function to check if received public key is already stored.
* Returns TRUE if key exists, FALSE if key is yet unknown.
*/
gboolean _silky_check_dir_for_key(const char * dir, SilcPublicKey pkey) {
DIR *pubkeydir;
struct dirent *keytypename = NULL;
SilcPublicKey lkey;
struct stat sb;
int ret = 0;
char fpath[256];
gboolean try;
debug("Checking for peer's existing SILC public key from '%s'...", dir);
/* Open directory for reading */
pubkeydir = opendir(dir);
if (pubkeydir == NULL) {
debug("no such dir");
return FALSE;
}
/* Init keytypename so it's not NULL */
keytypename = readdir(pubkeydir);
/* Read whole directory */
while (keytypename != NULL) {
keytypename = readdir(pubkeydir);
if (keytypename != NULL) {
/* Create path to file */
memset(fpath, 0, 256);
g_snprintf(fpath, 256, "%s%c%s", dir, DIR_SEPARATOR, keytypename->d_name);
debug("statting '%s'", fpath);
/* Check filetype */
#ifdef SILKY_WIN32
ret = stat(fpath, &sb);
#else
ret = lstat(fpath, &sb);
#endif
if (S_ISREG(sb.st_mode)) {
/* Regular file, let's try to load a SILC public key */
try = silc_pkcs_load_public_key(fpath, &lkey, SILC_PKCS_FILE_PEM);
if (!try)
try = silc_pkcs_load_public_key(fpath, &lkey, SILC_PKCS_FILE_BIN);
if (try) {
debug("found key");
/* Okay, it really was a SILC public key */
if(silc_pkcs_public_key_compare(pkey, lkey)) {
debug("Key %s matches", fpath);
closedir(pubkeydir);
return TRUE;
} else {
debug("key %s doesn't match", fpath);
}
} else {
debug("didn't find key");
}
}
}
}
closedir(pubkeydir);
debug("Received unknown key");
return FALSE;
}
/* (internal) Utility function to check if received public key is already stored.
* Returns TRUE if key exists, FALSE if key is yet unknown.
*/
gboolean silky_known_server_key(SilcPublicKey pkey) {
gboolean ret;
char pkdir[256];
memset(pkdir, 0, 256);
snprintf(pkdir, 256, "%s%cserverkeys", silkydir, DIR_SEPARATOR);
ret = _silky_check_dir_for_key(pkdir, pkey);
return ret;
}
gboolean silky_known_client_key(SilcPublicKey pkey) {
gboolean ret;
char pkdir[256];
memset(pkdir, 0, 256);
snprintf(pkdir, 256, "%s%cclientkeys", silkydir, DIR_SEPARATOR);
ret = _silky_check_dir_for_key(pkdir, pkey);
return ret;
}
/* this is called when generating keys, as a separate thread */
void silky_keygen (SilkySilcKey *key) {
gint ret;
/* create the key */
debug("silky_keygen [thread]");
debug("calling silc_create_key_pair()");
ret = silc_create_key_pair(key->cipher, key->length,
pubkey, privkey, /* global variables/pointers */
key->identifier, key->passphrase,
&silky->client->pkcs,
&silky->client->public_key,
&silky->client->private_key, FALSE);
debug("returned from silc_create_key_pair()");
if (ret) {
debug("success");
}
else {
debug("failed");
}
debug("setting key->ready = TRUE");
key->ready = TRUE; /* tell the caller we are ready */
debug("calling g_thread_exit()");
/* commit suicide */
g_thread_exit(&ret);
}
/*
Asks user if he wants to accept public key,
and saves it
*/
gboolean silky_save_public_key(SilcClient client, SilcClientConnection conn,
const char *name, SilcSocketType conn_type,
unsigned char *pk, SilcUInt32 pk_len,
SilcSKEPKType pk_type,
SilcVerifyPublicKey completion, void *context)
{
const gchar *keyfile;
GtkLabel *accept_server_key_label;
GtkWidget *dialog;
gchar *fingerprint = NULL;
gchar *babbleprint = NULL;
gint result;
SilcPublicKey pubkey;
unsigned char *encpk;
char *keytypename = NULL;
/*
Verify the key type
*/
if (pk_type != SILC_SKE_PK_TYPE_SILC) {
printconsole(_("Received unknown key type."));
if (completion) {
completion(FALSE, context);
}
return FALSE;
}
/*
Type of the key entity
*/
if (conn_type == SILC_SOCKET_TYPE_SERVER || conn_type == SILC_SOCKET_TYPE_ROUTER) {
keytypename = strdup("server");
}
if (conn_type == SILC_SOCKET_TYPE_CLIENT) {
keytypename = strdup("client");
}
debug("key type: %s (%d)", keytypename, conn_type);
/* replace spaces with underscores */
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
// fingerprint = g_strdelimit(fingerprint, " ", '_');
/* construct keyfile path */
keyfile = gen_keyfile_path(fingerprint, keytypename);
// keyfile = g_strdup_printf("%s%c%skeys%c%skey_%s.pub", silkydir, DIR_SEPARATOR, keytypename, DIR_SEPARATOR, keytypename, fingerprint);
g_free(fingerprint);
/* Check whether this key already exists */
if ( !g_file_test(keyfile, G_FILE_TEST_IS_REGULAR)) { /* require a regular file, not symlink */
/* no such file */
/* Decode new key */
if ( !silc_pkcs_public_key_decode(pk, pk_len, &pubkey) ) {
printconsole(_("Could not decode received public key."));
return FALSE;
}
/* Encode new key */
encpk = silc_pkcs_public_key_encode(pubkey, &pk_len);
if (!encpk) {
printconsole(_("Could not encode received public key."));
silc_pkcs_public_key_free(pubkey);
return FALSE;
}
else {
/* does the user accept the key, before saving? */
/* create dialog */
dialog = glade_xml_get_widget (xmlmain, "dialog_accept_new_key");
/* add text to label */
accept_server_key_label = GTK_LABEL(glade_xml_get_widget (xmlmain, "accept_new_key_text"));
fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
gtk_label_set_text(GTK_LABEL(accept_server_key_label), g_strdup_printf("%s\n%s\n", fingerprint, babbleprint) );
g_free(fingerprint);
g_free(babbleprint);
/* show the dialog, blocking UI */
debug("showing dialog...");
gdk_threads_enter();
result = gtk_dialog_run (GTK_DIALOG(dialog));
gdk_threads_leave();
debug("returned from the dialog");
gtk_widget_hide (GTK_WIDGET(dialog));
if (result == GTK_RESPONSE_OK) {
debug("user accepted, saving '%s'", keyfile);
silc_pkcs_save_public_key(keyfile, pubkey, SILC_PKCS_FILE_PEM);
silc_pkcs_public_key_free(pubkey);
printconsole(_("Key saved.")); /* was it? */
buddy_set_keyverified_by_fp(fingerprint, 1); /* set buddylist parameter */
return TRUE;
}
else {
debug("user didn't accept the key, not saved");
buddy_set_keyverified_by_fp(fingerprint, -1); /* set buddylist parameter */
silc_pkcs_public_key_free(pubkey);
return FALSE;
}
} /* if (!encpk) */
}
else {
/* key exists, now verify it */
/* compare existing keys */
// bool silc_pkcs_public_key_compare(SilcPublicKey key1, SilcPublicKey key2);
debug("known key");
printconsole(_("This key is already saved."));
/* FIXME: verify that the file matches the key! */
buddy_set_keyverified_by_fp(fingerprint, 1); /* set buddylist parameter */
return TRUE;
}
}
/*
generates full path to a keyfile of given fingerprint
*/
const gchar *gen_keyfile_path(const gchar *fingerprint, const gchar *keytype) {
const gchar *keyfile;
gchar *_fingerprint = g_strdup(fingerprint); /* we need a local copy */
g_strdelimit(_fingerprint, " ", '_');
keyfile = g_strdup_printf("%s%c%skeys%c%skey_%s.pub", silkydir, DIR_SEPARATOR, keytype, DIR_SEPARATOR, keytype, _fingerprint);
debug("keyfile path: '%s'", keyfile);
g_free(_fingerprint);
return keyfile;
}
/*
runs after user fills fields to the keygen form,
*/
gint silky_keygen_ui () {
guint progress_timeout;
GtkEntry *ekey_name;
GtkEntry *ekey_email;
gchar *key_passphrase1;
GtkEntry *ekey_passphrase1;
gchar *key_passphrase2;
GtkEntry *ekey_passphrase2;
GtkWidget *dialog_keygen_progressbar;
GtkWidget *keygen_progressbar;
GThread *thread_id;
SilkySilcKey key;
// gtk_dialog_run(GTK_DIALOG(glade_xml_get_widget (xmlmain, "dialog_firstrun_manual")));
/* fixme; check ret and return -1 if user wants to cancel! */
/* hide the dialogs */
// gtk_widget_hide(glade_xml_get_widget (xmlmain, "dialog_firstrun"));
// gtk_widget_hide(glade_xml_get_widget (xmlmain, "dialog_firstrun_manual"));
/* get user's data */
ekey_name = GTK_ENTRY(glade_xml_get_widget (xmlmain, "fr_key_name"));
key.name = strdup(gtk_entry_get_text(ekey_name));
ekey_email = GTK_ENTRY(glade_xml_get_widget (xmlmain, "fr_key_email"));
key.email = strdup(gtk_entry_get_text(ekey_email));
ekey_passphrase1 = GTK_ENTRY(glade_xml_get_widget (xmlmain, "fr_key_passphrase1"));
key_passphrase1 = strdup(gtk_entry_get_text(ekey_passphrase1));
ekey_passphrase2 = GTK_ENTRY(glade_xml_get_widget (xmlmain, "fr_key_passphrase2"));
key_passphrase2 = strdup(gtk_entry_get_text(ekey_passphrase2));
if (!strcmp (key_passphrase1, key_passphrase2) == 0) {
debug("pass wrong");
errordialog(_("Passphrases don't match! Try again."));
memset(key_passphrase1, 0, sizeof(key_passphrase1));
memset(key_passphrase2, 0, sizeof(key_passphrase2));
free(key_passphrase1); free(key_passphrase2);
return 0; /* go back to idle mode, gtk will call us again */
}
else {
debug("pass equals, continuing");
}
key.passphrase = g_strdup(key_passphrase1);
memset(key_passphrase1, 0, sizeof(key_passphrase1));
memset(key_passphrase2, 0, sizeof(key_passphrase2));
free(key_passphrase1); free(key_passphrase2);
/* generate identifier, 'RN=real name, HN=localhost, E=email@address' */
key.identifier = g_strconcat("RN=", key.name, ", HN=localhost, E=", key.email , NULL);
debug("Using identifier: '%s'", key.identifier);
/* verify that the keys are there */
if (!pubkey || !privkey) {
g_error("can't find the key file names!");
/* aborts */
}
/* show the progress dialog */
keygen_progressbar = glade_xml_get_widget (xmlmain, "keygen_progressbar");
progress_timeout = gtk_timeout_add(50, (GSourceFunc)silky_progressbar_update, keygen_progressbar); /* update the bar every X second */
dialog_keygen_progressbar = glade_xml_get_widget (xmlmain, "dialog_keygen_progress");
key.length = (gint)g_strtod(gtk_entry_get_text(GTK_ENTRY(glade_xml_get_widget (xmlmain, "fr_key_length"))), NULL);
key.cipher = g_strdup("rsa"); /* fixme; put this into the ui */
debug("calling g_thread_create(silky_keygen)");
thread_id = g_thread_create( (GThreadFunc)silky_keygen, &key, TRUE, NULL);
/* this will block until silky_keygen() enables the OK button in the dialog */
debug("showing progress dialog");
/* I18N This is a window title */
gtk_window_set_title(GTK_WINDOW(dialog_keygen_progressbar), g_strdup_printf("Silky v%s : %s", SILKY_VERSION, _("Generating SILC keys...")));
gtk_widget_show_all (dialog_keygen_progressbar);
/* we sit here and wait */
while (key.ready != TRUE) {
gtk_main_iteration_do(FALSE); /* give GTK some CPU time, non-blocking call */
}
debug("key.ready was set TRUE");
/* stop updating the bar */
gtk_timeout_remove(progress_timeout);
if (!GTK_IS_PROGRESS_BAR(keygen_progressbar)) {
/* this happens if user destroys the progress dialog while we generate the key,
quitting */
debug("progress dialog destroyed by user, returning -1");
return -1;
}
/* set text and fraction */
gtk_progress_bar_set_text( GTK_PROGRESS_BAR(keygen_progressbar), N_("Ready! Click OK to continue."));
gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR(keygen_progressbar), 1 ); /* set 100% */
gtk_widget_set_sensitive(glade_xml_get_widget (xmlmain, "button_keygen_ok"), TRUE);
debug("now waiting for the user");
while (gtk_events_pending ()) {
gtk_main_iteration ();
}
/* lets wait until user presses OK button */
gtk_dialog_run (GTK_DIALOG (dialog_keygen_progressbar));
debug("dialog returned");
/* hide the dialog window */
gtk_widget_hide(dialog_keygen_progressbar); /* get rid of the window */
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1