[pan2/testing: 226/279] * added CertStore for ssl certificates * added callbacks for certificate verification * added remove
- From: Heinrich MÃller <henmull src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pan2/testing: 226/279] * added CertStore for ssl certificates * added callbacks for certificate verification * added remove
- Date: Sat, 3 Dec 2011 22:40:34 +0000 (UTC)
commit eb2516e8c5b565d8c10fbb48a76d9cfe083b4d5b
Author: Heinrich MÃller <sphemuel stud informatik uni-erlangen de>
Date: Mon Nov 7 09:18:02 2011 +0100
* added CertStore for ssl certificates
* added callbacks for certificate verification
* added remove/add/inspect cert functionality in GUI
* added 'urgent' warnings for suspicious behaviour of the server when using SSL connections
* added GUI info dialog for adding new certs to Store
pan.cbp | 3 +
pan/data-impl/Makefile.am | 43 +++---
pan/data-impl/add-server.cc | 12 +-
pan/data-impl/data-impl.h | 10 +
pan/general/file-util.cc | 2 +-
pan/gui/actions.cc | 6 +
pan/gui/gui.cc | 134 ++++++++++++----
pan/gui/gui.h | 13 ++-
pan/gui/pan-ui.h | 1 +
pan/gui/pan.cc | 18 ++-
pan/gui/pan.ui.h | 1 +
pan/gui/post-ui.cc | 81 +++++-----
pan/gui/server-ui.cc | 336 +++++++++++++++++++++++++++++++++++++-
pan/gui/server-ui.h | 18 ++
pan/gui/task-pane.cc | 3 +-
pan/icons/Makefile.am | 4 +-
pan/icons/icon_cert.png | Bin 0 -> 689 bytes
pan/icons/icon_plain.png | Bin 0 -> 687 bytes
pan/icons/icon_tabbar.png | Bin 0 -> 594 bytes
pan/tasks/Makefile.am | 6 +-
pan/tasks/cert-store.cc | 266 ++++++++++++++++++++++++++++++
pan/tasks/cert-store.h | 121 ++++++++++++++
pan/tasks/nntp-pool.cc | 30 +++-
pan/tasks/nntp-pool.h | 18 ++-
pan/tasks/queue.cc | 6 +-
pan/tasks/queue.h | 10 +-
pan/tasks/socket-impl-gio.h | 3 +-
pan/tasks/socket-impl-main.cc | 35 +++-
pan/tasks/socket-impl-main.h | 23 ++-
pan/tasks/socket-impl-openssl.cc | 146 +++++++++--------
pan/tasks/socket-impl-openssl.h | 29 ++--
pan/tasks/socket.h | 10 +
pan/usenet-utils/Makefile.am | 3 +-
33 files changed, 1164 insertions(+), 227 deletions(-)
---
diff --git a/pan.cbp b/pan.cbp
index 3d72e28..300777c 100644
--- a/pan.cbp
+++ b/pan.cbp
@@ -203,6 +203,8 @@
<Unit filename="pan/tasks/adaptable-set-test.cc" />
<Unit filename="pan/tasks/adaptable-set.cc" />
<Unit filename="pan/tasks/adaptable-set.h" />
+ <Unit filename="pan/tasks/cert-store.cc" />
+ <Unit filename="pan/tasks/cert-store.h" />
<Unit filename="pan/tasks/decoder.cc" />
<Unit filename="pan/tasks/decoder.h" />
<Unit filename="pan/tasks/defgroup.h" />
@@ -264,6 +266,7 @@
<Unit filename="pan/usenet-utils/scorefile-test.cc" />
<Unit filename="pan/usenet-utils/scorefile.cc" />
<Unit filename="pan/usenet-utils/scorefile.h" />
+ <Unit filename="pan/usenet-utils/ssl-utils.h" />
<Unit filename="pan/usenet-utils/text-massager-test.cc" />
<Unit filename="pan/usenet-utils/text-massager.cc" />
<Unit filename="pan/usenet-utils/text-massager.h" />
diff --git a/pan/data-impl/Makefile.am b/pan/data-impl/Makefile.am
index 19142bf..c20532d 100644
--- a/pan/data-impl/Makefile.am
+++ b/pan/data-impl/Makefile.am
@@ -1,4 +1,4 @@
-AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@
+AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@
noinst_LIBRARIES = libpandata.a
@@ -24,26 +24,25 @@ noinst_HEADERS = \
profiles.h \
memchunk.h
-#noinst_PROGRAMS = \
-# add-server \
-# headers-test \
-# speed-test-load-group
+noinst_PROGRAMS = \
+ add-server \
+ headers-test \
+ speed-test-load-group
-#TESTS = \
-# add-server \
-# headers-test
+TESTS = \
+ add-server \
+ headers-test
-#TEST_LDADD = \
-# ./libpandata.a \
-# ../tasks/libtasks.a \
-# ../data/libdata.a \
-# ../usenet-utils/libusenetutils.a \
-# ../general/libgeneralutils.a \
-# ../../uulib/libuu.a \
-# @GMIME_LIBS@ @GLIB_LIBS@
-#add_server_SOURCES = add-server.cc
-#add_server_LDADD = $(TEST_LDADD)
-#headers_test_SOURCES = headers-test.cc
-#headers_test_LDADD = $(TEST_LDADD)
-#speed_test_load_group_SOURCES = speed-test-load-group.cc
-#speed_test_load_group_LDADD = $(TEST_LDADD)
+TEST_LDADD = \
+ ./libpandata.a \
+ ../tasks/libtasks.a \
+ ../data/libdata.a \
+ ../usenet-utils/libusenetutils.a \
+ ../general/libgeneralutils.a \
+ ../../uulib/libuu.a @GMIME_LIBS@ @GLIB_LIBS@ @OPENSSL_LIBS@
+add_server_SOURCES = add-server.cc
+add_server_LDADD = $(TEST_LDADD)
+headers_test_SOURCES = headers-test.cc
+headers_test_LDADD = $(TEST_LDADD)
+speed_test_load_group_SOURCES = speed-test-load-group.cc
+speed_test_load_group_LDADD = $(TEST_LDADD)
diff --git a/pan/data-impl/add-server.cc b/pan/data-impl/add-server.cc
index 3ae41b0..d5e82ac 100644
--- a/pan/data-impl/add-server.cc
+++ b/pan/data-impl/add-server.cc
@@ -5,9 +5,12 @@
#include <pan/tasks/queue.h>
#include <pan/tasks/socket-impl-gio.h>
#include <pan/tasks/task-groups.h>
-#include <pan/tasks/socket-impl-main.h>
+#include <pan/tasks/socket.h>
#include "data-impl.h"
+
+//FIXME adapt to SocketCreator & SSL-Support!
+
using namespace pan;
namespace
@@ -58,10 +61,11 @@ int main (int argc, char *argv[])
// initialize the queue
TaskArchive null_task_archive;
WorkerPool pool;
+ CertStore cs(0);
- // FIXME : DBG!
-// SocketCreator _socket_creator;
-// Queue queue (data, null_task_archive, &_socket_creator, pool, true, 10);
+ // FIXME : DBG!
+// SocketCreator _socket_creator(cs);
+// Queue queue (data, null_task_archive, &_socket_creator, cs, pool, true, 10);
// queue.add_task (new TaskGroups (data, servername));
//
// // start the event loop...
diff --git a/pan/data-impl/data-impl.h b/pan/data-impl/data-impl.h
index fb27ba0..09549a2 100644
--- a/pan/data-impl/data-impl.h
+++ b/pan/data-impl/data-impl.h
@@ -44,6 +44,16 @@
#include <pan/data-impl/profiles.h>
#include <pan/data-impl/memchunk.h>
+#ifdef HAVE_OPENSSL
+ #include <pan/tasks/cert-store.h>
+ #include <openssl/crypto.h>
+ #include <openssl/x509.h>
+ #include <openssl/x509v3.h>
+ #include <openssl/pem.h>
+ #include <openssl/ssl.h>
+ #include <openssl/err.h>
+#endif
+
namespace pan
{
typedef std::vector<const Article*> articles_t;
diff --git a/pan/general/file-util.cc b/pan/general/file-util.cc
index 2e08bc2..6b3ea29 100644
--- a/pan/general/file-util.cc
+++ b/pan/general/file-util.cc
@@ -66,7 +66,7 @@ file :: get_pan_home ()
}
}
- file :: ensure_dir_exists (pan_home);
+ ensure_dir_exists (pan_home);
return pan_home;
}
diff --git a/pan/gui/actions.cc b/pan/gui/actions.cc
index 187eb7f..72db18f 100644
--- a/pan/gui/actions.cc
+++ b/pan/gui/actions.cc
@@ -141,6 +141,7 @@ namespace
void do_read_previous_thread (GtkAction*) { pan_ui->do_read_previous_thread(); }
void do_read_parent_article (GtkAction*) { pan_ui->do_read_parent_article(); }
void do_show_servers_dialog (GtkAction*) { pan_ui->do_show_servers_dialog(); }
+ void do_show_sec_dialog (GtkAction*) { pan_ui->do_show_sec_dialog(); }
void do_plonk (GtkAction*) { pan_ui->do_plonk(); }
void do_ignore (GtkAction*) { pan_ui->do_ignore(); }
void do_watch (GtkAction*) { pan_ui->do_watch(); }
@@ -415,6 +416,11 @@ namespace
NULL,
G_CALLBACK(do_show_servers_dialog) },
+ { "show-sec-dialog", GTK_STOCK_DIALOG_AUTHENTICATION,
+ N_("Edit _SSL Certificates"), NULL,
+ NULL,
+ G_CALLBACK(do_show_sec_dialog) },
+
{ "jump-to-group-tab", GTK_STOCK_JUMP_TO,
N_("Jump to _Group Tab"), "1",
NULL,
diff --git a/pan/gui/gui.cc b/pan/gui/gui.cc
index f510038..ff0a185 100644
--- a/pan/gui/gui.cc
+++ b/pan/gui/gui.cc
@@ -75,6 +75,13 @@ namespace pan
{
gtk_box_pack_start( box, child, TRUE, TRUE, 0 );
}
+
+ void remove_from_parent (GtkWidget * w)
+ {
+ GtkWidget *parent = gtk_widget_get_parent(w);
+ if (parent != 0)
+ gtk_container_remove (GTK_CONTAINER(parent), w);
+ }
}
using namespace pan;
@@ -124,8 +131,6 @@ GUI :: add_widget (GtkUIManager *,
const char * name (gtk_widget_get_name (widget));
GUI * self (static_cast<GUI*>(gui_g));
- std::cerr<<"add widget "<<name<<std::endl;
-
if (name && strstr(name,"main-window-")==name)
{
if (!GTK_IS_TOOLBAR (widget))
@@ -178,9 +183,7 @@ namespace
// };
//}
-
-
-GUI :: GUI (Data& data, Queue& queue, ArticleCache& cache, EncodeCache& encode_cache, Prefs& prefs, GroupPrefs& group_prefs):
+GUI :: GUI (Data& data, Queue& queue, ArticleCache& cache, EncodeCache& encode_cache, CertStore& cs, Prefs& prefs, GroupPrefs& group_prefs):
_data (data),
_queue (queue),
_cache (cache),
@@ -199,7 +202,8 @@ GUI :: GUI (Data& data, Queue& queue, ArticleCache& cache, EncodeCache& encode_c
_connection_size_label (0),
_queue_size_label (0),
_queue_size_button (0),
- _taskbar (0)
+ _taskbar (0),
+ _certstore(cs)
{
char * filename = g_build_filename (file::get_pan_home().c_str(), "pan.ui", NULL);
@@ -348,6 +352,8 @@ GUI :: GUI (Data& data, Queue& queue, ArticleCache& cache, EncodeCache& encode_c
on_queue_task_active_changed (queue, *(*it), true);
}
}
+
+ _certstore.add_listener(this);
}
namespace
@@ -358,6 +364,9 @@ namespace
GUI :: ~GUI ()
{
+
+ _certstore.remove_listener(this);
+
const std::string accel_filename (get_accel_filename());
gtk_accel_map_save (accel_filename.c_str());
chmod (accel_filename.c_str(), 0600);
@@ -997,6 +1006,12 @@ void GUI :: server_list_dialog_destroyed_cb (GtkWidget * w, gpointer self)
static_cast<GUI*>(self)->server_list_dialog_destroyed (w);
}
+void GUI :: sec_dialog_destroyed_cb (GtkWidget * w, gpointer self)
+{
+ static_cast<GUI*>(self)->sec_dialog_destroyed (w);
+}
+
+
// this queues up a grouplist task for any servers that
// were added while the server list dialog was up.
void GUI :: server_list_dialog_destroyed (GtkWidget *)
@@ -1010,6 +1025,18 @@ void GUI :: server_list_dialog_destroyed (GtkWidget *)
}
}
+
+void GUI :: sec_dialog_destroyed (GtkWidget * w)
+{
+// quarks_t empty_servers, all_servers (_data.get_servers());
+// foreach_const (quarks_t, all_servers, it) {
+// quarks_t tmp;
+// _data.server_get_groups (*it, tmp);
+// if (tmp.empty() && _data.get_server_limits(*it))
+// _queue.add_task (new TaskGroups (_data, *it));
+// }
+}
+
void GUI :: prefs_dialog_destroyed_cb (GtkWidget * w, gpointer self)
{
static_cast<GUI*>(self)->prefs_dialog_destroyed (w);
@@ -1036,6 +1063,15 @@ void GUI :: do_show_servers_dialog ()
g_signal_connect (w, "destroy", G_CALLBACK(server_list_dialog_destroyed_cb), this);
}
+
+void GUI :: do_show_sec_dialog ()
+{
+ GtkWidget * w = sec_dialog_new (_data, _queue, get_window(_root));
+ g_signal_connect (w, "destroy", G_CALLBACK(sec_dialog_destroyed_cb), this);
+ gtk_widget_show_all (w);
+}
+
+
void GUI :: do_show_score_dialog ()
{
const Quark& group (_header_pane->get_group());
@@ -1247,25 +1283,48 @@ void GUI :: do_cancel_article ()
}
bool GUI::deletion_confirmation_dialog()
- {
- bool ret(false);
- GtkWidget * d = gtk_message_dialog_new (
- get_window(_root),
- GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
- GTK_MESSAGE_WARNING,
- GTK_BUTTONS_NONE, NULL);
- HIG :: message_dialog_set_text (GTK_MESSAGE_DIALOG(d),
- _("You marked some articles for deletion"),
- _("Are you sure you want to delete them?"));
- gtk_dialog_add_buttons (GTK_DIALOG(d),
- GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
- GTK_STOCK_APPLY, GTK_RESPONSE_YES,
- NULL);
- gtk_dialog_set_default_response (GTK_DIALOG(d), GTK_RESPONSE_NO);
- ret = gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_YES;
- gtk_widget_destroy(d);
- return ret;
- }
+{
+ bool ret(false);
+ GtkWidget * d = gtk_message_dialog_new (
+ get_window(_root),
+ GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE, NULL);
+ HIG :: message_dialog_set_text (GTK_MESSAGE_DIALOG(d),
+ _("You marked some articles for deletion"),
+ _("Are you sure you want to delete them?"));
+ gtk_dialog_add_buttons (GTK_DIALOG(d),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
+ GTK_STOCK_APPLY, GTK_RESPONSE_YES,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG(d), GTK_RESPONSE_NO);
+ ret = gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_YES;
+ gtk_widget_destroy(d);
+ return ret;
+}
+
+
+bool GUI::confirm_accept_new_cert_dialog(X509* cert, const Quark& server)
+{
+ bool ret(false);
+ GtkWidget * d = gtk_message_dialog_new (
+ get_window(_root),
+ GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
+ GTK_MESSAGE_WARNING,
+ GTK_BUTTONS_NONE, NULL);
+ char buf[4096];
+ CertStore::pretty_print_x509(buf,sizeof(buf), server, cert);
+ HIG :: message_dialog_set_text (GTK_MESSAGE_DIALOG(d), buf,
+ _("Do you want to accept it permanently (deletable afterwards) ?"));
+ gtk_dialog_add_buttons (GTK_DIALOG(d),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
+ GTK_STOCK_APPLY, GTK_RESPONSE_YES,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG(d), GTK_RESPONSE_NO);
+ ret = gtk_dialog_run (GTK_DIALOG(d)) == GTK_RESPONSE_YES;
+ gtk_widget_destroy(d);
+ return ret;
+}
void GUI :: do_delete_article ()
{
@@ -1423,13 +1482,6 @@ void GUI :: do_work_online (bool b)
namespace
{
- void remove_from_parent (GtkWidget * w)
- {
- GtkWidget *parent = gtk_widget_get_parent(w);
- if (parent != 0)
- gtk_container_remove (GTK_CONTAINER(parent), w);
- }
-
enum { HORIZONTAL, VERTICAL };
void hpane_destroy_cb (GtkWidget *, gpointer)
@@ -2032,3 +2084,21 @@ GUI :: on_prefs_string_changed (const StringView& key, const StringView& value)
if (key == "default-save-attachments-path")
prev_path.assign (value.str, value.len);
}
+
+
+void
+GUI :: on_verify_cert_failed(X509* cert, std::string server, int nr)
+{
+ std::cerr<<"gui cert failed : "<<cert<<"\n";
+
+ if (confirm_accept_new_cert_dialog(cert,server))
+ if (!_certstore.add(cert, server))
+ std::cerr<<"error adding cert to "<<server<<std::endl;
+
+}
+
+void
+GUI :: on_valid_cert_added (X509* cert, std::string server)
+{
+
+}
diff --git a/pan/gui/gui.h b/pan/gui/gui.h
index 06f668c..0a81773 100644
--- a/pan/gui/gui.h
+++ b/pan/gui/gui.h
@@ -51,11 +51,12 @@ namespace pan
private Log::Listener,
private Progress::Listener,
private Queue::Listener,
- private Prefs::Listener
+ private Prefs::Listener,
+ private CertStore::Listener
{
public:
- GUI (Data& data, Queue&, ArticleCache&, EncodeCache&, Prefs&, GroupPrefs&);
+ GUI (Data& data, Queue&, ArticleCache&, EncodeCache&, CertStore&, Prefs&, GroupPrefs&);
virtual ~GUI ();
GtkWidget* root () { return _root; }
typedef std::vector<std::string> strings_t;
@@ -115,6 +116,7 @@ namespace pan
virtual void do_read_previous_thread ();
virtual void do_read_parent_article ();
virtual void do_show_servers_dialog ();
+ virtual void do_show_sec_dialog ();
virtual void do_show_selected_article_info ();
virtual void do_plonk ();
virtual void do_watch ();
@@ -125,6 +127,7 @@ namespace pan
virtual void do_supersede_article ();
virtual void do_delete_article ();
virtual bool deletion_confirmation_dialog();
+ virtual bool confirm_accept_new_cert_dialog(X509*, const Quark&);
virtual void do_clear_article_cache ();
virtual void do_mark_article_read ();
virtual void do_mark_article_unread ();
@@ -176,6 +179,9 @@ namespace pan
virtual void on_queue_online_changed (Queue&, bool online);
virtual void on_queue_error (Queue&, const StringView& message);
+ private: // CertStore::Listener
+ virtual void on_verify_cert_failed(X509*, std::string, int);
+ virtual void on_valid_cert_added (X509*, std::string);
private: // Log::Listener
virtual void on_log_entry_added (const Log::Entry& e);
@@ -198,6 +204,7 @@ namespace pan
EncodeCache& _encode_cache;
Prefs& _prefs;
GroupPrefs& _group_prefs;
+ CertStore& _certstore;
private:
GtkWidget * _root;
@@ -241,6 +248,8 @@ namespace pan
static void add_widget (GtkUIManager*, GtkWidget*, gpointer);
static void server_list_dialog_destroyed_cb (GtkWidget*, gpointer);
void server_list_dialog_destroyed (GtkWidget*);
+ static void sec_dialog_destroyed_cb (GtkWidget*, gpointer);
+ void sec_dialog_destroyed (GtkWidget*);
static void prefs_dialog_destroyed_cb (GtkWidget * w, gpointer self);
void prefs_dialog_destroyed (GtkWidget* w);
int score_int_from_string(std::string val, const char* rules[]);
diff --git a/pan/gui/pan-ui.h b/pan/gui/pan-ui.h
index 4f35574..2cac8b5 100644
--- a/pan/gui/pan-ui.h
+++ b/pan/gui/pan-ui.h
@@ -89,6 +89,7 @@ namespace pan
virtual void do_show_group_preferences_dialog () = 0;
virtual void do_show_profiles_dialog () = 0;
virtual void do_show_servers_dialog () = 0;
+ virtual void do_show_sec_dialog () = 0;
virtual void do_show_score_dialog () = 0;
virtual void do_show_new_score_dialog () = 0;
virtual void do_show_selected_article_info () = 0;
diff --git a/pan/gui/pan.cc b/pan/gui/pan.cc
index 62a2ca2..a17a999 100644
--- a/pan/gui/pan.cc
+++ b/pan/gui/pan.cc
@@ -36,6 +36,12 @@ extern "C" {
#include <pan/general/log.h>
#include <pan/general/file-util.h>
#include <pan/general/worker-pool.h>
+
+#ifdef HAVE_OPENSSL
+ #include <pan/tasks/socket-impl-openssl.h>
+ #include <pan/tasks/cert-store.h>
+#endif
+
#include <pan/tasks/socket-impl-gio.h>
#include <pan/tasks/socket-impl-main.h>
#include <pan/tasks/task-groups.h>
@@ -130,6 +136,7 @@ namespace
void run_pan_in_window (ArticleCache & cache,
EncodeCache & encode_cache,
+ CertStore & certstore,
Data & data,
Queue & queue,
Prefs & prefs,
@@ -139,7 +146,7 @@ namespace
{
const gulong delete_cb_id = g_signal_connect (window, "delete-event", G_CALLBACK(delete_event_cb), 0);
- GUI gui (data, queue, cache, encode_cache, prefs, group_prefs);
+ GUI gui (data, queue, cache, encode_cache, certstore, prefs, group_prefs);
gtk_container_add (GTK_CONTAINER(window), gui.root());
gtk_widget_show (GTK_WIDGET(window));
@@ -326,6 +333,9 @@ main (int argc, char *argv[])
ArticleCache& cache (data.get_cache ());
EncodeCache& encode_cache (data.get_encode_cache());
+ /* init certificate store for SSL */
+ CertStore certstore(0);
+
if (nzb && data.get_servers().empty()) {
std::cerr << _("Please configure Pan's news servers before using it as an nzb client.") << std::endl;
return EXIT_FAILURE;
@@ -335,9 +345,9 @@ main (int argc, char *argv[])
// instantiate the queue...
WorkerPool worker_pool (4, true);
- SocketCreator socket_creator;
+ SocketCreator socket_creator(certstore);
- Queue queue (data, data, &socket_creator, worker_pool,
+ Queue queue (data, data, &socket_creator, certstore, worker_pool,
prefs.get_flag ("work-online", true),
prefs.get_int ("task-save-delay-secs", 10));
g_timeout_add (5000, queue_upkeep_timer_cb, &queue);
@@ -399,7 +409,7 @@ main (int argc, char *argv[])
gtk_window_set_resizable (GTK_WINDOW(window), true);
gtk_window_set_default_icon (pixbuf);
g_object_unref (pixbuf);
- run_pan_in_window (cache, encode_cache, data, queue, prefs, group_prefs, GTK_WINDOW(window));
+ run_pan_in_window (cache, encode_cache, certstore, data, queue, prefs, group_prefs, GTK_WINDOW(window));
}
worker_pool.cancel_all_silently ();
diff --git a/pan/gui/pan.ui.h b/pan/gui/pan.ui.h
index 06891fa..c09a505 100644
--- a/pan/gui/pan.ui.h
+++ b/pan/gui/pan.ui.h
@@ -20,6 +20,7 @@ const char * fallback_ui_file =
" <menuitem action='select-article-body' />\n"
" <separator />\n"
" <menuitem action='show-servers-dialog' />\n"
+" <menuitem action='show-sec-dialog' />\n"
" <menuitem action='show-profiles-dialog' />\n"
" <menuitem action='show-preferences-dialog' />\n"
" <menuitem action='show-group-preferences-dialog' />\n"
diff --git a/pan/gui/post-ui.cc b/pan/gui/post-ui.cc
index c97cd28..77ec248 100644
--- a/pan/gui/post-ui.cc
+++ b/pan/gui/post-ui.cc
@@ -2659,7 +2659,6 @@ PostUI :: create_parts_tab ()
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w), GTK_SHADOW_IN);
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(w), t);
-// gtk_container_add (GTK_CONTAINER(w), t);
return w;
}
@@ -2897,7 +2896,8 @@ PostUI :: select_parts ()
gtk_window_set_role (GTK_WINDOW(w), "pan-parts-window");
gtk_window_set_title (GTK_WINDOW(w), _("Select Parts"));
int x,y;
- x = _prefs.get_int("post-ui-width", 450); // FIXME BUG: Native Windows wider or taller than 65535 pixels are not supported
+ // FIXME (sometimes....) BUG: Native Windows wider or taller than 65535 pixels are not supported
+ x = _prefs.get_int("post-ui-width", 450);
y = _prefs.get_int("post-ui-height", 450);
gtk_window_set_default_size (GTK_WINDOW(w), x, y);
// populate the window
@@ -3116,51 +3116,50 @@ PostUI :: prompt_user_for_queueable_files (GtkWindow * parent, const Prefs& pref
const int response (gtk_dialog_run (GTK_DIALOG(w)));
if (response == GTK_RESPONSE_ACCEPT) {
+ GSList * tmp_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER (w));
+ gtk_widget_destroy (w);
- GSList * tmp_list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER (w));
- gtk_widget_destroy (w);
-
- TaskUpload::UploadInfo ui;
- // query lines per file value
- ui.bpf = _prefs.get_int("upload-option-bpf",512*1024);
-
- std::string author;
- profile.get_from_header(author);
- std::string subject(utf8ize (g_mime_message_get_subject (tmp)));
-
- // insert groups info from msg
- quarks_t groups;
- StringView line (g_mime_object_get_header ((GMimeObject *) tmp, "Newsgroups"));
- StringView groupname;
- while (line.pop_token (groupname, ',')) {
- groupname.trim ();
- if (!groupname.empty())
- groups.insert(Quark(groupname));
- }
+ TaskUpload::UploadInfo ui;
+ // query lines per file value
+ ui.bpf = _prefs.get_int("upload-option-bpf",512*1024);
- GSList * cur = g_slist_nth (tmp_list,0);
- for (; cur; cur = cur->next)
- {
- GMimeMessage * msg (new_message_from_ui (UPLOADING));
+ std::string author;
+ profile.get_from_header(author);
+ std::string subject(utf8ize (g_mime_message_get_subject (tmp)));
+
+ // insert groups info from msg
+ quarks_t groups;
+ StringView line (g_mime_object_get_header ((GMimeObject *) tmp, "Newsgroups"));
+ StringView groupname;
+ while (line.pop_token (groupname, ',')) {
+ groupname.trim ();
+ if (!groupname.empty())
+ groups.insert(Quark(groupname));
+ }
+
+ GSList * cur = g_slist_nth (tmp_list,0);
+ for (; cur; cur = cur->next)
+ {
+ GMimeMessage * msg (new_message_from_ui (UPLOADING));
- //for nzb handling
- Article a;
- a.subject = subject;
- a.author = author;
- foreach_const (quarks_t, groups, git)
- a.xref.insert (profile.posting_server, *git,0);
+ //for nzb handling
+ Article a;
+ a.subject = subject;
+ a.author = author;
+ foreach_const (quarks_t, groups, git)
+ a.xref.insert (profile.posting_server, *git,0);
- struct stat sb;
- ui.total = get_total_parts((const char*)cur->data);
- TaskUpload* tmp = new TaskUpload(std::string((const char*)cur->data),
- profile.posting_server, _cache, a, ui, msg);
+ struct stat sb;
+ ui.total = get_total_parts((const char*)cur->data);
+ TaskUpload* tmp = new TaskUpload(std::string((const char*)cur->data),
+ profile.posting_server, _cache, a, ui, msg);
- // insert wanted parts to upload
- for (int i=1;i<=ui.total; ++i)
- tmp->_wanted.insert(i);
+ // insert wanted parts to upload
+ for (int i=1;i<=ui.total; ++i)
+ tmp->_wanted.insert(i);
- _upload_queue.add_task(tmp);
- }
+ _upload_queue.add_task(tmp);
+ }
if (_file_queue_empty) _file_queue_empty= false;
g_slist_free (tmp_list);
diff --git a/pan/gui/server-ui.cc b/pan/gui/server-ui.cc
index 8507a9c..8c269d4 100644
--- a/pan/gui/server-ui.cc
+++ b/pan/gui/server-ui.cc
@@ -26,6 +26,9 @@ extern "C" {
#include <glib/gi18n.h>
#include <gtk/gtk.h>
}
+
+#include <pan/usenet-utils/ssl-utils.h>
+#include <pan/icons/pan-pixbufs-internal.h>
#include <pan/general/macros.h>
#include <pan/general/quark.h>
#include <pan/data/data.h>
@@ -34,6 +37,16 @@ extern "C" {
#include "hig.h"
#include "gtk_compat.h"
+#ifdef HAVE_OPENSSL
+ #include <pan/tasks/cert-store.h>
+ #include <openssl/crypto.h>
+ #include <openssl/x509.h>
+ #include <openssl/x509v3.h>
+ #include <openssl/pem.h>
+ #include <openssl/ssl.h>
+ #include <openssl/err.h>
+#endif
+
using namespace pan;
/************
@@ -220,6 +233,32 @@ namespace
}
}
+std::string
+pan :: import_sec_from_disk_dialog_new (Data& data, Queue& queue, GtkWindow * window)
+{
+ std::string prev_path = g_get_home_dir ();
+ std::string res;
+
+ GtkWidget * w = gtk_file_chooser_dialog_new (_("Import SSL certificate (PEM format) from File"),
+ window,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (w), prev_path.c_str());
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (w), false);
+ gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (w), false);
+
+ const int response (gtk_dialog_run (GTK_DIALOG(w)));
+ if (response == GTK_RESPONSE_ACCEPT) {
+ res = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (w));
+ gtk_widget_destroy (w);
+ } else
+ gtk_widget_destroy (w);
+
+ return res;
+}
+
GtkWidget*
pan :: server_edit_dialog_new (Data& data, Queue& queue, GtkWindow * window, const Quark& server)
{
@@ -367,6 +406,24 @@ pan :: server_edit_dialog_new (Data& data, Queue& queue, GtkWindow * window, con
return d->dialog;
}
+namespace
+{
+ enum
+ {
+ ICON_PLAIN,
+ ICON_CERT,
+ ICON_QTY
+ };
+
+ struct Icon {
+ const guint8 * pixbuf_txt;
+ GdkPixbuf * pixbuf;
+ } _icons[ICON_QTY] = {
+ { icon_plain, 0 },
+ { icon_cert, 0 }
+ };
+}
+
/************
************* LIST DIALOG
@@ -376,6 +433,7 @@ namespace
{
enum
{
+ COL_FLAG,
COL_HOST,
COL_DATA,
N_COLUMNS
@@ -393,6 +451,7 @@ namespace
ServerListDialog (Data& d, Queue& q): data(d), queue(q) {}
};
+
Quark
get_selected_server (ServerListDialog * d)
{
@@ -416,6 +475,29 @@ namespace
return server;
}
+ Quark
+ get_selected_server_name (ServerListDialog * d)
+ {
+ g_assert (d != 0);
+
+ Quark server;
+
+ GtkTreeSelection * selection (gtk_tree_view_get_selection(GTK_TREE_VIEW (d->server_tree_view)));
+ GtkTreeModel * model;
+ GtkTreeIter iter;
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ char * host (0);
+ gtk_tree_model_get (model, &iter, COL_HOST, &host, -1);
+ if (host) {
+ server = host;
+ g_free (host);
+ }
+ }
+
+ //std::cerr << LINE_ID << " selected server is " << server << std::endl;
+ return server;
+ }
+
void
button_refresh (ServerListDialog * d)
{
@@ -458,12 +540,20 @@ namespace
delete (ServerListDialog*)p;
}
+ void delete_sec_dialog (gpointer p)
+ {
+ for (guint i=0; i<ICON_QTY; ++i)
+ g_object_unref (_icons[i].pixbuf);
+ delete (ServerListDialog*)p;
+ }
+
void
server_list_dialog_response_cb (GtkDialog * dialog, int, gpointer)
{
gtk_widget_destroy (GTK_WIDGET(dialog));
}
+
void
remove_button_clicked_cb (GtkButton * button, gpointer data)
{
@@ -543,6 +633,147 @@ namespace
}
}
+/* security dialog */
+namespace
+{
+
+ /* Show the current certificate of the selected server, if any */
+ void
+ cert_edit_button_clicked_cb (GtkButton *, gpointer user_data)
+ {
+ GtkWidget * list_dialog = GTK_WIDGET (user_data);
+ ServerListDialog * d = (ServerListDialog*) g_object_get_data (G_OBJECT(list_dialog), "dialog");
+ Quark selected_server (get_selected_server (d));
+
+ int port;
+ std::string addr;
+ d->data.get_server_addr (selected_server, addr, port);
+
+ char buf[4096] ;
+
+ if (!selected_server.empty()) {
+ X509* cert = (X509*)d->queue.store().get_cert_to_server(addr);
+ if (cert)
+ {
+ CertStore::pretty_print_x509(buf,sizeof(buf),addr, cert);
+ if (!buf) g_snprintf(buf,sizeof(buf), "%s", _("No information available.")) ;
+ GtkWidget * w = gtk_message_dialog_new_with_markup (
+ 0,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE, buf);
+ gtk_widget_show_all (w);
+ g_signal_connect_swapped (w, "response", G_CALLBACK (gtk_widget_destroy), w);
+ }
+ }
+ }
+
+ void
+ sec_tree_view_refresh (ServerListDialog * d)
+ {
+ GtkTreeSelection * selection (gtk_tree_view_get_selection(GTK_TREE_VIEW (d->server_tree_view)));
+ const quarks_t servers (d->data.get_servers ());
+ const Quark selected_server (get_selected_server (d));
+
+ bool found_selected (false);
+ GtkTreeIter selected_iter;
+ gtk_list_store_clear (d->servers_store);
+ foreach_const (quarks_t, servers, it)
+ {
+ const Quark& server (*it);
+ std::string addr; int port;
+ d->data.get_server_addr (server, addr, port);
+
+ if(d->data.get_server_ssl_support(server))
+ {
+ GtkTreeIter iter;
+ gtk_list_store_append (d->servers_store, &iter);
+ gtk_list_store_set (d->servers_store, &iter,
+ COL_FLAG, d->queue.store().exist(addr),
+ COL_HOST, addr.c_str(),
+ COL_DATA, server.c_str(),
+ -1);
+ if ((found_selected = (server == selected_server)))
+ selected_iter = iter;
+ }
+ }
+
+ if (found_selected)
+ gtk_tree_selection_select_iter (selection, &selected_iter);
+ }
+
+ void
+ sec_dialog_destroy_cb (GtkWidget *, gpointer user_data)
+ {
+ if (GTK_IS_WIDGET (user_data))
+ {
+ ServerListDialog * d = (ServerListDialog*) g_object_get_data (G_OBJECT(user_data), "dialog");
+ sec_tree_view_refresh (d);
+ }
+ }
+
+
+ /* add a cert from disk, overwriting the current certificate for the selected server */
+ void
+ cert_add_button_clicked_cb (GtkButton *, gpointer user_data)
+ {
+ const Quark empty_quark;
+ GtkWidget * list_dialog = GTK_WIDGET (user_data);
+ ServerListDialog * d = (ServerListDialog*) g_object_get_data (G_OBJECT(list_dialog), "dialog");
+ std::string ret = import_sec_from_disk_dialog_new (d->data, d->queue, GTK_WINDOW(list_dialog));
+ const Quark selected_server (get_selected_server (d));
+
+ if (!ret.empty() )
+ {
+ std::string addr; int port;
+ FILE *fp = fopen(ret.c_str(),"r");
+ X509 *x = X509_new();
+ PEM_read_X509(fp,&x, 0, 0);
+ fclose(fp);
+ d->data.get_server_addr(selected_server, addr, port);
+ d->queue.store().add(x,addr);
+ sec_tree_view_refresh (d);
+ }
+ }
+
+
+ /* remove cert from certstore */
+ void
+ cert_remove_button_clicked_cb (GtkButton * button, gpointer data)
+ {
+ ServerListDialog * d (static_cast<ServerListDialog*>(data));
+ Quark selected_server (get_selected_server (d));
+ if (!selected_server.empty())
+ {
+ int port;
+ std::string addr;
+ d->data.get_server_addr (selected_server, addr, port);
+
+ GtkWidget * w = gtk_message_dialog_new (GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))),
+ GtkDialogFlags(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ _("Really delete certificate for \"%s\"?"),
+ addr.c_str());
+ gtk_dialog_add_buttons (GTK_DIALOG(w),
+ GTK_STOCK_NO, GTK_RESPONSE_NO,
+ GTK_STOCK_DELETE, GTK_RESPONSE_YES,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG(w), GTK_RESPONSE_NO);
+ const int response (gtk_dialog_run (GTK_DIALOG (w)));
+ gtk_widget_destroy (w);
+
+ d->data.get_server_addr (selected_server, addr, port);
+ d->queue.store().remove(addr);
+
+ if (response == GTK_RESPONSE_YES)
+ sec_tree_view_refresh (d);
+
+ button_refresh (d);
+ }
+ }
+
+}
GtkWidget*
pan :: server_list_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
@@ -568,7 +799,7 @@ pan :: server_list_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
// create the list
- d->servers_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
+ d->servers_store = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
w = d->server_tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (d->servers_store));
GtkCellRenderer * renderer = gtk_cell_renderer_text_new ();
GtkTreeViewColumn * column = gtk_tree_view_column_new_with_attributes (_("Servers"), renderer, "text", COL_HOST, NULL);
@@ -618,3 +849,106 @@ pan :: server_list_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
button_refresh (d);
return d->dialog;
}
+
+
+void
+pan :: render_cert_flag (GtkTreeViewColumn * ,
+ GtkCellRenderer * renderer,
+ GtkTreeModel * model,
+ GtkTreeIter * iter,
+ gpointer )
+{
+ bool index (false);
+ gtk_tree_model_get (model, iter, COL_FLAG, &index, -1);
+ g_object_set (renderer, "pixbuf", _icons[index].pixbuf, NULL);
+}
+
+
+GtkWidget*
+pan :: sec_dialog_new (Data& data, Queue& queue, GtkWindow* parent)
+{
+ ServerListDialog * d = new ServerListDialog (data, queue);
+
+ for (guint i=0; i<ICON_QTY; ++i)
+ _icons[i].pixbuf = gdk_pixbuf_new_from_inline (-1, _icons[i].pixbuf_txt, FALSE, 0);
+
+ // dialog
+ char * title = g_strdup_printf ("Pan: %s", _("SSL Certificates"));
+ GtkWidget * w = d->dialog = gtk_dialog_new_with_buttons (title, parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
+ NULL);
+ g_free (title);
+ gtk_window_set_role (GTK_WINDOW(w), "pan-sec-dialog");
+ gtk_window_set_resizable (GTK_WINDOW(w), TRUE);
+ g_signal_connect (GTK_OBJECT(w), "response", G_CALLBACK(server_list_dialog_response_cb), d);
+ g_object_set_data_full (G_OBJECT(w), "dialog", d, delete_sec_dialog);
+
+ // workarea
+ GtkWidget * hbox = gtk_hbox_new (FALSE, PAD);
+ gtk_container_set_border_width (GTK_CONTAINER(hbox), 12);
+ gtk_box_pack_start (GTK_BOX( gtk_dialog_get_content_area( GTK_DIALOG(w))), hbox, TRUE, TRUE, 0);
+
+ // create the list
+ d->servers_store = gtk_list_store_new (N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
+ w = d->server_tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (d->servers_store));
+
+ GtkCellRenderer * r = GTK_CELL_RENDERER (g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF, "xpad", 2,"ypad", 0,NULL));
+// GtkTreeViewColumn * col = gtk_tree_view_column_new ();
+// gtk_tree_view_column_set_resizable (col, false);
+// gtk_tree_view_column_set_title (col, _("Certificate"));
+// gtk_tree_view_column_pack_start (col, r, false);
+// gtk_tree_view_column_set_cell_data_func (col, r, render_cert_flag, 0, 0);
+// gtk_tree_view_append_column (GTK_TREE_VIEW(w), col);
+ GtkTreeViewColumn * column = gtk_tree_view_column_new_with_attributes (_("Certificates"), r, NULL);
+ gtk_tree_view_column_set_cell_data_func (column, r, render_cert_flag, 0, 0);
+ gtk_tree_view_append_column (GTK_TREE_VIEW(w), column);
+
+ r = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (_("Servers"), r, "text", COL_HOST, NULL);
+ gtk_tree_view_column_set_sort_column_id (column, COL_HOST);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (w), column);
+ GtkTreeSelection * selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (w));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+ // add callbacks
+ g_signal_connect (GTK_TREE_VIEW (w), "row-activated",
+ G_CALLBACK (server_tree_view_row_activated_cb), d->dialog);
+ g_signal_connect (G_OBJECT (selection), "changed",
+ G_CALLBACK (server_tree_view_selection_changed_cb), d);
+
+ w = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(w), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER(w), d->server_tree_view);
+ gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0);
+ gtk_widget_set_size_request (w, 300, 300);
+
+ // button box
+ GtkWidget * bbox = gtk_vbox_new (FALSE, PAD_SMALL);
+ gtk_box_pack_start (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
+
+ // add button
+ w = gtk_button_new_from_stock (GTK_STOCK_ADD);
+ gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(w, _("Import Certificate"));
+ g_signal_connect (w, "clicked", G_CALLBACK(cert_add_button_clicked_cb), d->dialog);
+
+ // inspect button
+ w = gtk_button_new_from_stock (GTK_STOCK_FIND);
+ gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(w, _("Inspect Certificate"));
+ g_signal_connect (w, "clicked", G_CALLBACK(cert_edit_button_clicked_cb), d->dialog);
+ d->edit_button = w;
+
+ // remove button
+ w = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
+ gtk_box_pack_start (GTK_BOX (bbox), w, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(w, _("Remove Certificate"));
+ g_signal_connect (w, "clicked", G_CALLBACK(cert_remove_button_clicked_cb), d);
+ d->remove_button = w;
+
+ sec_tree_view_refresh (d);
+ button_refresh (d);
+ return d->dialog;
+}
diff --git a/pan/gui/server-ui.h b/pan/gui/server-ui.h
index 2201976..971d387 100644
--- a/pan/gui/server-ui.h
+++ b/pan/gui/server-ui.h
@@ -20,17 +20,35 @@
#ifndef SERVER_UI_H
#define SERVER_UI_H
+#include <set>
#include <gtk/gtk.h>
#include <pan/data/data.h>
#include <pan/tasks/queue.h>
namespace pan
{
+
+ typedef std::set<std::string> strings_t;
+
/** @ingroup GUI */
GtkWidget* server_edit_dialog_new (Data&, Queue&, GtkWindow*, const Quark& server);
/** @ingroup GUI */
GtkWidget* server_list_dialog_new (Data&, Queue&, GtkWindow*);
+
+ /** @ingroup GUI */
+ GtkWidget* sec_dialog_new (Data& data, Queue& queue, GtkWindow* parent);
+
+ /** @ingroup GUI */
+ std::string
+ import_sec_from_disk_dialog_new (Data& data, Queue& queue, GtkWindow * window);
+
+ /** @ingroup GUI */
+ void render_cert_flag (GtkTreeViewColumn * ,
+ GtkCellRenderer * ,
+ GtkTreeModel * ,
+ GtkTreeIter * ,
+ gpointer );
}
#endif
diff --git a/pan/gui/task-pane.cc b/pan/gui/task-pane.cc
index cb49bb9..b25e57d 100644
--- a/pan/gui/task-pane.cc
+++ b/pan/gui/task-pane.cc
@@ -127,7 +127,6 @@ TaskPane:: on_tooltip_query(GtkWidget *widget,
return false;
gtk_tree_model_get (model, &iter, COL_TASK_POINTER, &task, -1);
-// if (!task) return false;
g_snprintf(buffer,sizeof(buffer),"...");
@@ -611,7 +610,7 @@ namespace
void do_stop (GtkAction*, gpointer p) { static_cast<TaskPane*>(p)->stop_clicked_cb(0, static_cast<TaskPane*>(p)); }
void do_delete (GtkAction*, gpointer p) { static_cast<TaskPane*>(p)->delete_clicked_cb(0, static_cast<TaskPane*>(p)); }
void do_restart (GtkAction*, gpointer p) { static_cast<TaskPane*>(p)->restart_clicked_cb(0, static_cast<TaskPane*>(p)); }
- void do_change_dest (GtkAction* a, gpointer p){ static_cast<TaskPane*>(p)->change_dest_clicked_cb(0, static_cast<TaskPane*>(p)); }
+ void do_change_dest (GtkAction*, gpointer p) { static_cast<TaskPane*>(p)->change_dest_clicked_cb(0, static_cast<TaskPane*>(p)); }
GtkActionEntry taskpane_popup_entries[] =
{
diff --git a/pan/icons/Makefile.am b/pan/icons/Makefile.am
index 040a872..6490d38 100644
--- a/pan/icons/Makefile.am
+++ b/pan/icons/Makefile.am
@@ -44,7 +44,9 @@ stock_images = \
icon_mozilla_smile.png \
icon_mozilla_surprised.png \
icon_mozilla_tongueout.png \
- icon_mozilla_wink.png
+ icon_mozilla_wink.png \
+ icon_plain.png \
+ icon_cert.png
EXTRA_DIST = \
diff --git a/pan/icons/icon_cert.png b/pan/icons/icon_cert.png
new file mode 100644
index 0000000..ddf83d9
Binary files /dev/null and b/pan/icons/icon_cert.png differ
diff --git a/pan/icons/icon_plain.png b/pan/icons/icon_plain.png
new file mode 100644
index 0000000..b09e418
Binary files /dev/null and b/pan/icons/icon_plain.png differ
diff --git a/pan/icons/icon_tabbar.png b/pan/icons/icon_tabbar.png
new file mode 100644
index 0000000..d013c93
Binary files /dev/null and b/pan/icons/icon_tabbar.png differ
diff --git a/pan/tasks/Makefile.am b/pan/tasks/Makefile.am
index 2a05e51..9264efb 100644
--- a/pan/tasks/Makefile.am
+++ b/pan/tasks/Makefile.am
@@ -1,6 +1,6 @@
-AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@
+AM_CPPFLAGS = -I top_srcdir@ @GMIME_CFLAGS@ @GLIB_CFLAGS@ @OPENSSL_CFLAGS@
-AM_LDFLAGS = ../../uulib/libuu.a -lz @OPENSSL_LIBS@
+AM_LDFLAGS = ../../uulib/libuu.a -lz @OPENSSL_LIBS@
noinst_LIBRARIES = libtasks.a
@@ -18,6 +18,7 @@ libtasks_a_SOURCES = \
queue.cc \
upload-queue.cc \
socket.cc \
+ cert-store.cc \
socket-impl-main.cc \
socket-impl-openssl.cc \
socket-impl-gio.cc \
@@ -43,6 +44,7 @@ noinst_HEADERS = \
queue.h \
upload-queue.h \
socket.h \
+ cert-store.h \
socket-impl-main.h \
socket-impl-openssl.cc \
socket-impl-gio.h \
diff --git a/pan/tasks/cert-store.cc b/pan/tasks/cert-store.cc
new file mode 100644
index 0000000..4cc0b84
--- /dev/null
+++ b/pan/tasks/cert-store.cc
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006 Charles Kerr <charles rebelbase com>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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
+ */
+
+#include <string>
+#include <glib/giochannel.h>
+#include <glib/gstring.h>
+#include <pan/tasks/socket.h>
+#include <config.h>
+#include <map>
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <cerrno>
+#include <cstring>
+
+extern "C" {
+ #include <glib/gi18n.h>
+ #include <dirent.h>
+}
+
+#include <pan/general/debug.h>
+#include <pan/general/e-util.h>
+#include <pan/general/macros.h>
+#include <pan/usenet-utils/mime-utils.h>
+
+#include <pan/general/debug.h>
+#include <pan/general/file-util.h>
+#include <pan/general/macros.h>
+#include <pan/general/messages.h>
+#include <pan/general/log.h>
+#include <pan/general/string-view.h>
+#include <pan/usenet-utils/mime-utils.h>
+
+#include "cert-store.h"
+
+using namespace pan;
+
+namespace pan
+{
+
+ int
+ verify_callback(int ok, X509_STORE_CTX *store)
+// verify_callback(X509_STORE_CTX *store, void* args)
+ {
+
+ SSL * ssl = (SSL*)X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx());
+ mydata_t* mydata = (mydata_t*)SSL_get_ex_data(ssl, SSL_get_fd(ssl));
+
+// CertStore * me = (CertStore*)args;
+
+ if (!ok)
+ {
+ if (mydata->ignore_all==1) return 1;
+
+ X509 *cert = X509_STORE_CTX_get_current_cert(store);
+ int depth = X509_STORE_CTX_get_error_depth(store);
+ int err = X509_STORE_CTX_get_error(store);
+
+ if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)
+ mydata->cs->verify_failed(cert, mydata->server, err);
+ }
+ return ok;
+
+// SSL_CTX_set_verify(me->get_ctx(), SSL_VERIFY_PEER, store->verify_cb);
+//
+// int ok =
+//
+// }
+
+}
+
+void
+CertStore :: get_all_certs_from_disk(std::set<X509*>& setme)
+{
+ char filename[PATH_MAX];
+ const char * fname;
+ GError * err = NULL;
+ GDir * dir = g_dir_open (_path.c_str(), 0, &err);
+
+ int cnt(0);
+ while ((fname = g_dir_read_name (dir)))
+ {
+ if (strlen(fname)<=1) continue;
+
+ g_snprintf (filename, sizeof(filename), "%s%c%s", _path.c_str(), G_DIR_SEPARATOR, fname);
+ FILE *fp = fopen(filename,"r");
+ X509 *x = X509_new();
+ PEM_read_X509(fp,&x, 0, 0);
+ fclose(fp);
+ setme.insert(x);
+
+ std::string fn(fname);
+ std::string::size_type idx = fn.rfind(".pem");
+
+ if(idx != std::string::npos)
+ {
+ std::string server = fn.substr(0,idx);
+ _certs.insert(server);
+ _cert_to_server[server] = x;
+ }
+ ++cnt;
+ }
+ g_dir_close (dir);
+
+ if (cnt != 0) Log::add_info_va(_("Succesfully added %d SSL PEM certificate(s) to Certificate Store."), cnt);
+}
+
+void
+CertStore :: init_me()
+{
+ assert (_ctx);
+
+ _store = SSL_CTX_get_cert_store(_ctx);
+
+ std::set<X509*> certs;
+ get_all_certs_from_disk (certs);
+ foreach_const (std::set<X509*>, certs, it)
+ X509_STORE_add_cert(_store, *it);
+// SSL_CTX_set_cert_verify_callback(_ctx, verify_callback, (void*)this);
+ SSL_CTX_set_verify(_ctx, SSL_VERIFY_PEER, verify_callback);
+
+}
+
+void
+CertStore :: remove_hard(const Quark& server)
+{
+ char buf[2048];
+ g_snprintf (buf, sizeof(buf), "%s%c%s.pem", _path.c_str(), G_DIR_SEPARATOR, server.c_str());
+ unlink(buf);
+}
+
+void
+CertStore :: remove (const Quark& server)
+{
+ if (_cert_to_server.count(server) > 0)
+ {
+ _cert_to_server.erase(server);
+ _certs.erase(server);
+ remove_hard(server);
+ }
+}
+
+CertStore :: CertStore (SSL_CTX * ctx) : _ctx(ctx)
+{
+ if (ctx) init_me();
+ char buf[2048];
+ g_snprintf(buf,sizeof(buf),"%s%cssl_certs",file::get_pan_home().c_str(), G_DIR_SEPARATOR);
+ file::ensure_dir_exists (buf);
+ _path = buf;
+}
+
+CertStore :: ~CertStore ()
+{}
+
+
+
+bool
+CertStore :: add(X509* cert, const Quark& server)
+{
+ if (_certs.count(server) > 0 || !cert || server.empty()) return false;
+
+ X509_STORE_add_cert(get_store(),cert);
+ _certs.insert(server);
+ _cert_to_server[server] = cert;
+
+ char buf[2048];
+ g_snprintf(buf,sizeof(buf),"%s%c%s.pem",_path.c_str(),G_DIR_SEPARATOR,server.c_str());
+ FILE * fp = fopen(buf, "wb");
+ PEM_write_X509(fp, cert);
+ fclose(fp);
+
+ valid_cert_added(cert, server.c_str());
+ return true;
+}
+
+const X509*
+CertStore :: get_cert_to_server(const Quark& server) const
+{
+ const X509* ret(0);
+ Quark serv;
+
+ /* strip port from server if existing */
+ std::string s(server);
+ std::string::size_type idx = s.rfind(":");
+ if(idx != std::string::npos)
+ serv = s.substr(0,idx);
+ else
+ serv = server;
+
+ /* dbg dump all */
+// std::cerr<<"asking for server cert "<<serv<<std::endl;
+// std::cerr<<"existing certs : \n";
+// foreach_const(certs_m, _cert_to_server, it)
+// {
+// std::cerr<<it->first<<" "<<it->second<<std::endl;
+// }
+
+ if (_cert_to_server.count(serv) > 0)
+ ret = _cert_to_server.find(serv)->second;
+ return ret;
+}
+
+namespace
+{
+ std::string
+ get_x509_fingerpint_md5(X509* cert)
+ {
+ std::string res;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ unsigned int n;
+
+ if (! X509_digest(cert, EVP_md5(), md, &n))
+ res += _("Not available.");
+ else {
+ char hex[] = "0123456789ABCDEF";
+ char fp[EVP_MAX_MD_SIZE*3];
+ if (n < sizeof(fp)) {
+ unsigned int i;
+ for (i = 0; i < n; i++) {
+ fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
+ fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
+ fp[i*3+2] = i == n - 1 ? '\0' : ':';
+ }
+ res += fp;
+ }
+ }
+ return res;
+ }
+
+}
+
+void
+CertStore :: pretty_print_x509 (char* buf, size_t size, const Quark& server, X509* cert)
+{
+ if (!cert) return;
+ g_snprintf(buf,size, _("The current server <b>'%s'</b> sent this security certificate :\n\n"
+ "<b>Issuer :</b> \n%s\n\n"
+ "<b>Subject : </b>\n%s\n\n"
+ "<b>Fingerprint : </b>\n%s\n\n"),
+ server.c_str(),
+ X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0),
+ X509_NAME_oneline(X509_get_subject_name(cert), 0, 0),
+ get_x509_fingerpint_md5(cert).c_str());
+}
+
+
+
+} // namespace pan
diff --git a/pan/tasks/cert-store.h b/pan/tasks/cert-store.h
new file mode 100644
index 0000000..d5be2c8
--- /dev/null
+++ b/pan/tasks/cert-store.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Pan - A Newsreader for Gtk+
+ * Copyright (C) 2002-2006 Charles Kerr <charles rebelbase com>
+ *
+ * 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; version 2 of the License.
+ *
+ * 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
+ */
+
+#ifndef __CertStore_h__
+#define __CertStore_h__
+
+#ifdef HAVE_OPENSSL
+ #include <openssl/pem.h>
+ #include <openssl/err.h>
+ #include <openssl/pkcs12.h>
+ #include <openssl/bio.h>
+ #include <openssl/rand.h>
+ #include <openssl/x509.h>
+#endif
+
+#include <pan/tasks/socket.h>
+#include <pan/general/quark.h>
+#include <pan/general/macros.h>
+#include <pan/general/worker-pool.h>
+#include <pan/general/string-view.h>
+
+#include <map>
+
+namespace pan
+{
+#ifdef HAVE_OPENSSL
+ class CertStore
+ {
+ public:
+ CertStore (SSL_CTX*) ;
+ virtual ~CertStore () ;
+
+ private:
+ SSL_CTX* _ctx;
+ typedef std::set<Quark> certs_t;
+ certs_t _certs;
+ typedef std::map<Quark,X509*> certs_m;
+ typedef std::pair<Quark,X509*> certs_p;
+ certs_m _cert_to_server;
+ X509_STORE* _store;
+ std::string _path;
+
+ public:
+ SSL_CTX* get_ctx() { return _ctx; }
+ X509_STORE* get_store() const { return _store; }
+ void get_all_certs_from_disk(std::set<X509*>& setme);
+ const X509* get_cert_to_server(const Quark& server) const;
+ static void pretty_print_x509 (char* buf, size_t size, const Quark& server, X509* cert);
+
+ private:
+ void remove_hard(const Quark&);
+
+ public:
+ bool add(X509*, const Quark&) ;
+ void remove (const Quark&);
+ bool exist (const Quark& q) { return (_certs.count(q) > 0); }
+
+ struct Listener
+ {
+ virtual ~Listener() {}
+ /* functions that other listeners listen on */
+ virtual void on_verify_cert_failed (X509* cert UNUSED, std::string server UNUSED, int nr UNUSED) = 0;
+ virtual void on_valid_cert_added (X509* cert UNUSED, std::string server UNUSED) = 0;
+ };
+
+ typedef std::set<Listener*> listeners_t;
+ listeners_t _listeners;
+
+ void add_listener (Listener * l) { _listeners.insert(l); }
+ void remove_listener (Listener * l) { _listeners.erase(l); }
+
+ /* notify functions for listener list */
+ void verify_failed (X509* c, std::string server, int nr)
+ {
+ for (listeners_t::iterator it(_listeners.begin()), end(_listeners.end()); it!=end; ++it)
+ (*it)->on_verify_cert_failed (c, server, nr);
+ }
+
+ void valid_cert_added (X509* c, std::string server)
+ {
+ for (listeners_t::iterator it(_listeners.begin()), end(_listeners.end()); it!=end; ++it)
+ (*it)->on_valid_cert_added (c, server);
+ }
+
+ private:
+ void init_me();
+
+ protected:
+ friend class SocketCreator;
+ void set_ctx(SSL_CTX* c) { _ctx = c; init_me(); }
+
+ };
+
+ struct mydata_t {
+ SSL_CTX* ctx;
+ int depth;
+ int ignore_all;
+ CertStore* cs;
+ std::string server;
+ CertStore::Listener* l;
+ };
+#endif
+}
+
+ #endif
diff --git a/pan/tasks/nntp-pool.cc b/pan/tasks/nntp-pool.cc
index 12e4fc0..ebcddf3 100644
--- a/pan/tasks/nntp-pool.cc
+++ b/pan/tasks/nntp-pool.cc
@@ -39,14 +39,17 @@ namespace
NNTP_Pool :: NNTP_Pool (const Quark & server,
ServerInfo & server_info,
- SocketCreator * creator):
+ SocketCreator * creator,
+ CertStore & certstore):
_server_info (server_info),
_server (server),
_socket_creator (creator),
+ _certstore(certstore),
_pending_connections (0),
_active_count (0),
_time_to_allow_new_connections (0)
{
+ certstore.add_listener(this);
}
NNTP_Pool :: ~NNTP_Pool ()
@@ -55,6 +58,7 @@ NNTP_Pool :: ~NNTP_Pool ()
delete it->nntp->_socket;
delete it->nntp;
}
+ _certstore.remove_listener(this);
}
/***
@@ -174,6 +178,7 @@ NNTP_Pool :: on_socket_created (const StringView & host UNUSED,
}
}
+
void
NNTP_Pool :: on_nntp_done (NNTP* nntp, Health health, const StringView& response)
{
@@ -270,9 +275,12 @@ NNTP_Pool :: request_nntp (WorkerPool& threadpool)
int port;
if (_server_info.get_server_addr (_server, address, port))
{
- ++_pending_connections;
- const bool ssl(_server_info.get_server_ssl_support(_server));
- _socket_creator->create_socket (address, port, threadpool, this, ssl);
+ if (_blacklist.count(address) == 0)
+ {
+ ++_pending_connections;
+ const bool ssl(_server_info.get_server_ssl_support(_server));
+ _socket_creator->create_socket (address, port, threadpool, this, ssl);
+ }
}
}
}
@@ -333,3 +341,17 @@ NNTP_Pool :: idle_upkeep ()
item = 0;
}
}
+
+
+void
+NNTP_Pool :: on_verify_cert_failed (X509* cert, std::string server, int nr)
+{
+ _blacklist.insert(server);
+ abort_tasks();
+}
+
+void
+NNTP_Pool :: on_valid_cert_added (X509* cert, std::string server)
+{
+ _blacklist.erase(server);
+}
diff --git a/pan/tasks/nntp-pool.h b/pan/tasks/nntp-pool.h
index 4815e1b..81b00ac 100644
--- a/pan/tasks/nntp-pool.h
+++ b/pan/tasks/nntp-pool.h
@@ -27,6 +27,7 @@
#include <pan/tasks/socket.h>
#include <pan/tasks/nntp.h>
#include <pan/tasks/socket-impl-main.h>
+#include <pan/tasks/cert-store.h>
namespace pan
{
@@ -40,13 +41,15 @@ namespace pan
class NNTP_Pool:
public NNTP::Source,
private NNTP::Listener,
- private Socket::Creator::Listener
+ private Socket::Creator::Listener,
+ private CertStore::Listener
{
public:
NNTP_Pool (const Quark & server,
ServerInfo & server_info,
- SocketCreator *);
+ SocketCreator *,
+ CertStore & certstore);
virtual ~NNTP_Pool ();
@@ -58,7 +61,7 @@ namespace pan
void get_counts (int& setme_active,
int& setme_idle,
int& setme_connecting,
- int& setme_max) const;
+ int& setme_max) const;
public:
@@ -76,7 +79,11 @@ namespace pan
private: // NNTP::Listener
virtual void on_nntp_done (NNTP*, Health, const StringView&);
-
+ #ifdef HAVE_OPENSSL
+ private: // CertStore::Listener
+ virtual void on_verify_cert_failed (X509*, std::string, int);
+ virtual void on_valid_cert_added (X509* cert, std::string server);
+#endif
private: // Socket::Creator::Listener
virtual void on_socket_created (const StringView& host, int port, bool ok, Socket*);
@@ -95,6 +102,7 @@ namespace pan
const Quark _server;
SocketCreator * _socket_creator;
int _pending_connections;
+ std::set<std::string> _blacklist;
struct PoolItem {
NNTP * nntp;
@@ -105,6 +113,8 @@ namespace pan
pool_items_t _pool_items;
int _active_count;
+ CertStore& _certstore;
+
private:
time_t _time_to_allow_new_connections;
diff --git a/pan/tasks/queue.cc b/pan/tasks/queue.cc
index 3fbc306..4992042 100644
--- a/pan/tasks/queue.cc
+++ b/pan/tasks/queue.cc
@@ -34,6 +34,7 @@ using namespace pan;
Queue :: Queue (ServerInfo & server_info,
TaskArchive & archive,
SocketCreator * socket_creator,
+ CertStore & store,
WorkerPool & pool,
bool online,
int save_delay_secs):
@@ -49,7 +50,8 @@ Queue :: Queue (ServerInfo & server_info,
_needs_saving (false),
_last_time_saved (0),
_archive (archive),
- _uploads_total(0)
+ _uploads_total(0),
+ _certstore (store)
{
tasks_t tasks;
_archive.load_tasks (tasks);
@@ -98,7 +100,7 @@ Queue :: get_pool (const Quark& servername)
}
else // have to build one
{
- pool = new NNTP_Pool (servername, _server_info, _socket_creator);
+ pool = new NNTP_Pool (servername, _server_info, _socket_creator, _certstore);
pool->add_listener (this);
_pools[servername] = pool;
}
diff --git a/pan/tasks/queue.h b/pan/tasks/queue.h
index 1257641..ffb19cc 100644
--- a/pan/tasks/queue.h
+++ b/pan/tasks/queue.h
@@ -35,6 +35,7 @@
#include <pan/tasks/encoder.h>
#include <pan/tasks/task-weak-ordering.h>
#include <pan/tasks/socket-impl-main.h>
+#include <pan/tasks/cert-store.h>
namespace pan
{
@@ -70,7 +71,7 @@ namespace pan
private AdaptableSet<Task*, TaskWeakOrdering>::Listener
{
public:
- Queue (ServerInfo&, TaskArchive&, SocketCreator*, WorkerPool&,
+ Queue (ServerInfo&, TaskArchive&, SocketCreator*, CertStore&, WorkerPool&,
bool online, int save_delay_secs);
virtual ~Queue ();
@@ -241,8 +242,13 @@ namespace pan
private:
TaskArchive& _archive;
void clean_n_save ();
-
int _uploads_total;
+ CertStore& _certstore;
+
+ /* FIXME: move all this certstore crap to data */
+ public:
+ const CertStore& store() const { return _certstore; }
+ CertStore& store() { return _certstore; }
private:
typedef AdaptableSet<Task*, TaskWeakOrdering> TaskSet;
diff --git a/pan/tasks/socket-impl-gio.h b/pan/tasks/socket-impl-gio.h
index 736eb76..22070a8 100644
--- a/pan/tasks/socket-impl-gio.h
+++ b/pan/tasks/socket-impl-gio.h
@@ -25,8 +25,6 @@
#include <glib/gstring.h>
#include <pan/tasks/socket.h>
-
-
namespace pan
{
/**
@@ -55,6 +53,7 @@ namespace pan
bool _io_performed;
private:
+ friend class GIOChannelSocketSSL;
enum WatchMode { READ_NOW, WRITE_NOW, IGNORE_NOW };
void set_watch_mode (WatchMode mode);
static gboolean gio_func (GIOChannel*, GIOCondition, gpointer);
diff --git a/pan/tasks/socket-impl-main.cc b/pan/tasks/socket-impl-main.cc
index f5a09d7..9b0b87b 100644
--- a/pan/tasks/socket-impl-main.cc
+++ b/pan/tasks/socket-impl-main.cc
@@ -60,15 +60,16 @@ namespace pan
std::string err;
bool use_ssl;
SSL_CTX * context;
+ CertStore& store;
- ThreadWorker (const StringView& h, int p, Socket::Creator::Listener *l, bool ssl, SSL_CTX* ctx):
- host(h), port(p), listener(l), ok(false), socket(0), use_ssl(ssl), context(ctx) {}
+ ThreadWorker (const StringView& h, int p, Socket::Creator::Listener *l, bool ssl, SSL_CTX* ctx, CertStore& cs):
+ host(h), port(p), listener(l), ok(false), socket(0), use_ssl(ssl), context(ctx), store(cs) {}
void do_work ()
{
#ifdef HAVE_OPENSSL
if (use_ssl)
- socket = new GIOChannelSocketSSL (context);
+ socket = new GIOChannelSocketSSL (context, store);
else
#endif
socket = new GIOChannelSocket ();
@@ -112,28 +113,33 @@ namespace
for (int i=0; i<CRYPTO_num_locks(); i++)
pthread_mutex_destroy(&lock_cs[i]);
CRYPTO_set_locking_callback(0);
-// CRYPTO_set_id_callback(0);
OPENSSL_free(lock_cs);
}
+
}
#endif
-SocketCreator :: SocketCreator()
+SocketCreator :: SocketCreator(CertStore& cs) : store(cs)
{
#ifdef HAVE_OPENSSL
SSL_library_init();
SSL_load_error_strings();
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+
/* init static locks for threads */
ssl_thread_setup();
ssl_ctx = SSL_CTX_new(SSLv3_client_method());
+ cs.set_ctx(ssl_ctx);
+ SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_AUTO_RETRY);
#endif
-
}
+
SocketCreator :: ~SocketCreator()
{
#ifdef HAVE_OPENSSL
ssl_thread_cleanup();
- SSL_CTX_free(ssl_ctx);
+ if (ssl_ctx) SSL_CTX_free(ssl_ctx);
#endif
}
@@ -144,8 +150,17 @@ SocketCreator :: create_socket (const StringView & host,
Socket::Creator::Listener * listener,
bool use_ssl)
{
- ensure_module_init ();
+ ensure_module_init ();
+
+ ThreadWorker * w = new ThreadWorker (host, port, listener, use_ssl, ssl_ctx, store);
+ threadpool.push_work (w, w, true);
+}
- ThreadWorker * w = new ThreadWorker (host, port, listener, use_ssl, ssl_ctx);
- threadpool.push_work (w, w, true);
+void
+SocketCreator :: on_verify_cert_failed(X509* cert, std::string server, int nr)
+{
}
+
+void
+SocketCreator :: on_valid_cert_added (X509* cert, std::string server)
+{}
diff --git a/pan/tasks/socket-impl-main.h b/pan/tasks/socket-impl-main.h
index 8942357..8776803 100644
--- a/pan/tasks/socket-impl-main.h
+++ b/pan/tasks/socket-impl-main.h
@@ -35,11 +35,14 @@
#include <pan/general/string-view.h>
#include <pan/general/worker-pool.h>
#include "socket.h"
+
#ifdef HAVE_OPENSSL
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include "socket-impl-openssl.h"
+ #include "cert-store.h"
#endif
+
#include "socket-impl-gio.h"
namespace
@@ -112,20 +115,30 @@ namespace
namespace pan
{
-#ifdef HAVE_OPENSSL
- static SSL_CTX* ssl_ctx;
-#endif
- class SocketCreator
+
+ class SocketCreator:
+ private CertStore::Listener
{
public:
- SocketCreator ();
+ SocketCreator (CertStore&);
virtual ~SocketCreator ();
+#ifdef HAVE_OPENSSL
+ private:
+ SSL_CTX* ssl_ctx;
+ CertStore & store;
+#endif
+
+ public:
virtual void create_socket (const StringView & host,
int port,
WorkerPool & threadpool,
Socket::Creator::Listener * listener,
bool use_ssl);
+
+ // CertStore::Listener
+ virtual void on_verify_cert_failed(X509*, std::string, int);
+ virtual void on_valid_cert_added (X509*, std::string );
};
}
diff --git a/pan/tasks/socket-impl-openssl.cc b/pan/tasks/socket-impl-openssl.cc
index 83f17da..290a007 100644
--- a/pan/tasks/socket-impl-openssl.cc
+++ b/pan/tasks/socket-impl-openssl.cc
@@ -19,6 +19,10 @@
/* #define DEBUG_SOCKET_IO */
+/** Copyright notice: Some code taken from here :
+ * http://dslinux.gits.kiev.ua/trunk/user/irssi/src/src/core/network-openssl.c
+ * Copyright (C) 2002 vjt (irssi project) */
+
/******
*******
******/
@@ -34,6 +38,7 @@ extern "C" {
#include <glib/gi18n.h>
}
+#include <pan/usenet-utils/ssl-utils.h>
#include <pan/general/file-util.h>
#include <pan/general/log.h>
#include <pan/general/macros.h>
@@ -90,6 +95,7 @@ extern "C" {
#include <pan/usenet-utils/gnksa.h>
#include "socket-impl-openssl.h"
#include "socket-impl-main.h"
+#include "cert-store.h"
using namespace pan;
@@ -106,7 +112,7 @@ extern t_freeaddrinfo p_freeaddrinfo;
#ifdef HAVE_OPENSSL // without libssl this class is just a stub....
-GIOChannelSocketSSL :: GIOChannelSocketSSL (SSL_CTX* ctx):
+GIOChannelSocketSSL :: GIOChannelSocketSSL (SSL_CTX* ctx, CertStore& cs):
_channel (0),
_tag_watch (0),
_tag_timeout (0),
@@ -114,9 +120,11 @@ GIOChannelSocketSSL :: GIOChannelSocketSSL (SSL_CTX* ctx):
_out_buf (g_string_new (0)),
_in_buf (g_string_new (0)),
_io_performed (false),
- _ctx(ctx)
+ _ctx(ctx),
+ _certstore(cs)
{
- debug ("GIOChannelSocketSSL ctor " << (void*)this);
+// std::cerr<<"GIOChannelSocketSSL ctor " << (void*)this<<std::endl;
+ cs.add_listener(this);
}
@@ -201,7 +209,7 @@ GIOChannelSocketSSL :: create_channel (const StringView& host_in, int port, std:
// try to open a socket on any ipv4 or ipv6 addresses we found
errno = 0;
sockfd = -1;
- for (struct addrinfo * walk(ans); walk && sockfd<0; walk=walk->ai_next)
+ for (struct addrinfo * walk(ans); walk && sockfd<=0; walk=walk->ai_next)
{
// only use ipv4 or ipv6 addresses
if ((walk->ai_family!=PF_INET) && (walk->ai_family!=PF_INET6))
@@ -224,7 +232,7 @@ GIOChannelSocketSSL :: create_channel (const StringView& host_in, int port, std:
}
// create the giochannel...
- if (sockfd < 0) {
+ if (sockfd <= 0) {
char buf[512];
snprintf (buf, sizeof(buf), _("Error connecting to \"%s\""), hpbuf);
setme_err = buf;
@@ -239,7 +247,6 @@ GIOChannelSocketSSL :: create_channel (const StringView& host_in, int port, std:
GIOChannel * channel (0);
#ifndef G_OS_WIN32
channel = g_io_channel_unix_new (sockfd);
- g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, 0);
#else
channel = g_io_channel_win32_new_socket (sockfd);
#endif
@@ -270,51 +277,11 @@ namespace
GIOChannel *giochan;
SSL *ssl;
SSL_CTX *ctx;
+ char* host;
unsigned int verify;
} GIOSSLChannel;
- /* FIXME todo: real verify ! */
- gboolean ssl_verify(SSL *ssl, SSL_CTX *ctx, X509 *cert)
- {
- if (SSL_get_verify_result(ssl) != X509_V_OK) {
- unsigned char md[EVP_MAX_MD_SIZE];
- unsigned int n;
- char *str;
-
- if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL)
- g_warning(" Could not get subject-name from peer certificate");
- else {
- g_warning(" Subject : %s", str);
- free(str);
- }
- if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL)
- g_warning(" Could not get issuer-name from peer certificate");
- else {
- g_warning(" Issuer : %s", str);
- free(str);
- }
- if (! X509_digest(cert, EVP_md5(), md, &n))
- g_warning(" Could not get fingerprint from peer certificate");
- else {
- char hex[] = "0123456789ABCDEF";
- char fp[EVP_MAX_MD_SIZE*3];
- if (n < sizeof(fp)) {
- unsigned int i;
- for (i = 0; i < n; i++) {
- fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
- fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
- fp[i*3+2] = i == n - 1 ? '\0' : ':';
- }
- g_warning(" MD5 Fingerprint : %s", fp);
- }
- }
- return FALSE;
- }
- return TRUE;
- }
-
-
void ssl_free(GIOChannel *handle)
{
GIOSSLChannel *chan = (GIOSSLChannel *)handle;
@@ -327,7 +294,10 @@ namespace
GIOChannelSocketSSL :: ~GIOChannelSocketSSL ()
{
-//std::cerr << LINE_ID << " destroying socket " << this << std::endl;
+
+// std::cerr << LINE_ID << " destroying socket " << this << std::endl;
+
+ _certstore.remove_listener(this);
remove_source (_tag_watch);
remove_source (_tag_timeout);
@@ -362,7 +332,7 @@ GIOChannelSocketSSL :: get_host (std::string& setme) const
}
void
-GIOChannelSocketSSL :: write_command (const StringView& command, Listener * l)
+GIOChannelSocketSSL :: write_command (const StringView& command, Socket::Listener * l)
{
_partial_read.clear ();
_listener = l;
@@ -381,11 +351,6 @@ GIOChannelSocketSSL :: write_command (const StringView& command, Listener * l)
namespace
{
- SSL_CTX* ssl_init(void)
- {
- return 0;
- }
-
static GIOStatus ssl_errno(gint e)
{
switch(e)
@@ -401,31 +366,60 @@ namespace
return G_IO_STATUS_ERROR;
}
- int ssl_handshake(GIOChannel *handle)
+
+ int ssl_handshake(GIOChannel *handle, CertStore::Listener* listener, CertStore* cs, std::string host)
{
+
GIOSSLChannel *chan = (GIOSSLChannel *)handle;
int ret;
int err;
X509 *cert;
const char *errstr;
- if (!handle || !chan->ssl || !chan->ctx) return -1;
+ /* init custom data for callback */
+ mydata_t mydata;// = new mydata_t();
+ mydata.ctx = chan->ctx;
+ mydata.cs = cs;
+ mydata.ignore_all = 0;
+ mydata.l = listener;
+ mydata.server = host;
+ SSL_set_ex_data(chan->ssl, SSL_get_fd(chan->ssl), &mydata);
ret = SSL_connect(chan->ssl);
if (ret <= 0) {
err = SSL_get_error(chan->ssl, ret);
- if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE)
- return -1;
- return err == SSL_ERROR_WANT_READ ? 1 : 3;
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ return 1;
+ case SSL_ERROR_WANT_WRITE:
+ return 3;
+ case SSL_ERROR_ZERO_RETURN:
+ g_warning("SSL handshake failed: %s", "server closed connection");
+ return -1;
+ case SSL_ERROR_SYSCALL:
+ errstr = ERR_reason_error_string(ERR_get_error());
+ if (errstr == NULL && ret == -1)
+ errstr = strerror(errno);
+ g_warning("SSL handshake failed: %s", errstr != NULL ? errstr : "server closed connection unexpectedly");
+ return -1;
+ default:
+ errstr = ERR_reason_error_string(ERR_get_error());
+ g_warning("SSL handshake failed: %s", errstr != NULL ? errstr : "unknown SSL error");
+ return -1;
+ }
}
cert = SSL_get_peer_certificate(chan->ssl);
- if (!cert && chan->ssl)
+ if (!cert) {
+ g_warning("SSL server supplied no certificate");
return -1;
+ }
- ret = chan->verify ? ssl_verify(chan->ssl, chan->ctx, cert) : 0;
+ ret = !chan->verify || ssl_verify(chan->ssl, chan->ctx, host.c_str(), cert);
X509_free(cert);
- return ret;
+
+ return ret ? 0 : -1;
+
}
GIOStatus ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr)
@@ -742,26 +736,23 @@ GIOChannelSocketSSL :: set_watch_mode (WatchMode mode)
GIOChannel *
GIOChannelSocketSSL :: ssl_get_iochannel(GIOChannel *handle, gboolean verify)
{
+
GIOSSLChannel *chan(0);
GIOChannel *gchan(0);
int err(0), fd(0);
SSL *ssl(0);
- SSL_CTX *ctx(0);
+ SSL_CTX *ctx(_ctx);
g_return_val_if_fail(handle != 0, 0);
-
- ctx = _ctx;
- if (!ctx) return 0;
+ g_return_val_if_fail(ctx != 0, 0);
if(!(fd = g_io_channel_unix_get_fd(handle)))
{
- if (ctx) SSL_CTX_free(ctx);
return 0;
}
if(!(ssl = SSL_new(ctx)))
{
- SSL_CTX_free(ctx);
g_warning("Failed to allocate SSL structure");
return 0;
}
@@ -770,7 +761,6 @@ GIOChannelSocketSSL :: ssl_get_iochannel(GIOChannel *handle, gboolean verify)
{
g_warning("Failed to associate socket to SSL stream");
SSL_free(ssl);
- SSL_CTX_free(ctx);
return 0;
}
@@ -781,18 +771,32 @@ GIOChannelSocketSSL :: ssl_get_iochannel(GIOChannel *handle, gboolean verify)
chan->giochan = handle;
chan->ssl = ssl;
chan->ctx = ctx;
- chan->verify = verify ? 0 : -1;
+ chan->verify = verify ? 1 : 0;
gchan = (GIOChannel *)chan;
gchan->funcs = &ssl_channel_funcs;
g_io_channel_init(gchan);
gchan->read_buf = g_string_sized_new(4096*128);
- if (ssl_handshake(gchan))
+ if (ssl_handshake(gchan, this, &_certstore, _host) == 0)
{
+ std::cerr<<"handshake success\n";
+ g_io_channel_set_flags (handle, G_IO_FLAG_NONBLOCK, 0);
return gchan;
}
+ std::cerr<<"handshake fail\n";
return 0;
+}
+
+void
+GIOChannelSocketSSL :: on_verify_cert_failed (X509* cert, std::string server, int nr)
+{
+
+}
+
+void
+GIOChannelSocketSSL :: on_valid_cert_added (X509* cert, std::string server)
+{
}
#endif //HAVE_OPENSSL
diff --git a/pan/tasks/socket-impl-openssl.h b/pan/tasks/socket-impl-openssl.h
index 9529522..4bb9a11 100644
--- a/pan/tasks/socket-impl-openssl.h
+++ b/pan/tasks/socket-impl-openssl.h
@@ -23,9 +23,12 @@
#include <string>
#include <glib/giochannel.h>
#include <glib/gstring.h>
+#include <pan/general/quark.h>
#include <pan/tasks/socket.h>
+#include <pan/tasks/socket-impl-gio.h>
#ifdef HAVE_OPENSSL
+ #include <pan/tasks/cert-store.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
@@ -34,10 +37,6 @@
#include <openssl/err.h>
#endif
-#include "socket-impl-gio.h"
-#include "socket-impl-main.h"
-
-
namespace pan
{
/**
@@ -45,32 +44,30 @@ namespace pan
*
* @ingroup tasks
*/
- class GIOChannelSocketSSL: public GIOChannelSocket
+ class GIOChannelSocketSSL:
+ public GIOChannelSocket,
+ private CertStore::Listener
{
public:
virtual ~GIOChannelSocketSSL ();
-#ifndef HAVE_OPENSSL
- GIOChannelSocketSSL ();
-#else
- GIOChannelSocketSSL (SSL_CTX* ctx=0);
-#endif
+ GIOChannelSocketSSL (SSL_CTX* ctx, CertStore& cs);
+
virtual bool open (const StringView& address, int port, std::string& setme_err);
- virtual void write_command (const StringView& chars, Listener *);
+ virtual void write_command (const StringView& chars, Socket::Listener *);
virtual void get_host (std::string& setme) const;
private:
GIOChannel * _channel;
unsigned int _tag_watch;
unsigned int _tag_timeout;
- Listener * _listener;
+ Socket::Listener * _listener;
GString * _out_buf;
GString * _in_buf;
std::string _partial_read;
std::string _host;
bool _io_performed;
-#ifdef HAVE_OPENSSL
SSL_CTX * _ctx;
-#endif
+ CertStore& _certstore;
private:
enum WatchMode { READ_NOW, WRITE_NOW, IGNORE_NOW };
@@ -82,6 +79,10 @@ namespace pan
DoResult do_read ();
DoResult do_write ();
+ // CertStore::Listener
+ virtual void on_verify_cert_failed (X509*, std::string, int) ;
+ virtual void on_valid_cert_added (X509*, std::string );
+
GIOChannel * create_channel (const StringView& host_in, int port, std::string& setme_err);
void gio_lock(int mode, int type, const char *file, int line);
diff --git a/pan/tasks/socket.h b/pan/tasks/socket.h
index a4cfa7e..bc6fd2e 100644
--- a/pan/tasks/socket.h
+++ b/pan/tasks/socket.h
@@ -21,6 +21,16 @@
#define __Socket_h__
#include <string>
+#include <config.h>
+
+#ifdef HAVE_OPENSSL
+ #include <openssl/crypto.h>
+ #include <openssl/x509.h>
+ #include <openssl/x509v3.h>
+ #include <openssl/pem.h>
+ #include <openssl/ssl.h>
+ #include <openssl/err.h>
+#endif
namespace pan
{
diff --git a/pan/usenet-utils/Makefile.am b/pan/usenet-utils/Makefile.am
index e51556d..df16332 100644
--- a/pan/usenet-utils/Makefile.am
+++ b/pan/usenet-utils/Makefile.am
@@ -23,7 +23,8 @@ noinst_HEADERS = \
numbers.h \
scorefile.h \
text-massager.h \
- url-find.h
+ url-find.h \
+ ssl-utils.h
#noinst_PROGRAMS = \
# gnksa-test \
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]