comparison libpurple/certificate.c @ 32819:2c6510167895 default tip

propagate from branch 'im.pidgin.pidgin.2.x.y' (head 3315c5dfbd0ad16511bdcf865e5b07c02d07df24) to branch 'im.pidgin.pidgin' (head cbd1eda6bcbf0565ae7766396bb8f6f419cb6a9a)
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Sat, 02 Jun 2012 02:30:49 +0000
parents 32642aa8dbe5
children
comparison
equal deleted inserted replaced
32818:01ff09d4a463 32819:2c6510167895
273 273
274 return (scheme->signed_by)(crt, issuer); 274 return (scheme->signed_by)(crt, issuer);
275 } 275 }
276 276
277 gboolean 277 gboolean
278 purple_certificate_check_signature_chain_with_failing(GList *chain, 278 purple_certificate_check_signature_chain(GList *chain,
279 PurpleCertificate **failing) 279 PurpleCertificate **failing)
280 { 280 {
281 GList *cur; 281 GList *cur;
282 PurpleCertificate *crt, *issuer; 282 PurpleCertificate *crt, *issuer;
283 gchar *uid; 283 gchar *uid;
361 /* If control reaches this point, the chain is valid */ 361 /* If control reaches this point, the chain is valid */
362 purple_debug_info("certificate", "Chain is VALID\n"); 362 purple_debug_info("certificate", "Chain is VALID\n");
363 return TRUE; 363 return TRUE;
364 } 364 }
365 365
366 gboolean
367 purple_certificate_check_signature_chain(GList *chain)
368 {
369 return purple_certificate_check_signature_chain_with_failing(chain, NULL);
370 }
371
372 PurpleCertificate * 366 PurpleCertificate *
373 purple_certificate_import(PurpleCertificateScheme *scheme, const gchar *filename) 367 purple_certificate_import(PurpleCertificateScheme *scheme, const gchar *filename)
374 { 368 {
375 g_return_val_if_fail(scheme, NULL); 369 g_return_val_if_fail(scheme, NULL);
376 g_return_val_if_fail(scheme->import_certificate, NULL); 370 g_return_val_if_fail(scheme->import_certificate, NULL);
501 this? */ 495 this? */
502 g_return_val_if_fail( (activation != NULL) || (expiration != NULL), FALSE); 496 g_return_val_if_fail( (activation != NULL) || (expiration != NULL), FALSE);
503 497
504 /* Throw the request on down to the certscheme */ 498 /* Throw the request on down to the certscheme */
505 return (scheme->get_times)(crt, activation, expiration); 499 return (scheme->get_times)(crt, activation, expiration);
500 }
501
502 GByteArray *
503 purple_certificate_get_der_data(PurpleCertificate *crt)
504 {
505 PurpleCertificateScheme *scheme;
506 GByteArray *data;
507
508 g_return_val_if_fail(crt, NULL);
509 g_return_val_if_fail(crt->scheme, NULL);
510
511 scheme = crt->scheme;
512
513 g_return_val_if_fail(scheme->get_der_data, NULL);
514
515 data = (scheme->get_der_data)(crt);
516
517 return data;
518 }
519
520 gchar *
521 purple_certificate_get_display_string(PurpleCertificate *crt)
522 {
523 PurpleCertificateScheme *scheme;
524 gchar *str;
525
526 g_return_val_if_fail(crt, NULL);
527 g_return_val_if_fail(crt->scheme, NULL);
528
529 scheme = crt->scheme;
530
531 g_return_val_if_fail(scheme->get_display_string, NULL);
532
533 str = (scheme->get_display_string)(crt);
534
535 return str;
506 } 536 }
507 537
508 gchar * 538 gchar *
509 purple_certificate_pool_mkpath(PurpleCertificatePool *pool, const gchar *id) 539 purple_certificate_pool_mkpath(PurpleCertificatePool *pool, const gchar *id)
510 { 540 {
648 /****************************************************************************/ 678 /****************************************************************************/
649 /* Builtin Verifiers, Pools, etc. */ 679 /* Builtin Verifiers, Pools, etc. */
650 /****************************************************************************/ 680 /****************************************************************************/
651 681
652 static void 682 static void
653 x509_singleuse_verify_cb (PurpleCertificateVerificationRequest *vrq, gint id) 683 x509_singleuse_verify_accept_cb(PurpleCertificateVerificationRequest *vrq)
654 { 684 {
655 g_return_if_fail(vrq); 685 g_return_if_fail(vrq);
656 686
657 purple_debug_info("certificate/x509_singleuse", 687 purple_debug_info("certificate/x509_singleuse",
658 "VRQ on cert from %s gave %d\n", 688 "VRQ on cert from %s accepted\n",
659 vrq->subject_name, id); 689 vrq->subject_name);
660 690
661 /* Signal what happened back to the caller */ 691 purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
662 if (1 == id) { 692 }
663 /* Accepted! */ 693
664 purple_certificate_verify_complete(vrq, 694 static void
665 PURPLE_CERTIFICATE_VALID); 695 x509_singleuse_verify_reject_cb(PurpleCertificateVerificationRequest *vrq)
666 } else { 696 {
667 /* Not accepted */ 697 g_return_if_fail(vrq);
668 purple_certificate_verify_complete(vrq, 698
669 PURPLE_CERTIFICATE_INVALID); 699 purple_debug_info("certificate/x509_singleuse",
670 700 "VRQ on cert from %s rejected\n",
671 } 701 vrq->subject_name);
702
703 purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
672 } 704 }
673 705
674 static void 706 static void
675 x509_singleuse_start_verify (PurpleCertificateVerificationRequest *vrq) 707 x509_singleuse_start_verify (PurpleCertificateVerificationRequest *vrq)
676 { 708 {
677 gchar *sha_asc;
678 GByteArray *sha_bin;
679 gchar *cn; 709 gchar *cn;
680 const gchar *cn_match; 710 const gchar *cn_match;
681 gchar *primary, *secondary; 711 gchar *primary, *secondary;
682 PurpleCertificate *crt = (PurpleCertificate *) vrq->cert_chain->data; 712 PurpleCertificate *crt = (PurpleCertificate *)vrq->cert_chain->data;
683 713
684 /* Pull out the SHA1 checksum */
685 sha_bin = purple_certificate_get_fingerprint_sha1(crt);
686 /* Now decode it for display */
687 sha_asc = purple_base16_encode_chunked(sha_bin->data,
688 sha_bin->len);
689
690 /* Get the cert Common Name */
691 cn = purple_certificate_get_subject_name(crt); 714 cn = purple_certificate_get_subject_name(crt);
692 715
693 /* Determine whether the name matches */
694 if (purple_certificate_check_subject_name(crt, vrq->subject_name)) { 716 if (purple_certificate_check_subject_name(crt, vrq->subject_name)) {
695 cn_match = ""; 717 cn_match = _("(MATCH)");
696 } else { 718 } else {
697 cn_match = _("(DOES NOT MATCH)"); 719 cn_match = _("(DOES NOT MATCH)");
698 } 720 }
699 721
700 /* Make messages */
701 primary = g_strdup_printf(_("%s has presented the following certificate for just-this-once use:"), vrq->subject_name); 722 primary = g_strdup_printf(_("%s has presented the following certificate for just-this-once use:"), vrq->subject_name);
702 secondary = g_strdup_printf(_("Common name: %s %s\nFingerprint (SHA1): %s"), cn, cn_match, sha_asc); 723 secondary = g_strdup_printf(_("Common name: %s %s"), cn, cn_match);
703 724
704 /* Make a semi-pretty display */ 725 /* Make a semi-pretty display */
705 purple_request_accept_cancel( 726 purple_request_certificate(
706 vrq->cb_data, /* TODO: Find what the handle ought to be */ 727 vrq->cb_data, /* TODO: Find what the handle ought to be */
707 _("Single-use Certificate Verification"), 728 _("Single-use Certificate Verification"),
708 primary, 729 primary,
709 secondary, 730 secondary,
710 0, /* Accept by default */ 731 crt,
711 NULL, /* No account */ 732 _("Accept"), G_CALLBACK(x509_singleuse_verify_accept_cb),
712 NULL, /* No other user */ 733 _("Reject"), G_CALLBACK(x509_singleuse_verify_reject_cb),
713 NULL, /* No associated conversation */ 734 vrq);
714 vrq, 735
715 x509_singleuse_verify_cb,
716 x509_singleuse_verify_cb );
717
718 /* Cleanup */
719 g_free(cn); 736 g_free(cn);
720 g_free(primary); 737 g_free(primary);
721 g_free(secondary); 738 g_free(secondary);
722 g_free(sha_asc);
723 g_byte_array_free(sha_bin, TRUE);
724 } 739 }
725 740
726 static void 741 static void
727 x509_singleuse_destroy_request (PurpleCertificateVerificationRequest *vrq) 742 x509_singleuse_destroy_request (PurpleCertificateVerificationRequest *vrq)
728 { 743 {
1275 1290
1276 /***** A Verifier that uses the tls_peers cache and the CA pool to validate certificates *****/ 1291 /***** A Verifier that uses the tls_peers cache and the CA pool to validate certificates *****/
1277 static PurpleCertificateVerifier x509_tls_cached; 1292 static PurpleCertificateVerifier x509_tls_cached;
1278 1293
1279 1294
1280 /* The following is several hacks piled together and needs to be fixed.
1281 * It exists because show_cert (see its comments) needs the original reason
1282 * given to user_auth in order to rebuild the dialog.
1283 */
1284 /* TODO: This will cause a ua_ctx to become memleaked if the request(s) get
1285 closed by handle or otherwise abnormally. */
1286 typedef struct {
1287 PurpleCertificateVerificationRequest *vrq;
1288 gchar *reason;
1289 } x509_tls_cached_ua_ctx;
1290
1291 static x509_tls_cached_ua_ctx *
1292 x509_tls_cached_ua_ctx_new(PurpleCertificateVerificationRequest *vrq,
1293 const gchar *reason)
1294 {
1295 x509_tls_cached_ua_ctx *c;
1296
1297 c = g_new0(x509_tls_cached_ua_ctx, 1);
1298 c->vrq = vrq;
1299 c->reason = g_strdup(reason);
1300
1301 return c;
1302 }
1303
1304
1305 static void 1295 static void
1306 x509_tls_cached_ua_ctx_free(x509_tls_cached_ua_ctx *c) 1296 x509_tls_cached_user_auth_accept_cb(PurpleCertificateVerificationRequest *vrq)
1307 { 1297 {
1308 g_return_if_fail(c);
1309 g_free(c->reason);
1310 g_free(c);
1311 }
1312
1313 static void
1314 x509_tls_cached_user_auth(PurpleCertificateVerificationRequest *vrq,
1315 const gchar *reason);
1316
1317 static void
1318 x509_tls_cached_show_cert(x509_tls_cached_ua_ctx *c, gint id)
1319 {
1320 PurpleCertificate *disp_crt = c->vrq->cert_chain->data;
1321
1322 /* Since clicking a button closes the request, show it again */
1323 x509_tls_cached_user_auth(c->vrq, c->reason);
1324
1325 /* Show the certificate AFTER re-opening the dialog so that this
1326 appears above the other */
1327 purple_certificate_display_x509(disp_crt);
1328
1329 x509_tls_cached_ua_ctx_free(c);
1330 }
1331
1332 static void
1333 x509_tls_cached_user_auth_cb (x509_tls_cached_ua_ctx *c, gint id)
1334 {
1335 PurpleCertificateVerificationRequest *vrq;
1336 PurpleCertificatePool *tls_peers; 1298 PurpleCertificatePool *tls_peers;
1337 1299 gchar *cache_id;
1338 g_return_if_fail(c); 1300
1339 g_return_if_fail(c->vrq); 1301 g_return_if_fail(vrq);
1340 1302
1341 vrq = c->vrq; 1303 tls_peers = purple_certificate_find_pool("x509", "tls_peers");
1342 1304
1343 x509_tls_cached_ua_ctx_free(c); 1305 cache_id = vrq->subject_name;
1344 1306 purple_debug_info("certificate/x509/tls_cached",
1345 tls_peers = purple_certificate_find_pool("x509","tls_peers");
1346
1347 if (2 == id) {
1348 gchar *cache_id = vrq->subject_name;
1349 purple_debug_info("certificate/x509/tls_cached",
1350 "User ACCEPTED cert\nCaching first in chain for future use as %s...\n", 1307 "User ACCEPTED cert\nCaching first in chain for future use as %s...\n",
1351 cache_id); 1308 cache_id);
1352 1309
1353 purple_certificate_pool_store(tls_peers, cache_id, 1310 purple_certificate_pool_store(tls_peers, cache_id, vrq->cert_chain->data);
1354 vrq->cert_chain->data); 1311
1355 1312 purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID);
1356 purple_certificate_verify_complete(vrq,
1357 PURPLE_CERTIFICATE_VALID);
1358 } else {
1359 purple_debug_warning("certificate/x509/tls_cached",
1360 "User REJECTED cert\n");
1361 purple_certificate_verify_complete(vrq,
1362 PURPLE_CERTIFICATE_INVALID);
1363 }
1364 } 1313 }
1365 1314
1366 static void 1315 static void
1367 x509_tls_cached_user_auth_accept_cb(x509_tls_cached_ua_ctx *c, gint ignore) 1316 x509_tls_cached_user_auth_reject_cb(PurpleCertificateVerificationRequest *vrq)
1368 { 1317 {
1369 x509_tls_cached_user_auth_cb(c, 2); 1318 g_return_if_fail(vrq);
1370 } 1319
1371 1320 purple_debug_warning("certificate/x509/tls_cached", "User REJECTED cert\n");
1372 static void 1321
1373 x509_tls_cached_user_auth_reject_cb(x509_tls_cached_ua_ctx *c, gint ignore) 1322 purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID);
1374 {
1375 x509_tls_cached_user_auth_cb(c, 1);
1376 } 1323 }
1377 1324
1378 /** Validates a certificate by asking the user 1325 /** Validates a certificate by asking the user
1379 * @param reason String to explain why the user needs to accept/refuse the 1326 * @param reason String to explain why the user needs to accept/refuse the
1380 * certificate. 1327 * certificate.
1384 x509_tls_cached_user_auth(PurpleCertificateVerificationRequest *vrq, 1331 x509_tls_cached_user_auth(PurpleCertificateVerificationRequest *vrq,
1385 const gchar *reason) 1332 const gchar *reason)
1386 { 1333 {
1387 gchar *primary; 1334 gchar *primary;
1388 1335
1389 /* Make messages */
1390 primary = g_strdup_printf(_("Accept certificate for %s?"), 1336 primary = g_strdup_printf(_("Accept certificate for %s?"),
1391 vrq->subject_name); 1337 vrq->subject_name);
1392 1338
1393 /* Make a semi-pretty display */ 1339 purple_request_certificate(
1394 purple_request_action(
1395 vrq->cb_data, /* TODO: Find what the handle ought to be */ 1340 vrq->cb_data, /* TODO: Find what the handle ought to be */
1396 _("SSL Certificate Verification"), 1341 _("SSL Certificate Verification"),
1397 primary, 1342 primary,
1398 reason, 1343 reason,
1399 0, /* Accept by default */ 1344 vrq->cert_chain->data,
1400 NULL, /* No account */ 1345 _("Accept"), G_CALLBACK(x509_tls_cached_user_auth_accept_cb),
1401 NULL, /* No other user */ 1346 _("Reject"), G_CALLBACK(x509_tls_cached_user_auth_reject_cb),
1402 NULL, /* No associated conversation */ 1347 vrq);
1403 x509_tls_cached_ua_ctx_new(vrq, reason), 1348
1404 3, /* Number of actions */
1405 _("Accept"), x509_tls_cached_user_auth_accept_cb,
1406 _("Reject"), x509_tls_cached_user_auth_reject_cb,
1407 _("_View Certificate..."), x509_tls_cached_show_cert);
1408
1409 /* Cleanup */
1410 g_free(primary); 1349 g_free(primary);
1411 } 1350 }
1412 1351
1413 static void 1352 static void
1414 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq, 1353 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq,
1620 } /* if (self signed) */ 1559 } /* if (self signed) */
1621 1560
1622 ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca"); 1561 ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca");
1623 1562
1624 /* Next, check that the certificate chain is valid */ 1563 /* Next, check that the certificate chain is valid */
1625 if (!purple_certificate_check_signature_chain_with_failing(chain, 1564 if (!purple_certificate_check_signature_chain(chain,
1626 &failing_crt)) 1565 &failing_crt))
1627 { 1566 {
1628 gboolean chain_validated = FALSE; 1567 gboolean chain_validated = FALSE;
1629 /* 1568 /*
1630 * Check if the failing certificate is in the CA store. If it is, then 1569 * Check if the failing certificate is in the CA store. If it is, then
1702 g_free(ca2_id); 1641 g_free(ca2_id);
1703 if ( NULL == ca_crts ) { 1642 if ( NULL == ca_crts ) {
1704 flags |= PURPLE_CERTIFICATE_CA_UNKNOWN; 1643 flags |= PURPLE_CERTIFICATE_CA_UNKNOWN;
1705 1644
1706 purple_debug_warning("certificate/x509/tls_cached", 1645 purple_debug_warning("certificate/x509/tls_cached",
1707 "No Certificate Authorities with either DN found " 1646 "No Certificate Authorities with either DN "
1708 "found. I'll prompt the user, I guess.\n"); 1647 "found. I'll prompt the user, I guess.\n");
1709 1648
1710 x509_tls_cached_check_subject_name(vrq, flags); 1649 x509_tls_cached_check_subject_name(vrq, flags);
1711 return; 1650 return;
1712 } 1651 }
2151 2090
2152 /****************************************************************************/ 2091 /****************************************************************************/
2153 /* Scheme-specific functions */ 2092 /* Scheme-specific functions */
2154 /****************************************************************************/ 2093 /****************************************************************************/
2155 2094
2156 void
2157 purple_certificate_display_x509(PurpleCertificate *crt)
2158 {
2159 gchar *sha_asc;
2160 GByteArray *sha_bin;
2161 gchar *cn;
2162 time_t activation, expiration;
2163 gchar *activ_str, *expir_str;
2164 gchar *secondary;
2165
2166 /* Pull out the SHA1 checksum */
2167 sha_bin = purple_certificate_get_fingerprint_sha1(crt);
2168 /* Now decode it for display */
2169 sha_asc = purple_base16_encode_chunked(sha_bin->data,
2170 sha_bin->len);
2171
2172 /* Get the cert Common Name */
2173 /* TODO: Will break on CA certs */
2174 cn = purple_certificate_get_subject_name(crt);
2175
2176 /* Get the certificate times */
2177 /* TODO: Check the times against localtime */
2178 /* TODO: errorcheck? */
2179 if (!purple_certificate_get_times(crt, &activation, &expiration)) {
2180 purple_debug_error("certificate",
2181 "Failed to get certificate times!\n");
2182 activation = expiration = 0;
2183 }
2184 activ_str = g_strdup(ctime(&activation));
2185 expir_str = g_strdup(ctime(&expiration));
2186
2187 /* Make messages */
2188 secondary = g_strdup_printf(_("Common name: %s\n\n"
2189 "Fingerprint (SHA1): %s\n\n"
2190 "Activation date: %s\n"
2191 "Expiration date: %s\n"),
2192 cn ? cn : "(null)",
2193 sha_asc ? sha_asc : "(null)",
2194 activ_str ? activ_str : "(null)",
2195 expir_str ? expir_str : "(null)");
2196
2197 /* Make a semi-pretty display */
2198 purple_notify_info(
2199 NULL, /* TODO: Find what the handle ought to be */
2200 _("Certificate Information"),
2201 "",
2202 secondary);
2203
2204 /* Cleanup */
2205 g_free(cn);
2206 g_free(secondary);
2207 g_free(sha_asc);
2208 g_free(activ_str);
2209 g_free(expir_str);
2210 g_byte_array_free(sha_bin, TRUE);
2211 }
2212
2213 void purple_certificate_add_ca_search_path(const char *path) 2095 void purple_certificate_add_ca_search_path(const char *path)
2214 { 2096 {
2215 if (g_list_find_custom(x509_ca_paths, path, (GCompareFunc)strcmp)) 2097 if (g_list_find_custom(x509_ca_paths, path, (GCompareFunc)strcmp))
2216 return; 2098 return;
2217 x509_ca_paths = g_list_append(x509_ca_paths, g_strdup(path)); 2099 x509_ca_paths = g_list_append(x509_ca_paths, g_strdup(path));