Mercurial > pidgin
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)); |