| 35 #include "gtkutils.h" |
35 #include "gtkutils.h" |
| 36 |
36 |
| 37 #include "gtkcertmgr.h" |
37 #include "gtkcertmgr.h" |
| 38 |
38 |
| 39 /***************************************************************************** |
39 /***************************************************************************** |
| 40 * X.509 tls_peers management interface * |
40 * X.509 certificate management interface * |
| 41 *****************************************************************************/ |
41 *****************************************************************************/ |
| 42 |
42 |
| 43 typedef struct { |
43 typedef struct { |
| 44 GtkWidget *mgmt_widget; |
44 GtkWidget *mgmt_widget; |
| 45 GtkTreeView *listview; |
45 GtkTreeView *listview; |
| 46 GtkTreeSelection *listselect; |
46 GtkTreeSelection *listselect; |
| 47 GtkWidget *importbutton; |
47 GtkWidget *importbutton; |
| 48 GtkWidget *exportbutton; |
48 GtkWidget *exportbutton; |
| 49 GtkWidget *infobutton; |
49 GtkWidget *infobutton; |
| 50 GtkWidget *deletebutton; |
50 GtkWidget *deletebutton; |
| 51 PurpleCertificatePool *tls_peers; |
|
| 52 } tls_peers_mgmt_data; |
51 } tls_peers_mgmt_data; |
| 53 |
52 |
| 54 tls_peers_mgmt_data *tpm_dat = NULL; |
53 tls_peers_mgmt_data *tpm_dat = NULL; |
| 55 |
54 |
| 56 /* Columns |
55 /* Columns |
| 74 |
73 |
| 75 static void |
74 static void |
| 76 tls_peers_mgmt_repopulate_list(void) |
75 tls_peers_mgmt_repopulate_list(void) |
| 77 { |
76 { |
| 78 GtkTreeView *listview = tpm_dat->listview; |
77 GtkTreeView *listview = tpm_dat->listview; |
| 79 PurpleCertificatePool *tls_peers; |
|
| 80 GList *idlist, *l; |
78 GList *idlist, *l; |
| 81 |
79 |
| 82 GtkListStore *store = GTK_LIST_STORE( |
80 GtkListStore *store = GTK_LIST_STORE( |
| 83 gtk_tree_view_get_model(GTK_TREE_VIEW(listview))); |
81 gtk_tree_view_get_model(GTK_TREE_VIEW(listview))); |
| 84 |
82 |
| 85 /* First, delete everything in the list */ |
83 /* First, delete everything in the list */ |
| 86 gtk_list_store_clear(store); |
84 gtk_list_store_clear(store); |
| 87 |
85 |
| 88 /* Locate the "tls_peers" pool */ |
86 /* Grab the available certificates */ |
| 89 tls_peers = purple_certificate_find_pool("x509", "tls_peers"); |
87 idlist = purple_tls_certificate_list_ids(); |
| 90 g_return_if_fail(tls_peers); |
|
| 91 |
|
| 92 /* Grab the loaded certificates */ |
|
| 93 idlist = purple_certificate_pool_get_idlist(tls_peers); |
|
| 94 |
88 |
| 95 /* Populate the listview */ |
89 /* Populate the listview */ |
| 96 for (l = idlist; l; l = l->next) { |
90 for (l = idlist; l; l = l->next) { |
| 97 GtkTreeIter iter; |
91 GtkTreeIter iter; |
| 98 gtk_list_store_append(store, &iter); |
92 gtk_list_store_append(store, &iter); |
| 99 |
93 |
| 100 gtk_list_store_set(GTK_LIST_STORE(store), &iter, |
94 gtk_list_store_set(GTK_LIST_STORE(store), &iter, |
| 101 TPM_HOSTNAME_COLUMN, l->data, |
95 TPM_HOSTNAME_COLUMN, l->data, |
| 102 -1); |
96 -1); |
| 103 } |
97 } |
| 104 purple_certificate_pool_destroy_idlist(idlist); |
98 |
| 105 } |
99 purple_tls_certificate_free_ids(idlist); |
| 106 |
|
| 107 static void |
|
| 108 tls_peers_mgmt_mod_cb(PurpleCertificatePool *pool, const gchar *id, gpointer data) |
|
| 109 { |
|
| 110 g_assert (pool == tpm_dat->tls_peers); |
|
| 111 |
|
| 112 tls_peers_mgmt_repopulate_list(); |
|
| 113 } |
100 } |
| 114 |
101 |
| 115 static void |
102 static void |
| 116 tls_peers_mgmt_select_chg_cb(GtkTreeSelection *ignored, gpointer data) |
103 tls_peers_mgmt_select_chg_cb(GtkTreeSelection *ignored, gpointer data) |
| 117 { |
104 { |
| 135 } |
122 } |
| 136 |
123 |
| 137 static void |
124 static void |
| 138 tls_peers_mgmt_import_ok2_cb(gpointer data, const char *result) |
125 tls_peers_mgmt_import_ok2_cb(gpointer data, const char *result) |
| 139 { |
126 { |
| 140 PurpleCertificate *crt = (PurpleCertificate *) data; |
127 GTlsCertificate *crt = data; |
| |
128 GError *error = NULL; |
| 141 |
129 |
| 142 /* TODO: Perhaps prompt if you're overwriting a cert? */ |
130 /* TODO: Perhaps prompt if you're overwriting a cert? */ |
| 143 |
131 |
| 144 /* Drop the certificate into the pool */ |
132 /* Trust the certificate */ |
| 145 if (result && *result) |
133 if (result && *result) { |
| 146 purple_certificate_pool_store(tpm_dat->tls_peers, result, crt); |
134 if(!purple_tls_certificate_trust(result, crt, &error)) { |
| |
135 purple_debug_error("gtkcertmgr/tls_peers_mgmt", |
| |
136 "Error trusting certificate '%s': %s", |
| |
137 result, error->message); |
| |
138 g_clear_error(&error); |
| |
139 } |
| |
140 |
| |
141 tls_peers_mgmt_repopulate_list(); |
| |
142 } |
| 147 |
143 |
| 148 /* And this certificate is not needed any more */ |
144 /* And this certificate is not needed any more */ |
| 149 purple_certificate_destroy(crt); |
145 g_object_unref(crt); |
| 150 } |
146 } |
| 151 |
147 |
| 152 static void |
148 static void |
| 153 tls_peers_mgmt_import_cancel2_cb(gpointer data, const char *result) |
149 tls_peers_mgmt_import_cancel2_cb(gpointer data, const char *result) |
| 154 { |
150 { |
| 155 PurpleCertificate *crt = (PurpleCertificate *) data; |
151 GTlsCertificate *crt = data; |
| 156 purple_certificate_destroy(crt); |
152 g_object_unref(crt); |
| 157 } |
153 } |
| 158 |
154 |
| 159 static void |
155 static void |
| 160 tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename) |
156 tls_peers_mgmt_import_ok_cb(gpointer data, const char *filename) |
| 161 { |
157 { |
| 162 PurpleCertificateScheme *x509; |
158 GTlsCertificate *crt; |
| 163 PurpleCertificate *crt; |
159 GError *error = NULL; |
| 164 |
|
| 165 /* Load the scheme of our tls_peers pool (ought to be x509) */ |
|
| 166 x509 = purple_certificate_pool_get_scheme(tpm_dat->tls_peers); |
|
| 167 |
160 |
| 168 /* Now load the certificate from disk */ |
161 /* Now load the certificate from disk */ |
| 169 crt = purple_certificate_import(x509, filename); |
162 crt = g_tls_certificate_new_from_file(filename, &error); |
| 170 |
163 |
| 171 /* Did it work? */ |
164 /* Did it work? */ |
| 172 if (crt != NULL) { |
165 if (crt != NULL) { |
| 173 gchar *default_hostname; |
166 gchar *default_hostname; |
| 174 /* Get name to add to pool as */ |
167 PurpleTlsCertificateInfo *info; |
| |
168 |
| |
169 /* Get name to add trust as */ |
| 175 /* Make a guess about what the hostname should be */ |
170 /* Make a guess about what the hostname should be */ |
| 176 default_hostname = purple_certificate_get_subject_name(crt); |
171 info = purple_tls_certificate_get_info(crt); |
| |
172 default_hostname = purple_tls_certificate_info_get_subject_name(info); |
| |
173 purple_tls_certificate_info_free(info); |
| |
174 |
| 177 /* TODO: Find a way to make sure that crt gets destroyed |
175 /* TODO: Find a way to make sure that crt gets destroyed |
| 178 if the window gets closed unusually, such as by handle |
176 if the window gets closed unusually, such as by handle |
| 179 deletion */ |
177 deletion */ |
| 180 /* TODO: Display some more information on the certificate? */ |
178 /* TODO: Display some more information on the certificate? */ |
| 181 purple_request_input(tpm_dat, |
179 purple_request_input(tpm_dat, |
| 199 /* Errors! Oh no! */ |
197 /* Errors! Oh no! */ |
| 200 /* TODO: Perhaps find a way to be specific about what just |
198 /* TODO: Perhaps find a way to be specific about what just |
| 201 went wrong? */ |
199 went wrong? */ |
| 202 gchar * secondary; |
200 gchar * secondary; |
| 203 |
201 |
| |
202 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
| |
203 "File %s couldn't be imported: %s", |
| |
204 filename, error->message); |
| |
205 g_clear_error(&error); |
| |
206 |
| 204 secondary = g_strdup_printf(_("File %s could not be imported.\nMake sure that the file is readable and in PEM format.\n"), filename); |
207 secondary = g_strdup_printf(_("File %s could not be imported.\nMake sure that the file is readable and in PEM format.\n"), filename); |
| 205 purple_notify_error(NULL, |
208 purple_notify_error(NULL, |
| 206 _("Certificate Import Error"), |
209 _("Certificate Import Error"), |
| 207 _("X.509 certificate import failed"), |
210 _("X.509 certificate import failed"), |
| 208 secondary, NULL); |
211 secondary, NULL); |
| 224 } |
227 } |
| 225 |
228 |
| 226 static void |
229 static void |
| 227 tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename) |
230 tls_peers_mgmt_export_ok_cb(gpointer data, const char *filename) |
| 228 { |
231 { |
| 229 PurpleCertificate *crt = (PurpleCertificate *) data; |
232 GTlsCertificate *crt = data; |
| |
233 gchar *pem = NULL; |
| |
234 GError *error = NULL; |
| 230 |
235 |
| 231 g_assert(filename); |
236 g_assert(filename); |
| 232 |
237 |
| 233 if (!purple_certificate_export(filename, crt)) { |
238 g_object_get(crt, "certificate-pem", &pem, NULL); |
| |
239 |
| |
240 if (!g_file_set_contents(filename, pem, -1, &error)) { |
| 234 /* Errors! Oh no! */ |
241 /* Errors! Oh no! */ |
| 235 /* TODO: Perhaps find a way to be specific about what just |
242 /* TODO: Perhaps find a way to be specific about what just |
| 236 went wrong? */ |
243 went wrong? */ |
| 237 gchar * secondary; |
244 gchar * secondary; |
| |
245 |
| |
246 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
| |
247 "File %s couldn't be exported: %s", |
| |
248 filename, error->message); |
| |
249 g_clear_error(&error); |
| 238 |
250 |
| 239 secondary = g_strdup_printf(_("Export to file %s failed.\nCheck that you have write permission to the target path\n"), filename); |
251 secondary = g_strdup_printf(_("Export to file %s failed.\nCheck that you have write permission to the target path\n"), filename); |
| 240 purple_notify_error(NULL, |
252 purple_notify_error(NULL, |
| 241 _("Certificate Export Error"), |
253 _("Certificate Export Error"), |
| 242 _("X.509 certificate export failed"), |
254 _("X.509 certificate export failed"), |
| 243 secondary, NULL); |
255 secondary, NULL); |
| 244 g_free(secondary); |
256 g_free(secondary); |
| 245 } |
257 } else { |
| 246 |
258 tls_peers_mgmt_repopulate_list(); |
| 247 purple_certificate_destroy(crt); |
259 } |
| 248 } |
260 |
| 249 |
261 g_free(pem); |
| 250 static void |
262 g_object_unref(crt); |
| 251 tls_peers_mgmt_export_cancel_cb(gpointer data, const char *filename) |
|
| 252 { |
|
| 253 PurpleCertificate *crt = (PurpleCertificate *) data; |
|
| 254 /* Pressing cancel just frees the duplicated certificate */ |
|
| 255 purple_certificate_destroy(crt); |
|
| 256 } |
263 } |
| 257 |
264 |
| 258 static void |
265 static void |
| 259 tls_peers_mgmt_export_cb(GtkWidget *button, gpointer data) |
266 tls_peers_mgmt_export_cb(GtkWidget *button, gpointer data) |
| 260 { |
267 { |
| 261 PurpleCertificate *crt; |
268 GTlsCertificate *crt; |
| 262 GtkTreeSelection *select = tpm_dat->listselect; |
269 GtkTreeSelection *select = tpm_dat->listselect; |
| 263 GtkTreeIter iter; |
270 GtkTreeIter iter; |
| 264 GtkTreeModel *model; |
271 GtkTreeModel *model; |
| 265 gchar *id; |
272 gchar *id; |
| |
273 gchar *path; |
| |
274 GError *error = NULL; |
| 266 |
275 |
| 267 /* See if things are selected */ |
276 /* See if things are selected */ |
| 268 if (!gtk_tree_selection_get_selected(select, &model, &iter)) { |
277 if (!gtk_tree_selection_get_selected(select, &model, &iter)) { |
| 269 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
278 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
| 270 "Export clicked with no selection?\n"); |
279 "Export clicked with no selection?\n"); |
| 274 /* Retrieve the selected hostname */ |
283 /* Retrieve the selected hostname */ |
| 275 gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1); |
284 gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1); |
| 276 |
285 |
| 277 /* Extract the certificate from the pool now to make sure it doesn't |
286 /* Extract the certificate from the pool now to make sure it doesn't |
| 278 get deleted out from under us */ |
287 get deleted out from under us */ |
| 279 crt = purple_certificate_pool_retrieve(tpm_dat->tls_peers, id); |
288 crt = purple_tls_certificate_new_from_id(id, &error); |
| 280 |
289 |
| 281 if (NULL == crt) { |
290 if (NULL == crt) { |
| 282 purple_debug_error("gtkcertmgr/tls_peers_mgmt", |
291 purple_debug_error("gtkcertmgr/tls_peers_mgmt", |
| 283 "Id %s was not in the peers cache?!\n", |
292 "Error fetching trusted cert '%s': %s\n", |
| 284 id); |
293 id, error->message); |
| |
294 g_clear_error(&error); |
| 285 g_free(id); |
295 g_free(id); |
| 286 return; |
296 return; |
| 287 } |
297 } |
| 288 g_free(id); |
298 g_free(id); |
| 289 |
299 |
| 291 purple_request_file(tpm_dat, |
301 purple_request_file(tpm_dat, |
| 292 _("PEM X.509 Certificate Export"), |
302 _("PEM X.509 Certificate Export"), |
| 293 "certificate.pem", |
303 "certificate.pem", |
| 294 TRUE, /* Is a save dialog */ |
304 TRUE, /* Is a save dialog */ |
| 295 G_CALLBACK(tls_peers_mgmt_export_ok_cb), |
305 G_CALLBACK(tls_peers_mgmt_export_ok_cb), |
| 296 G_CALLBACK(tls_peers_mgmt_export_cancel_cb), |
306 G_CALLBACK(g_object_unref), |
| 297 NULL, /* No extra parameters */ |
307 NULL, /* No extra parameters */ |
| 298 crt); /* Pass the certificate on to the callback */ |
308 crt); /* Pass the certificate on to the callback */ |
| 299 } |
309 } |
| 300 |
310 |
| 301 static void |
311 static void |
| 303 { |
313 { |
| 304 GtkTreeSelection *select = tpm_dat->listselect; |
314 GtkTreeSelection *select = tpm_dat->listselect; |
| 305 GtkTreeIter iter; |
315 GtkTreeIter iter; |
| 306 GtkTreeModel *model; |
316 GtkTreeModel *model; |
| 307 gchar *id; |
317 gchar *id; |
| 308 PurpleCertificate *crt; |
318 GTlsCertificate *crt; |
| 309 char *title; |
319 char *title; |
| |
320 GError *error = NULL; |
| 310 |
321 |
| 311 /* See if things are selected */ |
322 /* See if things are selected */ |
| 312 if (!gtk_tree_selection_get_selected(select, &model, &iter)) { |
323 if (!gtk_tree_selection_get_selected(select, &model, &iter)) { |
| 313 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
324 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
| 314 "Info clicked with no selection?\n"); |
325 "Info clicked with no selection?\n"); |
| 317 |
328 |
| 318 /* Retrieve the selected hostname */ |
329 /* Retrieve the selected hostname */ |
| 319 gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1); |
330 gtk_tree_model_get(model, &iter, TPM_HOSTNAME_COLUMN, &id, -1); |
| 320 |
331 |
| 321 /* Now retrieve the certificate */ |
332 /* Now retrieve the certificate */ |
| 322 crt = purple_certificate_pool_retrieve(tpm_dat->tls_peers, id); |
333 crt = purple_tls_certificate_new_from_id(id, NULL); |
| 323 g_return_if_fail(crt); |
334 |
| |
335 if (crt == NULL) { |
| |
336 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
| |
337 "Unable to fetch certificate '%s': %s", |
| |
338 id, error->message); |
| |
339 g_clear_error(&error); |
| |
340 g_free(id); |
| |
341 } |
| 324 |
342 |
| 325 /* Fire the notification */ |
343 /* Fire the notification */ |
| 326 title = g_strdup_printf(_("Certificate Information for %s"), id); |
344 title = g_strdup_printf(_("Certificate Information for %s"), id); |
| 327 purple_request_certificate(tpm_dat, title, NULL, NULL, crt, |
345 purple_request_certificate(tpm_dat, title, NULL, NULL, crt, |
| 328 _("OK"), G_CALLBACK(purple_certificate_destroy), |
346 _("OK"), G_CALLBACK(g_object_unref), |
| 329 _("Cancel"), G_CALLBACK(purple_certificate_destroy), |
347 _("Cancel"), G_CALLBACK(g_object_unref), |
| 330 crt); |
348 crt); |
| 331 |
349 |
| 332 g_free(id); |
350 g_free(id); |
| 333 g_free(title); |
351 g_free(title); |
| 334 } |
352 } |
| 340 } |
358 } |
| 341 |
359 |
| 342 static void |
360 static void |
| 343 tls_peers_mgmt_delete_confirm_cb(gchar *id, gint choice) |
361 tls_peers_mgmt_delete_confirm_cb(gchar *id, gint choice) |
| 344 { |
362 { |
| |
363 GError *error = NULL; |
| |
364 |
| 345 if (1 == choice) { |
365 if (1 == choice) { |
| 346 /* Yes, delete was confirmed */ |
366 /* Yes, distrust was confirmed */ |
| 347 /* Now delete the thing */ |
367 /* Now distrust the thing */ |
| 348 if (!purple_certificate_pool_delete(tpm_dat->tls_peers, id)) { |
368 if (!purple_tls_certificate_distrust(id, &error)) { |
| 349 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
369 purple_debug_warning("gtkcertmgr/tls_peers_mgmt", |
| 350 "Deletion failed on id %s\n", |
370 "Deletion failed on id %s: %s\n", |
| 351 id); |
371 id, error->message); |
| 352 }; |
372 g_clear_error(&error); |
| |
373 } else { |
| |
374 tls_peers_mgmt_repopulate_list(); |
| |
375 } |
| 353 } |
376 } |
| 354 |
377 |
| 355 g_free(id); |
378 g_free(id); |
| 356 } |
379 } |
| 357 |
380 |
| 516 |
539 |
| 517 /* Call the "selection changed" callback, which will probably disable |
540 /* Call the "selection changed" callback, which will probably disable |
| 518 all the buttons since nothing is selected yet */ |
541 all the buttons since nothing is selected yet */ |
| 519 tls_peers_mgmt_select_chg_cb(select, NULL); |
542 tls_peers_mgmt_select_chg_cb(select, NULL); |
| 520 |
543 |
| 521 /* Bind us to the tls_peers pool */ |
|
| 522 tpm_dat->tls_peers = purple_certificate_find_pool("x509", "tls_peers"); |
|
| 523 |
|
| 524 /**** libpurple signals ****/ |
|
| 525 /* Respond to certificate add/remove by just reloading everything */ |
|
| 526 purple_signal_connect(tpm_dat->tls_peers, "certificate-stored", |
|
| 527 tpm_dat, PURPLE_CALLBACK(tls_peers_mgmt_mod_cb), |
|
| 528 NULL); |
|
| 529 purple_signal_connect(tpm_dat->tls_peers, "certificate-deleted", |
|
| 530 tpm_dat, PURPLE_CALLBACK(tls_peers_mgmt_mod_cb), |
|
| 531 NULL); |
|
| 532 |
|
| 533 return mgmt_widget; |
544 return mgmt_widget; |
| 534 } |
545 } |
| 535 |
546 |
| 536 const PidginCertificateManager tls_peers_mgmt = { |
547 const PidginCertificateManager tls_peers_mgmt = { |
| 537 tls_peers_mgmt_build, /* Widget creation function */ |
548 tls_peers_mgmt_build, /* Widget creation function */ |
| 569 GtkWidget *win; |
580 GtkWidget *win; |
| 570 GtkWidget *vbox; |
581 GtkWidget *vbox; |
| 571 |
582 |
| 572 /* Enumerate all the certificates on file */ |
583 /* Enumerate all the certificates on file */ |
| 573 { |
584 { |
| 574 GList *idlist, *poollist; |
585 GList *idlist; |
| 575 |
586 GList *l; |
| 576 for ( poollist = purple_certificate_get_pools(); |
587 |
| 577 poollist; |
588 purple_debug_info("gtkcertmgr", |
| 578 poollist = poollist->next ) { |
589 "Enumerating X.509 certificates:\n"); |
| 579 PurpleCertificatePool *pool = poollist->data; |
590 |
| 580 GList *l; |
591 idlist = purple_tls_certificate_list_ids(); |
| 581 |
592 |
| |
593 for (l=idlist; l; l = l->next) { |
| 582 purple_debug_info("gtkcertmgr", |
594 purple_debug_info("gtkcertmgr", |
| 583 "Pool %s found for scheme %s -" |
595 "- %s\n", |
| 584 "Enumerating certificates:\n", |
596 l->data ? (gchar *) l->data : "(null)"); |
| 585 pool->name, pool->scheme_name); |
597 } /* idlist */ |
| 586 |
598 |
| 587 idlist = purple_certificate_pool_get_idlist(pool); |
599 purple_tls_certificate_free_ids(idlist); |
| 588 |
|
| 589 for (l=idlist; l; l = l->next) { |
|
| 590 purple_debug_info("gtkcertmgr", |
|
| 591 "- %s\n", |
|
| 592 l->data ? (gchar *) l->data : "(null)"); |
|
| 593 } /* idlist */ |
|
| 594 purple_certificate_pool_destroy_idlist(idlist); |
|
| 595 } /* poollist */ |
|
| 596 } |
600 } |
| 597 |
601 |
| 598 |
602 |
| 599 /* If the manager is already open, bring it to the front */ |
603 /* If the manager is already open, bring it to the front */ |
| 600 if (certmgr_dialog != NULL) { |
604 if (certmgr_dialog != NULL) { |