1 /**
2  * @file gntcertmgr.c GNT Certificate Manager API
3  * @ingroup finch
4  */
5 
6 /* finch
7  *
8  * Finch is the legal property of its developers, whose names are too numerous
9  * to list here.  Please refer to the COPYRIGHT file distributed with this
10  * source distribution.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
25  *
26  */
27 
28 #include <internal.h>
29 #include "finch.h"
30 
31 #include "certificate.h"
32 #include "debug.h"
33 #include "notify.h"
34 #include "request.h"
35 
36 #include "gntcertmgr.h"
37 
38 #include "gntbutton.h"
39 #include "gntlabel.h"
40 #include "gnttree.h"
41 #include "gntutils.h"
42 #include "gntwindow.h"
43 
44 struct {
45 	GntWidget *window;
46 	GntWidget *tree;
47 	PurpleCertificatePool *pool;
48 } certmgr;
49 
50 /* Pretty much Xerox of gtkcertmgr */
51 
52 /* Add certificate */
53 static void
tls_peers_mgmt_import_ok2_cb(gpointer data,const char * result)54 tls_peers_mgmt_import_ok2_cb(gpointer data, const char *result)
55 {
56 	PurpleCertificate *crt = (PurpleCertificate *) data;
57 	const char *id = result;
58 
59 	/* TODO: Perhaps prompt if you're overwriting a cert? */
60 
61 	purple_certificate_pool_store(purple_certificate_find_pool("x509", "tls_peers"), id, crt);
62 	purple_certificate_destroy(crt);
63 }
64 
65 static void
tls_peers_mgmt_import_cancel2_cb(gpointer data,const char * result)66 tls_peers_mgmt_import_cancel2_cb(gpointer data, const char *result)
67 {
68 	PurpleCertificate *crt = (PurpleCertificate *) data;
69 	purple_certificate_destroy(crt);
70 }
71 
72 static void
tls_peers_mgmt_import_ok_cb(gpointer data,const char * filename)73 tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename)
74 {
75 	PurpleCertificateScheme *x509;
76 	PurpleCertificate *crt;
77 
78 	x509 = purple_certificate_pool_get_scheme(purple_certificate_find_pool("x509", "tls_peers"));
79 
80 	crt = purple_certificate_import(x509, filename);
81 
82 	if (crt != NULL) {
83 		gchar *default_hostname;
84 		default_hostname = purple_certificate_get_subject_name(crt);
85 		purple_request_input(NULL,
86 				_("Certificate Import"),
87 				_("Specify a hostname"),
88 				_("Type the host name this certificate is for."),
89 				default_hostname, FALSE, FALSE, NULL,
90 				_("OK"), G_CALLBACK(tls_peers_mgmt_import_ok2_cb),
91 				_("Cancel"), G_CALLBACK(tls_peers_mgmt_import_cancel2_cb),
92 				NULL, NULL, NULL,
93 				crt);
94 		g_free(default_hostname);
95 	} else {
96 		gchar * secondary;
97 		secondary = g_strdup_printf(_("File %s could not be imported.\nMake sure that the file is readable and in PEM format.\n"), filename);
98 		purple_notify_error(NULL,
99 				_("Certificate Import Error"),
100 				_("X.509 certificate import failed"),
101 				secondary);
102 		g_free(secondary);
103 	}
104 }
105 
106 static void
add_cert_cb(GntWidget * button,gpointer null)107 add_cert_cb(GntWidget *button, gpointer null)
108 {
109 	purple_request_file(NULL,
110 			_("Select a PEM certificate"),
111 			"certificate.pem",
112 			FALSE,
113 			G_CALLBACK(tls_peers_mgmt_import_ok_cb),
114 			NULL,
115 			NULL, NULL, NULL, NULL );
116 }
117 
118 /* Save certs in some file */
119 static void
tls_peers_mgmt_export_ok_cb(gpointer data,const char * filename)120 tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename)
121 {
122 	PurpleCertificate *crt = (PurpleCertificate *) data;
123 
124 	if (!purple_certificate_export(filename, crt)) {
125 		gchar * secondary;
126 
127 		secondary = g_strdup_printf(_("Export to file %s failed.\nCheck that you have write permission to the target path\n"), filename);
128 		purple_notify_error(NULL,
129 				    _("Certificate Export Error"),
130 				    _("X.509 certificate export failed"),
131 				    secondary);
132 		g_free(secondary);
133 	}
134 
135 	purple_certificate_destroy(crt);
136 }
137 
138 static void
save_cert_cb(GntWidget * button,gpointer null)139 save_cert_cb(GntWidget *button, gpointer null)
140 {
141 	PurpleCertificate *crt;
142 	const char *key;
143 
144 	if (!certmgr.window)
145 		return;
146 
147 	key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree));
148 	if (!key)
149 		return;
150 
151 	crt = purple_certificate_pool_retrieve(certmgr.pool, key);
152 	if (!crt) {
153 		purple_debug_error("gntcertmgr/tls_peers_mgmt",
154 				"Id %s was not in the peers cache?!\n", key);
155 		return;
156 	}
157 
158 	purple_request_file((void*)key,
159 			_("PEM X.509 Certificate Export"),
160 			"certificate.pem", TRUE,
161 			G_CALLBACK(tls_peers_mgmt_export_ok_cb),
162 			G_CALLBACK(purple_certificate_destroy),
163 			NULL, NULL, NULL,
164 			crt);
165 }
166 
167 /* Show information about a cert */
168 static void
info_cert_cb(GntWidget * button,gpointer null)169 info_cert_cb(GntWidget *button, gpointer null)
170 {
171 	const char *key;
172 	PurpleCertificate *crt;
173 	gchar *subject;
174 	GByteArray *fpr_sha1;
175 	gchar *fpr_sha1_asc;
176 	gchar *primary, *secondary;
177 
178 	if (!certmgr.window)
179 		return;
180 
181 	key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree));
182 	if (!key)
183 		return;
184 
185 	crt = purple_certificate_pool_retrieve(certmgr.pool, key);
186 	g_return_if_fail(crt);
187 
188 	primary = g_strdup_printf(_("Certificate for %s"), key);
189 
190 	fpr_sha1 = purple_certificate_get_fingerprint_sha1(crt);
191 	fpr_sha1_asc = purple_base16_encode_chunked(fpr_sha1->data,
192 						    fpr_sha1->len);
193 	subject = purple_certificate_get_subject_name(crt);
194 
195 	secondary = g_strdup_printf(_("Common name: %s\n\nSHA1 fingerprint:\n%s"), subject, fpr_sha1_asc);
196 
197 	purple_notify_info(NULL,
198 			   _("SSL Host Certificate"), primary, secondary);
199 
200 	g_free(primary);
201 	g_free(secondary);
202 	g_byte_array_free(fpr_sha1, TRUE);
203 	g_free(fpr_sha1_asc);
204 	g_free(subject);
205 	purple_certificate_destroy(crt);
206 }
207 
208 /* Delete a cert */
209 static void
tls_peers_mgmt_delete_confirm_cb(gchar * id,gint dontcare)210 tls_peers_mgmt_delete_confirm_cb(gchar *id, gint dontcare)
211 {
212 	if (!purple_certificate_pool_delete(certmgr.pool, id)) {
213 		purple_debug_warning("gntcertmgr/tls_peers_mgmt",
214 				"Deletion failed on id %s\n", id);
215 	};
216 
217 	g_free(id);
218 }
219 
220 static void
delete_cert_cb(GntWidget * button,gpointer null)221 delete_cert_cb(GntWidget *button, gpointer null)
222 {
223 	gchar *primary;
224 	const char *key;
225 
226 	if (!certmgr.window)
227 		return;
228 
229 	key = gnt_tree_get_selection_data(GNT_TREE(certmgr.tree));
230 	if (!key)
231 		return;
232 
233 	primary = g_strdup_printf(_("Really delete certificate for %s?"), key);
234 
235 	purple_request_close_with_handle((void *)key);
236 	purple_request_yes_no((void *)key, _("Confirm certificate delete"),
237 			primary, NULL,
238 			0,
239 			NULL, NULL, NULL,
240 			g_strdup(key),
241 			tls_peers_mgmt_delete_confirm_cb,
242 			g_free);
243 
244 	g_free(primary);
245 }
246 
247 /* populate the list */
248 static void
populate_cert_list(void)249 populate_cert_list(void)
250 {
251 	GList *idlist, *l;
252 
253 	if (!certmgr.window)
254 		return;
255 
256 	gnt_tree_remove_all(GNT_TREE(certmgr.tree));
257 
258 	idlist = purple_certificate_pool_get_idlist(purple_certificate_find_pool("x509", "tls_peers"));
259 	for (l = idlist; l; l = l->next) {
260 		gnt_tree_add_row_last(GNT_TREE(certmgr.tree), g_strdup(l->data),
261 				gnt_tree_create_row(GNT_TREE(certmgr.tree), l->data), NULL);
262 	}
263 	purple_certificate_pool_destroy_idlist(idlist);
264 }
265 
266 static void
cert_list_added(PurpleCertificatePool * pool,const char * id,gpointer null)267 cert_list_added(PurpleCertificatePool *pool, const char *id, gpointer null)
268 {
269 	g_return_if_fail(certmgr.window);
270 	gnt_tree_add_row_last(GNT_TREE(certmgr.tree), g_strdup(id),
271 			gnt_tree_create_row(GNT_TREE(certmgr.tree), id), NULL);
272 }
273 
274 static void
cert_list_removed(PurpleCertificatePool * pool,const char * id,gpointer null)275 cert_list_removed(PurpleCertificatePool *pool, const char *id, gpointer null)
276 {
277 	g_return_if_fail(certmgr.window);
278 	purple_request_close_with_handle((void*)id);
279 	gnt_tree_remove(GNT_TREE(certmgr.tree), (void*)id);
280 }
281 
finch_certmgr_show(void)282 void finch_certmgr_show(void)
283 {
284 	GntWidget *win, *tree, *box, *button;
285 	PurpleCertificatePool *pool;
286 
287 	if (certmgr.window) {
288 		gnt_window_present(certmgr.window);
289 		return;
290 	}
291 
292 	certmgr.window = win = gnt_vwindow_new(FALSE);
293 	gnt_box_set_title(GNT_BOX(win), _("Certificate Manager"));
294 	gnt_box_set_pad(GNT_BOX(win), 0);
295 
296 	certmgr.tree = tree = gnt_tree_new();
297 	gnt_tree_set_hash_fns(GNT_TREE(tree), g_str_hash, g_str_equal, g_free);
298 	gnt_tree_set_column_title(GNT_TREE(tree), 0, _("Hostname"));
299 	gnt_tree_set_show_title(GNT_TREE(tree), TRUE);
300 
301 	gnt_box_add_widget(GNT_BOX(win), tree);
302 
303 	box = gnt_hbox_new(FALSE);
304 	gnt_box_add_widget(GNT_BOX(win), box);
305 
306 	button = gnt_button_new(_("Add"));
307 	gnt_box_add_widget(GNT_BOX(box), button);
308 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(add_cert_cb), NULL);
309 	gnt_util_set_trigger_widget(GNT_WIDGET(tree), GNT_KEY_INS, button);
310 
311 	button = gnt_button_new(_("Save"));
312 	gnt_box_add_widget(GNT_BOX(box), button);
313 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_cert_cb), NULL);
314 
315 	button = gnt_button_new(_("Info"));
316 	gnt_box_add_widget(GNT_BOX(box), button);
317 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(info_cert_cb), NULL);
318 
319 	button = gnt_button_new(_("Delete"));
320 	gnt_box_add_widget(GNT_BOX(box), button);
321 	g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(delete_cert_cb), NULL);
322 	gnt_util_set_trigger_widget(GNT_WIDGET(tree), GNT_KEY_DEL, button);
323 
324 	button = gnt_button_new(_("Close"));
325 	gnt_box_add_widget(GNT_BOX(box), button);
326 	g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), win);
327 
328 	g_signal_connect_swapped(G_OBJECT(win), "destroy", G_CALLBACK(g_nullify_pointer), &certmgr.window);
329 
330 	populate_cert_list();
331 
332 	pool = certmgr.pool = purple_certificate_find_pool("x509", "tls_peers");
333 	purple_signal_connect(pool, "certificate-stored",
334 			      win, PURPLE_CALLBACK(cert_list_added), NULL);
335 	purple_signal_connect(pool, "certificate-deleted",
336 			      win, PURPLE_CALLBACK(cert_list_removed), NULL);
337 	g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(purple_signals_disconnect_by_handle), NULL);
338 
339 	gnt_widget_show(certmgr.window);
340 }
341 
342