| | 354 | import itertools, md5 |
| | 355 | |
| | 356 | # Private - shared between all OpenSSLCertificateOptions, counts up to provide |
| | 357 | # a unique session id for each context |
| | 358 | _sessionCounter = itertools.count().next |
| | 359 | |
| | 360 | class _SSLApplicationData(object): |
| | 361 | def __init__(self): |
| | 362 | self.problems = [] |
| | 363 | |
| | 364 | |
| | 365 | class OpenSSLVerifyError(Exception): |
| | 366 | |
| | 367 | _errorCodes = {0: ('X509_V_OK', |
| | 368 | 'ok', |
| | 369 | 'the operation was successful. >'), |
| | 370 | |
| | 371 | 2: ('X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT', |
| | 372 | 'unable to get issuer certificate', |
| | 373 | "The issuer certificate could not be found. This " |
| | 374 | "occurs if the issuer certificate of an untrusted " |
| | 375 | "certificate cannot be found."), |
| | 376 | |
| | 377 | 3: ('X509_V_ERR_UNABLE_TO_GET_CRL', |
| | 378 | 'unable to get certificate CRL', |
| | 379 | "The CRL of a certificate could not be found. " |
| | 380 | "Unused."), |
| | 381 | |
| | 382 | 4: ('X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE', |
| | 383 | "unable to decrypt certificate's signature", |
| | 384 | "The certificate signature could not be decrypted. " |
| | 385 | "This means that the actual signature value could not " |
| | 386 | "be determined rather than it not matching the " |
| | 387 | "expected value, this is only meaningful for RSA " |
| | 388 | "keys."), |
| | 389 | |
| | 390 | 5: ('X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE', |
| | 391 | "unable to decrypt CRL's signature", |
| | 392 | "The CRL signature could not be decrypted. This " |
| | 393 | "means that the actual signature value could not be " |
| | 394 | "determined rather than it not matching the expected " |
| | 395 | "value. Unused."), |
| | 396 | |
| | 397 | 6: ('X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY', |
| | 398 | 'unable to decode issuer', |
| | 399 | "Public key the public key in the certificate " |
| | 400 | "SubjectPublicKeyInfo could not be read."), |
| | 401 | |
| | 402 | 7: ('X509_V_ERR_CERT_SIGNATURE_FAILURE', |
| | 403 | 'certificate signature failure', |
| | 404 | 'The signature of the certificate is invalid.'), |
| | 405 | |
| | 406 | 8: ('X509_V_ERR_CRL_SIGNATURE_FAILURE', |
| | 407 | 'CRL signature failure', |
| | 408 | 'The signature of the certificate is invalid. Unused.'), |
| | 409 | |
| | 410 | 9: ('X509_V_ERR_CERT_NOT_YET_VALID', |
| | 411 | 'certificate is not yet valid', |
| | 412 | "The certificate is not yet valid. The notBefore " |
| | 413 | "date is after the current time."), |
| | 414 | |
| | 415 | 10: ('X509_V_ERR_CERT_HAS_EXPIRED', |
| | 416 | 'certificate has expired', |
| | 417 | "The certificate has expired. The notAfter date " |
| | 418 | "is before the current time."), |
| | 419 | |
| | 420 | 11: ('X509_V_ERR_CRL_NOT_YET_VALID', |
| | 421 | 'CRL is not yet valid', |
| | 422 | 'The CRL is not yet valid. Unused.'), |
| | 423 | |
| | 424 | 12: ('X509_V_ERR_CRL_HAS_EXPIRED', |
| | 425 | 'CRL has expired', |
| | 426 | 'The CRL has expired. Unused.'), |
| | 427 | |
| | 428 | 13: ('X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD', |
| | 429 | "format error in certificate's notBefore field", |
| | 430 | "The certificate's notBefore field contains an " |
| | 431 | "invalid time."), |
| | 432 | |
| | 433 | 14: ('X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD', |
| | 434 | "format error in certificate's notAfter field.", |
| | 435 | "The certificate's notAfter field contains an " |
| | 436 | "invalid time."), |
| | 437 | |
| | 438 | 15: ('X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD', |
| | 439 | "format error in CRL's lastUpdate field", |
| | 440 | "The CRL lastUpdate field contains an invalid " |
| | 441 | "time. Unused."), |
| | 442 | |
| | 443 | 16: ('X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD', |
| | 444 | "format error in CRL's nextUpdate field", |
| | 445 | "The CRL nextUpdate field contains an invalid " |
| | 446 | "time. Unused."), |
| | 447 | |
| | 448 | 17: ('X509_V_ERR_OUT_OF_MEM', |
| | 449 | 'out of memory', |
| | 450 | 'An error occurred trying to allocate memory. ' |
| | 451 | 'This should never happen.'), |
| | 452 | |
| | 453 | 18: ('X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT', |
| | 454 | 'self signed certificate', |
| | 455 | 'The passed certificate is self signed and the same ' |
| | 456 | 'certificate cannot be found in the list of trusted ' |
| | 457 | 'certificates.'), |
| | 458 | |
| | 459 | 19: ('X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN', |
| | 460 | 'self signed certificate in certificate chain', |
| | 461 | 'The certificate chain could be built up using the ' |
| | 462 | 'untrusted certificates but the root could not be ' |
| | 463 | 'found locally.'), |
| | 464 | |
| | 465 | 20: ('X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY', |
| | 466 | 'unable to get local issuer certificate', |
| | 467 | 'The issuer certificate of a locally looked up ' |
| | 468 | 'certificate could not be found. This normally ' |
| | 469 | 'means the list of trusted certificates is not ' |
| | 470 | 'complete.'), |
| | 471 | |
| | 472 | 21: ('X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE', |
| | 473 | 'unable to verify the first certificate', |
| | 474 | 'No signatures could be verified because the chain ' |
| | 475 | 'contains only one certificate and it is not self ' |
| | 476 | 'signed.'), |
| | 477 | |
| | 478 | 22: ('X509_V_ERR_CERT_CHAIN_TOO_LONG', |
| | 479 | 'certificate chain too long', |
| | 480 | 'The certificate chain length is greater than the ' |
| | 481 | 'supplied maximum depth. Unused.'), |
| | 482 | |
| | 483 | 23: ('X509_V_ERR_CERT_REVOKED', |
| | 484 | 'certificate revoked', |
| | 485 | 'The certificate has been revoked. Unused.'), |
| | 486 | |
| | 487 | 24: ('X509_V_ERR_INVALID_CA', |
| | 488 | 'invalid CA certificate', |
| | 489 | 'A CA certificate is invalid. Either it is not a CA ' |
| | 490 | 'or its extensions are not consistent with the ' |
| | 491 | 'supplied purpose.'), |
| | 492 | |
| | 493 | 25: ('X509_V_ERR_PATH_LENGTH_EXCEEDED', |
| | 494 | 'path length constraint exceeded', |
| | 495 | 'The basicConstraints pathlength parameter has been ' |
| | 496 | 'exceeded.'), |
| | 497 | |
| | 498 | 26: ('X509_V_ERR_INVALID_PURPOSE', |
| | 499 | 'unsupported certificate purpose', |
| | 500 | 'The supplied certificate cannot be used for the ' |
| | 501 | 'specified purpose.'), |
| | 502 | |
| | 503 | 27: ('X509_V_ERR_CERT_UNTRUSTED', |
| | 504 | 'certificate not trusted', |
| | 505 | 'The root CA is not marked as trusted for the ' |
| | 506 | 'specified purpose.'), |
| | 507 | |
| | 508 | 28: ('X509_V_ERR_CERT_REJECTED', |
| | 509 | 'certificate rejected', |
| | 510 | 'The root CA is marked to reject the specified ' |
| | 511 | 'purpose.'), |
| | 512 | |
| | 513 | 29: ('X509_V_ERR_SUBJECT_ISSUER_MISMATCH', |
| | 514 | 'subject issuer mismatch', |
| | 515 | 'The current candidate issuer certificate was ' |
| | 516 | 'rejected because its subject name did not match ' |
| | 517 | 'the issuer name of the current certificate. Only ' |
| | 518 | 'displayed when the issuer_checks option is set.'), |
| | 519 | |
| | 520 | 30: ('X509_V_ERR_AKID_SKID_MISMATCH', |
| | 521 | 'authority and subject key identifier mismatch', |
| | 522 | 'The current candidate issuer certificate was ' |
| | 523 | 'rejected because its subject key identifier was ' |
| | 524 | 'present and did not match the authority key ' |
| | 525 | 'identifier current certificate. Only displayed ' |
| | 526 | 'when the issuer_checks option is set.'), |
| | 527 | |
| | 528 | 31: ('X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH', |
| | 529 | 'authority and issuer serial number mismatch', |
| | 530 | 'The current candidate issuer certificate was ' |
| | 531 | 'rejected because its issuer name and serial ' |
| | 532 | 'number was present and did not match the ' |
| | 533 | 'authority key identifier of the current ' |
| | 534 | 'certificate. Only displayed when the issuer_checks ' |
| | 535 | 'option is set.'), |
| | 536 | |
| | 537 | 32: ('X509_V_ERR_KEYUSAGE_NO_CERTSIGN', |
| | 538 | 'key usage does not include certificate', |
| | 539 | 'Signing the current candidate issuer certificate was ' |
| | 540 | 'rejected because its keyUsage extension does not ' |
| | 541 | 'permit certificate signing.'), |
| | 542 | |
| | 543 | 50: ('X509_V_ERR_APPLICATION_VERIFICATION', |
| | 544 | 'application verification failure', |
| | 545 | 'an application specific error. Unused.')} |
| | 546 | |
| | 547 | |
| | 548 | def __init__(self, cert, errno, depth): |
| | 549 | Exception.__init__(self, cert, errno, depth) |
| | 550 | self.cert = cert |
| | 551 | self.errno = errno |
| | 552 | self.depth = depth |
| | 553 | |
| | 554 | def __repr__(self): |
| | 555 | x = self._errorCodes.get(self.errno) |
| | 556 | if x is not None: |
| | 557 | name, short, long = x |
| | 558 | return 'Peer Certificate Verification Failed: %s (error code: %d)' \ |
| | 559 | % ( long, self.errno ) |
| | 560 | return "Peer Certificate Verification Failed for Unknown Reason" |
| | 561 | |
| | 562 | __str__ = __repr__ |
| | 563 | |
| | 564 | |
| | 565 | class OpenSSLCertificateOptions(object): |
| | 566 | """ |
| | 567 | A factory for SSL context objects for both SSL servers and clients. |
| | 568 | """ |
| | 569 | |
| | 570 | _context = None |
| | 571 | # Older versions of PyOpenSSL didn't provide OP_ALL. |
| | 572 | # Fudge it here, just in case. |
| | 573 | _OP_ALL = getattr(SSL, 'OP_ALL', 0x0000FFFF) |
| | 574 | |
| | 575 | method = SSL.SSLv23_METHOD |
| | 576 | |
| | 577 | def __init__(self, |
| | 578 | privateKey=None, |
| | 579 | privateKeyPasswd=None, |
| | 580 | certificate=None, |
| | 581 | method=None, |
| | 582 | verify=False, |
| | 583 | caCerts=None, |
| | 584 | verifyDepth=9, |
| | 585 | requireCertificate=True, |
| | 586 | verifyOnce=True, |
| | 587 | enableSingleUseKeys=True, |
| | 588 | enableSessions=True, |
| | 589 | fixBrokenPeers=False): |
| | 590 | """ |
| | 591 | Create an OpenSSL context SSL connection context factory. |
| | 592 | |
| | 593 | ``ssl_privateKey`` |
| | 594 | |
| | 595 | private key file path or PKey object. |
| | 596 | |
| | 597 | ``ssl_privateKeyPasswd`` |
| | 598 | |
| | 599 | A string holding the ssl_privateKey's password. |
| | 600 | |
| | 601 | ``ssl_certificate`` |
| | 602 | |
| | 603 | This an optional SSL certificate file path (via OpenSSL) or |
| | 604 | X509 object. You can supply ``*`` and a development-only |
| | 605 | certificate will be created for you, or you can generate a |
| | 606 | self-signed test PEM certificate file as follows:: |
| | 607 | |
| | 608 | $ openssl genrsa 1024 > host.key |
| | 609 | $ chmod 400 host.key |
| | 610 | $ openssl req -new -x509 -nodes -sha1 -days 365 \\ |
| | 611 | -key host.key > host.cert |
| | 612 | $ cat host.cert host.key > host.pem |
| | 613 | $ chmod 400 host.pem |
| | 614 | |
| | 615 | ``ssl_method`` |
| | 616 | |
| | 617 | The SSL protocol to use, one of SSLv23_METHOD, SSLv2_METHOD, |
| | 618 | SSLv3_METHOD, TLSv1_METHOD. Defaults to SSLv23_METHOD. |
| | 619 | |
| | 620 | ``ssl_verify`` |
| | 621 | |
| | 622 | If True, verify certificates received from the peer and fail the |
| | 623 | handshake if verification fails. Otherwise, allow anonymous |
| | 624 | sessions and sessions with certificates which fail validation. |
| | 625 | By default this is False. |
| | 626 | |
| | 627 | ``ssl_caCerts`` |
| | 628 | |
| | 629 | List of certificate authority certificates to send to the client |
| | 630 | when requesting a certificate. Only used if verify is True. |
| | 631 | Since verify is False by default, this is None by default. |
| | 632 | |
| | 633 | ``ssl_verifyDepth`` |
| | 634 | |
| | 635 | Depth in certificate chain down to which to verify. |
| | 636 | If unspecified, use the underlying default (9). |
| | 637 | |
| | 638 | ``ssl_requireCertificate`` |
| | 639 | |
| | 640 | If True, do not allow anonymous sessions. |
| | 641 | |
| | 642 | ``ssl_verifyOnce`` |
| | 643 | |
| | 644 | If True, do not re-verify the certificate on session resumption. |
| | 645 | |
| | 646 | ``ssl_enableSingleUseKeys`` |
| | 647 | |
| | 648 | If True, generate a new key whenever ephemeral DH parameters are |
| | 649 | used to prevent small subgroup attacks. |
| | 650 | |
| | 651 | ``ssl_enableSessions`` |
| | 652 | |
| | 653 | If True, set a session ID on each context. This allows a |
| | 654 | shortened handshake to be used when a known client reconnects. |
| | 655 | |
| | 656 | ``fixBrokenPeers`` |
| | 657 | |
| | 658 | If True, enable various non-spec protocol fixes for broken SSL |
| | 659 | implementations. This should be entirely safe, according to the |
| | 660 | OpenSSL documentation, but YMMV. This option is now off by |
| | 661 | default, because it causes problems with connections between |
| | 662 | peers using OpenSSL 0.9.8a. |
| | 663 | """ |
| | 664 | |
| | 665 | assert (privateKey is None) == (certificate is None), \ |
| | 666 | "Specify neither or both of privateKey and certificate" |
| | 667 | self.privateKey = privateKey |
| | 668 | self.privateKeyPasswd = privateKeyPasswd |
| | 669 | self.certificate = certificate |
| | 670 | if method is not None: |
| | 671 | if method.lower().startswith('sslv23'): |
| | 672 | self.method = SSL.SSLv23_METHOD |
| | 673 | if method.lower().startswith('sslv2'): |
| | 674 | self.method = SSL.SSLv2_METHOD |
| | 675 | elif method.lower().startswith('sslv3'): |
| | 676 | self.method = SSL.SSLv3_METHOD |
| | 677 | elif method.lower().startswith('tlsv1'): |
| | 678 | self.method = SSL.TLSv1_METHOD |
| | 679 | else: |
| | 680 | self.method = SSL.SSLv23_METHOD |
| | 681 | |
| | 682 | self.verify = verify |
| | 683 | assert ((verify and caCerts) or |
| | 684 | (not verify)), "Specify client CA certificate information" \ |
| | 685 | " if and only if enabling certificate " \ |
| | 686 | "verification" |
| | 687 | |
| | 688 | if caCerts: |
| | 689 | certs = [] |
| | 690 | for caCert in caCerts: |
| | 691 | if isinstance(caCert, basestring): |
| | 692 | certs.append( |
| | 693 | crypto.load_certificate(crypto.FILETYPE_PEM, |
| | 694 | open(caCert).read()) |
| | 695 | ) |
| | 696 | self.caCerts = certs |
| | 697 | else: |
| | 698 | self.caCerts = caCerts |
| | 699 | self.verifyDepth = verifyDepth |
| | 700 | self.requireCertificate = requireCertificate |
| | 701 | self.verifyOnce = verifyOnce |
| | 702 | self.enableSingleUseKeys = enableSingleUseKeys |
| | 703 | self.enableSessions = enableSessions |
| | 704 | self.fixBrokenPeers = fixBrokenPeers |
| | 705 | |
| | 706 | |
| | 707 | def __getstate__(self): |
| | 708 | d = self.__dict__.copy() |
| | 709 | try: |
| | 710 | del d['_context'] |
| | 711 | except KeyError: |
| | 712 | pass |
| | 713 | return d |
| | 714 | |
| | 715 | |
| | 716 | def __setstate__(self, state): |
| | 717 | self.__dict__ = state |
| | 718 | |
| | 719 | |
| | 720 | def getContext(self): |
| | 721 | """Return a SSL.Context object. |
| | 722 | """ |
| | 723 | if self._context is None: |
| | 724 | self._context = self._makeContext() |
| | 725 | return self._context |
| | 726 | |
| | 727 | |
| | 728 | def _makeContext(self): |
| | 729 | ctx = SSL.Context(self.method) |
| | 730 | ctx.set_app_data(_SSLApplicationData()) |
| | 731 | |
| | 732 | if self.privateKeyPasswd: |
| | 733 | ctx.set_passwd_cb(lambda a, b, c: self.privateKeyPasswd) |
| | 734 | |
| | 735 | if self.certificate is not None and self.privateKey is not None: |
| | 736 | try: |
| | 737 | ctx.use_certificate(self.certificate) |
| | 738 | except: |
| | 739 | # Passed a filepath? |
| | 740 | ctx.use_certificate_file(self.certificate) |
| | 741 | try: |
| | 742 | ctx.use_privatekey(self.privateKey) |
| | 743 | except: |
| | 744 | # Passed a filepath? |
| | 745 | ctx.use_privatekey_file(self.privateKey) |
| | 746 | # Sanity check |
| | 747 | ctx.check_privatekey() |
| | 748 | |
| | 749 | verifyFlags = SSL.VERIFY_NONE |
| | 750 | if self.verify: |
| | 751 | verifyFlags = SSL.VERIFY_PEER |
| | 752 | if self.requireCertificate: |
| | 753 | verifyFlags |= SSL.VERIFY_FAIL_IF_NO_PEER_CERT |
| | 754 | if self.verifyOnce: |
| | 755 | verifyFlags |= SSL.VERIFY_CLIENT_ONCE |
| | 756 | if self.caCerts: |
| | 757 | store = ctx.get_cert_store() |
| | 758 | for cert in self.caCerts: |
| | 759 | store.add_cert(cert) |
| | 760 | self.verifyFlags = verifyFlags |
| | 761 | |
| | 762 | def _trackVerificationProblems(conn,cert,errno,depth,preverify_ok): |
| | 763 | # retcode is the answer OpenSSL's default verifier would have |
| | 764 | # given, had we allowed it to run. |
| | 765 | if not preverify_ok: |
| | 766 | error = OpenSSLVerifyError(cert, errno, depth) |
| | 767 | ctx.get_app_data().problems.append(error) |
| | 768 | return preverify_ok |
| | 769 | ctx.set_verify(verifyFlags, _trackVerificationProblems) |
| | 770 | |
| | 771 | if self.verifyDepth is not None: |
| | 772 | ctx.set_verify_depth(self.verifyDepth) |
| | 773 | |
| | 774 | if self.enableSingleUseKeys: |
| | 775 | ctx.set_options(SSL.OP_SINGLE_DH_USE) |
| | 776 | |
| | 777 | if self.fixBrokenPeers: |
| | 778 | ctx.set_options(self._OP_ALL) |
| | 779 | |
| | 780 | if self.enableSessions: |
| | 781 | sessionName = md5.md5("%s-%d" % ( |
| | 782 | self.__class__.__module__ + '.' + self.__class__.__name__, |
| | 783 | _sessionCounter())).hexdigest() |
| | 784 | ctx.set_session_id(sessionName) |
| | 785 | |
| | 786 | return ctx |