Mercurial > pidgin.yaz
comparison libpurple/certificate.c @ 28070:d0654dea0575
certs: Clean up the code a little, since I made it hard to follow.
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Thu, 30 Jul 2009 05:24:44 +0000 |
parents | 99baf778e0b9 |
children | 585684d297a3 f834ffa7490b 32956c0536b9 b131c68822ce |
comparison
equal
deleted
inserted
replaced
28069:82ac0bef7d89 | 28070:d0654dea0575 |
---|---|
1316 purple_certificate_destroy(cached_crt); | 1316 purple_certificate_destroy(cached_crt); |
1317 g_byte_array_free(peer_fpr, TRUE); | 1317 g_byte_array_free(peer_fpr, TRUE); |
1318 g_byte_array_free(cached_fpr, TRUE); | 1318 g_byte_array_free(cached_fpr, TRUE); |
1319 } | 1319 } |
1320 | 1320 |
1321 /* | |
1322 * This is called from two points in x509_tls_cached_unknown_peer below | |
1323 * once we've verified the signature chain is valid. Now we need to verify | |
1324 * the subject name of the certificate. | |
1325 */ | |
1326 static void | |
1327 x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest *vrq) | |
1328 { | |
1329 PurpleCertificatePool *tls_peers; | |
1330 PurpleCertificate *peer_crt; | |
1331 GList *chain = vrq->cert_chain; | |
1332 | |
1333 peer_crt = (PurpleCertificate *) chain->data; | |
1334 | |
1335 /* Last, check that the hostname matches */ | |
1336 if ( ! purple_certificate_check_subject_name(peer_crt, | |
1337 vrq->subject_name) ) { | |
1338 gchar *sn = purple_certificate_get_subject_name(peer_crt); | |
1339 gchar *msg; | |
1340 | |
1341 purple_debug_error("certificate/x509/tls_cached", | |
1342 "Name mismatch: Certificate given for %s " | |
1343 "has a name of %s\n", | |
1344 vrq->subject_name, sn); | |
1345 | |
1346 /* Prompt the user to authenticate the certificate */ | |
1347 /* TODO: Provide the user with more guidance about why he is | |
1348 being prompted */ | |
1349 /* vrq will be completed by user_auth */ | |
1350 msg = g_strdup_printf(_("The certificate presented by \"%s\" " | |
1351 "claims to be from \"%s\" instead. " | |
1352 "This could mean that you are not " | |
1353 "connecting to the service you " | |
1354 "believe you are."), | |
1355 vrq->subject_name, sn); | |
1356 | |
1357 x509_tls_cached_user_auth(vrq,msg); | |
1358 | |
1359 g_free(sn); | |
1360 g_free(msg); | |
1361 return; | |
1362 } /* if (name mismatch) */ | |
1363 | |
1364 /* If we reach this point, the certificate is good. */ | |
1365 /* Look up the local cache and store it there for future use */ | |
1366 tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name, | |
1367 "tls_peers"); | |
1368 | |
1369 if (tls_peers) { | |
1370 if (!purple_certificate_pool_store(tls_peers,vrq->subject_name, | |
1371 peer_crt) ) { | |
1372 purple_debug_error("certificate/x509/tls_cached", | |
1373 "FAILED to cache peer certificate\n"); | |
1374 } | |
1375 } else { | |
1376 purple_debug_error("certificate/x509/tls_cached", | |
1377 "Unable to locate tls_peers certificate " | |
1378 "cache.\n"); | |
1379 } | |
1380 | |
1381 /* Whew! Done! */ | |
1382 purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID); | |
1383 | |
1384 } | |
1385 | |
1321 /* For when we've never communicated with this party before */ | 1386 /* For when we've never communicated with this party before */ |
1322 /* TODO: Need ways to specify possibly multiple problems with a cert, or at | 1387 /* TODO: Need ways to specify possibly multiple problems with a cert, or at |
1323 least reprioritize them. For example, maybe the signature ought to be | 1388 least reprioritize them. |
1324 checked BEFORE the hostname checking? | 1389 */ |
1325 Stu thinks we should check the signature before the name, so we do now. | |
1326 The above TODO still stands. */ | |
1327 static void | 1390 static void |
1328 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq) | 1391 x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq) |
1329 { | 1392 { |
1330 PurpleCertificatePool *ca, *tls_peers; | 1393 PurpleCertificatePool *ca; |
1331 PurpleCertificate *peer_crt; | 1394 PurpleCertificate *peer_crt; |
1395 PurpleCertificate *ca_crt, *end_crt; | |
1332 PurpleCertificate *failing_crt; | 1396 PurpleCertificate *failing_crt; |
1333 GList *chain = vrq->cert_chain; | 1397 GList *chain = vrq->cert_chain; |
1334 gboolean chain_validated = FALSE; | 1398 GByteArray *last_fpr, *ca_fpr; |
1399 gchar *ca_id; | |
1335 | 1400 |
1336 peer_crt = (PurpleCertificate *) chain->data; | 1401 peer_crt = (PurpleCertificate *) chain->data; |
1337 | 1402 |
1338 /* TODO: Figure out a way to check for a bad signature, as opposed to | 1403 /* TODO: Figure out a way to check for a bad signature, as opposed to |
1339 "not self-signed" */ | 1404 "not self-signed" */ |
1359 | 1424 |
1360 /* Next, attempt to verify the last certificate against a CA */ | 1425 /* Next, attempt to verify the last certificate against a CA */ |
1361 ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca"); | 1426 ca = purple_certificate_find_pool(x509_tls_cached.scheme_name, "ca"); |
1362 | 1427 |
1363 /* Next, check that the certificate chain is valid */ | 1428 /* Next, check that the certificate chain is valid */ |
1364 if (purple_certificate_check_signature_chain_with_failing(chain, | 1429 if (!purple_certificate_check_signature_chain_with_failing(chain, |
1365 &failing_crt)) | 1430 &failing_crt)) |
1366 chain_validated = TRUE; | 1431 { |
1367 else { | 1432 gboolean chain_validated = FALSE; |
1368 /* | 1433 /* |
1369 * Check if the failing certificate is in the CA store. If it is, then | 1434 * Check if the failing certificate is in the CA store. If it is, then |
1370 * consider this fully validated. This works around issues with some | 1435 * consider this fully validated. This works around issues with some |
1371 * prominent intermediate CAs whose signature is md5WithRSAEncryption. | 1436 * prominent intermediate CAs whose signature is md5WithRSAEncryption. |
1372 * I'm looking at CACert Class 3 here. See #4458 for details. | 1437 * I'm looking at CACert Class 3 here. See #4458 for details. |
1397 | 1462 |
1398 /* | 1463 /* |
1399 * If we get here, either the cert matched the stuff right above | 1464 * If we get here, either the cert matched the stuff right above |
1400 * or it didn't, in which case we give up and complain to the user. | 1465 * or it didn't, in which case we give up and complain to the user. |
1401 */ | 1466 */ |
1402 if (!chain_validated) { | 1467 if (chain_validated) { |
1468 x509_tls_cached_check_subject_name(vrq); | |
1469 } else { | |
1403 /* TODO: Tell the user where the chain broke? */ | 1470 /* TODO: Tell the user where the chain broke? */ |
1404 /* TODO: This error will hopelessly confuse any | 1471 /* TODO: This error will hopelessly confuse any |
1405 non-elite user. */ | 1472 non-elite user. */ |
1406 gchar *secondary; | 1473 gchar *secondary; |
1407 | 1474 |
1419 g_free(secondary); | 1486 g_free(secondary); |
1420 | 1487 |
1421 /* Okay, we're done here */ | 1488 /* Okay, we're done here */ |
1422 purple_certificate_verify_complete(vrq, | 1489 purple_certificate_verify_complete(vrq, |
1423 PURPLE_CERTIFICATE_INVALID); | 1490 PURPLE_CERTIFICATE_INVALID); |
1424 return; | |
1425 } | 1491 } |
1492 | |
1493 return; | |
1426 } /* if (signature chain not good) */ | 1494 } /* if (signature chain not good) */ |
1427 | 1495 |
1428 /* If, for whatever reason, there is no Certificate Authority pool | 1496 /* If, for whatever reason, there is no Certificate Authority pool |
1429 loaded, we will simply present it to the user for checking. */ | 1497 loaded, we will simply present it to the user for checking. */ |
1430 if ( !ca ) { | 1498 if ( !ca ) { |
1438 "certificate cannot be " | 1506 "certificate cannot be " |
1439 "validated.")); | 1507 "validated.")); |
1440 return; | 1508 return; |
1441 } | 1509 } |
1442 | 1510 |
1443 if (!chain_validated) { | 1511 end_crt = g_list_last(chain)->data; |
1444 GByteArray *last_fpr, *ca_fpr; | 1512 |
1445 PurpleCertificate *ca_crt, *end_crt; | 1513 /* Attempt to look up the last certificate's issuer */ |
1446 gchar *ca_id; | 1514 ca_id = purple_certificate_get_issuer_unique_id(end_crt); |
1447 | 1515 purple_debug_info("certificate/x509/tls_cached", |
1448 end_crt = g_list_last(chain)->data; | 1516 "Checking for a CA with DN=%s\n", |
1449 | 1517 ca_id); |
1450 /* Attempt to look up the last certificate's issuer */ | 1518 ca_crt = purple_certificate_pool_retrieve(ca, ca_id); |
1451 ca_id = purple_certificate_get_issuer_unique_id(end_crt); | 1519 if ( NULL == ca_crt ) { |
1452 purple_debug_info("certificate/x509/tls_cached", | 1520 purple_debug_warning("certificate/x509/tls_cached", |
1453 "Checking for a CA with DN=%s\n", | 1521 "Certificate Authority with DN='%s' not " |
1522 "found. I'll prompt the user, I guess.\n", | |
1454 ca_id); | 1523 ca_id); |
1455 ca_crt = purple_certificate_pool_retrieve(ca, ca_id); | |
1456 if ( NULL == ca_crt ) { | |
1457 purple_debug_warning("certificate/x509/tls_cached", | |
1458 "Certificate Authority with DN='%s' not " | |
1459 "found. I'll prompt the user, I guess.\n", | |
1460 ca_id); | |
1461 g_free(ca_id); | |
1462 /* vrq will be completed by user_auth */ | |
1463 x509_tls_cached_user_auth(vrq,_("The root certificate this " | |
1464 "one claims to be issued by " | |
1465 "is unknown to Pidgin.")); | |
1466 return; | |
1467 } | |
1468 | |
1469 g_free(ca_id); | 1524 g_free(ca_id); |
1470 | 1525 /* vrq will be completed by user_auth */ |
1471 /* | 1526 x509_tls_cached_user_auth(vrq,_("The root certificate this " |
1472 * Check the fingerprints; if they match, then this certificate *is* one | 1527 "one claims to be issued by " |
1473 * of the designated "trusted roots", and we don't need to verify the | 1528 "is unknown to Pidgin.")); |
1474 * signature. This is good because some of the older roots are self-signed | 1529 return; |
1475 * with bad hash algorithms that we don't want to allow in any other | 1530 } |
1476 * circumstances (one of Verisign's root CAs is self-signed with MD2). | 1531 |
1477 * | 1532 g_free(ca_id); |
1478 * If the fingerprints don't match, we'll fall back to checking the | 1533 |
1479 * signature. | 1534 /* |
1480 * | 1535 * Check the fingerprints; if they match, then this certificate *is* one |
1481 * GnuTLS doesn't seem to include the final root in the verification | 1536 * of the designated "trusted roots", and we don't need to verify the |
1482 * list, so this check will never succeed. NSS *does* include it in | 1537 * signature. This is good because some of the older roots are self-signed |
1483 * the list, so here we are. | 1538 * with bad hash algorithms that we don't want to allow in any other |
1484 */ | 1539 * circumstances (one of Verisign's root CAs is self-signed with MD2). |
1485 last_fpr = purple_certificate_get_fingerprint_sha1(end_crt); | 1540 * |
1486 ca_fpr = purple_certificate_get_fingerprint_sha1(ca_crt); | 1541 * If the fingerprints don't match, we'll fall back to checking the |
1487 | 1542 * signature. |
1488 if ( !byte_arrays_equal(last_fpr, ca_fpr) && | 1543 * |
1489 !purple_certificate_signed_by(end_crt, ca_crt) ) | 1544 * GnuTLS doesn't seem to include the final root in the verification |
1490 { | 1545 * list, so this check will never succeed. NSS *does* include it in |
1491 /* TODO: If signed_by ever returns a reason, maybe mention | 1546 * the list, so here we are. |
1492 that, too. */ | 1547 */ |
1493 /* TODO: Also mention the CA involved. While I could do this | 1548 last_fpr = purple_certificate_get_fingerprint_sha1(end_crt); |
1494 now, a full DN is a little much with which to assault the | 1549 ca_fpr = purple_certificate_get_fingerprint_sha1(ca_crt); |
1495 user's poor, leaky eyes. */ | 1550 |
1496 /* TODO: This error message makes my eyes cross, and I wrote it */ | 1551 if ( !byte_arrays_equal(last_fpr, ca_fpr) && |
1497 gchar * secondary = | 1552 !purple_certificate_signed_by(end_crt, ca_crt) ) |
1498 g_strdup_printf(_("The certificate chain presented by " | 1553 { |
1499 "%s does not have a valid digital " | 1554 /* TODO: If signed_by ever returns a reason, maybe mention |
1500 "signature from the Certificate " | 1555 that, too. */ |
1501 "Authority from which it claims to " | 1556 /* TODO: Also mention the CA involved. While I could do this |
1502 "have a signature."), | 1557 now, a full DN is a little much with which to assault the |
1503 vrq->subject_name); | 1558 user's poor, leaky eyes. */ |
1504 | 1559 /* TODO: This error message makes my eyes cross, and I wrote it */ |
1505 purple_notify_error(NULL, /* TODO: Probably wrong */ | 1560 gchar * secondary = |
1506 _("SSL Certificate Error"), | 1561 g_strdup_printf(_("The certificate chain presented by " |
1507 _("Invalid certificate authority" | 1562 "%s does not have a valid digital " |
1508 " signature"), | 1563 "signature from the Certificate " |
1509 secondary); | 1564 "Authority from which it claims to " |
1510 g_free(secondary); | 1565 "have a signature."), |
1511 | 1566 vrq->subject_name); |
1512 /* Signal "bad cert" */ | 1567 |
1513 purple_certificate_verify_complete(vrq, | 1568 purple_notify_error(NULL, /* TODO: Probably wrong */ |
1514 PURPLE_CERTIFICATE_INVALID); | 1569 _("SSL Certificate Error"), |
1515 | 1570 _("Invalid certificate authority" |
1516 purple_certificate_destroy(ca_crt); | 1571 " signature"), |
1517 g_byte_array_free(ca_fpr, TRUE); | 1572 secondary); |
1518 g_byte_array_free(last_fpr, TRUE); | 1573 g_free(secondary); |
1519 return; | 1574 |
1520 } /* if (CA signature not good) */ | 1575 /* Signal "bad cert" */ |
1521 | 1576 purple_certificate_verify_complete(vrq, |
1577 PURPLE_CERTIFICATE_INVALID); | |
1578 | |
1579 purple_certificate_destroy(ca_crt); | |
1522 g_byte_array_free(ca_fpr, TRUE); | 1580 g_byte_array_free(ca_fpr, TRUE); |
1523 g_byte_array_free(last_fpr, TRUE); | 1581 g_byte_array_free(last_fpr, TRUE); |
1524 } | |
1525 | |
1526 /* Last, check that the hostname matches */ | |
1527 if ( ! purple_certificate_check_subject_name(peer_crt, | |
1528 vrq->subject_name) ) { | |
1529 gchar *sn = purple_certificate_get_subject_name(peer_crt); | |
1530 gchar *msg; | |
1531 | |
1532 purple_debug_error("certificate/x509/tls_cached", | |
1533 "Name mismatch: Certificate given for %s " | |
1534 "has a name of %s\n", | |
1535 vrq->subject_name, sn); | |
1536 | |
1537 /* Prompt the user to authenticate the certificate */ | |
1538 /* TODO: Provide the user with more guidance about why he is | |
1539 being prompted */ | |
1540 /* vrq will be completed by user_auth */ | |
1541 msg = g_strdup_printf(_("The certificate presented by \"%s\" " | |
1542 "claims to be from \"%s\" instead. " | |
1543 "This could mean that you are not " | |
1544 "connecting to the service you " | |
1545 "believe you are."), | |
1546 vrq->subject_name, sn); | |
1547 | |
1548 x509_tls_cached_user_auth(vrq,msg); | |
1549 | |
1550 g_free(sn); | |
1551 g_free(msg); | |
1552 return; | 1582 return; |
1553 } /* if (name mismatch) */ | 1583 } /* if (CA signature not good) */ |
1554 | 1584 |
1555 /* If we reach this point, the certificate is good. */ | 1585 g_byte_array_free(ca_fpr, TRUE); |
1556 /* Look up the local cache and store it there for future use */ | 1586 g_byte_array_free(last_fpr, TRUE); |
1557 tls_peers = purple_certificate_find_pool(x509_tls_cached.scheme_name, | 1587 |
1558 "tls_peers"); | 1588 x509_tls_cached_check_subject_name(vrq); |
1559 | |
1560 if (tls_peers) { | |
1561 if (!purple_certificate_pool_store(tls_peers,vrq->subject_name, | |
1562 peer_crt) ) { | |
1563 purple_debug_error("certificate/x509/tls_cached", | |
1564 "FAILED to cache peer certificate\n"); | |
1565 } | |
1566 } else { | |
1567 purple_debug_error("certificate/x509/tls_cached", | |
1568 "Unable to locate tls_peers certificate " | |
1569 "cache.\n"); | |
1570 } | |
1571 | |
1572 /* Whew! Done! */ | |
1573 purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_VALID); | |
1574 } | 1589 } |
1575 | 1590 |
1576 static void | 1591 static void |
1577 x509_tls_cached_start_verify(PurpleCertificateVerificationRequest *vrq) | 1592 x509_tls_cached_start_verify(PurpleCertificateVerificationRequest *vrq) |
1578 { | 1593 { |