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 {