src/modules/client/publisher.py
changeset 3321 52e8eec3014c
parent 3318 864be9e4db61
child 3325 18a3d7b0d618
equal deleted inserted replaced
3320:f727edff50bd 3321:52e8eec3014c
    47 import six
    47 import six
    48 import tempfile
    48 import tempfile
    49 import time
    49 import time
    50 import uuid
    50 import uuid
    51 
    51 
       
    52 from cryptography import x509
       
    53 from cryptography.hazmat.backends import default_backend
       
    54 from cryptography.hazmat.primitives import serialization
       
    55 from cryptography.hazmat.primitives.asymmetric import padding
    52 from six.moves.urllib.parse import quote, urlsplit, urlparse, urlunparse, \
    56 from six.moves.urllib.parse import quote, urlsplit, urlparse, urlunparse, \
    53     ParseResult
    57     ParseResult
    54 from six.moves.urllib.request import url2pathname
    58 from six.moves.urllib.request import url2pathname
    55 
    59 
    56 import pkg.catalog
    60 import pkg.catalog
    59 import pkg.client.pkgdefs as pkgdefs
    63 import pkg.client.pkgdefs as pkgdefs
    60 import pkg.digest as digest
    64 import pkg.digest as digest
    61 import pkg.misc as misc
    65 import pkg.misc as misc
    62 import pkg.portable as portable
    66 import pkg.portable as portable
    63 import pkg.server.catalog as old_catalog
    67 import pkg.server.catalog as old_catalog
    64 import M2Crypto as m2
       
    65 
    68 
    66 from pkg.client import global_settings
    69 from pkg.client import global_settings
    67 from pkg.client.debugvalues import DebugValues
    70 from pkg.client.debugvalues import DebugValues
    68 logger = global_settings.logger
    71 logger = global_settings.logger
    69 from pkg.misc import EmptyDict, EmptyI, SIGNATURE_POLICY, DictProperty, \
    72 from pkg.misc import EmptyDict, EmptyI, SIGNATURE_POLICY, DictProperty, \
    99 # Sort policy mapping.
   102 # Sort policy mapping.
   100 URI_SORT_POLICIES = {
   103 URI_SORT_POLICIES = {
   101     URI_SORT_PRIORITY: lambda obj: (obj.priority, obj.uri),
   104     URI_SORT_PRIORITY: lambda obj: (obj.priority, obj.uri),
   102 }
   105 }
   103 
   106 
   104 # This dictionary records the recognized values of extensions.
   107 # The strings in the value field refer to the boolean properties of the
       
   108 # Cryptography extension classes. If a property has a value True set, it means
       
   109 # this property is added as an extension value in the certificate generation,
       
   110 # and vice versa.
       
   111 EXTENSIONS_VALUES = {
       
   112     x509.BasicConstraints: ["ca", "path_length"],
       
   113     x509.KeyUsage: ["digital_signature", "content_commitment",
       
   114     "key_encipherment", "data_encipherment", "key_agreement", "key_cert_sign",
       
   115     "crl_sign", "encipher_only", "decipher_only"]
       
   116 }
       
   117 
       
   118 # Only listed extension values (properties) here can have a value True set in a
       
   119 # certificate extension; any other properties with a value True set will be
       
   120 # treated as unsupported.
   105 SUPPORTED_EXTENSION_VALUES = {
   121 SUPPORTED_EXTENSION_VALUES = {
   106     "basicConstraints": ("CA:TRUE", "CA:FALSE", "PATHLEN:"),
   122     x509.BasicConstraints: ("ca", "path_length"),
   107     "keyUsage": ("DIGITAL SIGNATURE", "CERTIFICATE SIGN", "CRL SIGN")
   123     x509.KeyUsage: ("digital_signature", "key_cert_sign", "crl_sign")
   108 }
   124 }
   109 
   125 
   110 # These dictionaries map uses into their extensions.
   126 # These dictionaries map uses into their extensions.
   111 CODE_SIGNING_USE = {
   127 CODE_SIGNING_USE = {
   112     "keyUsage": ["DIGITAL SIGNATURE"]
   128     x509.KeyUsage: ["digital_signature"],
   113 }
   129 }
   114 
   130 
   115 CERT_SIGNING_USE = {
   131 CERT_SIGNING_USE = {
   116     "basicConstraints": ["CA:TRUE"],
   132     x509.BasicConstraints: ["ca"],
   117     "keyUsage": ["CERTIFICATE SIGN"]
   133     x509.KeyUsage: ["key_cert_sign"],
   118 }
   134 }
   119 
   135 
   120 CRL_SIGNING_USE = {
   136 CRL_SIGNING_USE = {
   121     "keyUsage": ["CRL SIGN"]
   137     x509.KeyUsage: ["crl_sign"],
   122 }
   138 }
   123 
   139 
   124 POSSIBLE_USES = [CODE_SIGNING_USE, CERT_SIGNING_USE, CRL_SIGNING_USE]
   140 POSSIBLE_USES = [CODE_SIGNING_USE, CERT_SIGNING_USE, CRL_SIGNING_USE]
   125 
   141 
   126 # A special token used in place of the system repository URL which is
   142 # A special token used in place of the system repository URL which is
  2442 
  2458 
  2443         @staticmethod
  2459         @staticmethod
  2444         def __hash_cert(c):
  2460         def __hash_cert(c):
  2445                 # In order to interoperate with older images, we must use SHA-1
  2461                 # In order to interoperate with older images, we must use SHA-1
  2446                 # here.
  2462                 # here.
  2447                 return hashlib.sha1(c.as_pem()).hexdigest()
  2463                 return hashlib.sha1(
       
  2464                     c.public_bytes(serialization.Encoding.PEM)).hexdigest()
  2448 
  2465 
  2449         @staticmethod
  2466         @staticmethod
  2450         def __string_to_cert(s, pkg_hash=None):
  2467         def __string_to_cert(s, pkg_hash=None):
  2451                 """Convert a string to a X509 cert."""
  2468                 """Convert a string to a X509 cert."""
  2452 
  2469 
  2453                 try:
  2470                 try:
  2454                         return m2.X509.load_cert_string(s)
  2471                         return x509.load_pem_x509_certificate(
  2455                 except m2.X509.X509Error as e:
  2472                             misc.force_bytes(s), default_backend())
       
  2473                 except ValueError:
  2456                         if pkg_hash is not None:
  2474                         if pkg_hash is not None:
  2457                                 raise api_errors.BadFileFormat(_("The file "
  2475                                 raise api_errors.BadFileFormat(_("The file "
  2458                                     "with hash {0} was expected to be a PEM "
  2476                                     "with hash {0} was expected to be a PEM "
  2459                                     "certificate but it could not be "
  2477                                     "certificate but it could not be "
  2460                                     "read.").format(pkg_hash))
  2478                                     "read.").format(pkg_hash))
  2471                         pkg_hash = self.__hash_cert(cert)
  2489                         pkg_hash = self.__hash_cert(cert)
  2472                 pkg_hash_pth = os.path.join(self.cert_root, pkg_hash)
  2490                 pkg_hash_pth = os.path.join(self.cert_root, pkg_hash)
  2473                 file_problem = False
  2491                 file_problem = False
  2474                 try:
  2492                 try:
  2475                         with open(pkg_hash_pth, "wb") as fh:
  2493                         with open(pkg_hash_pth, "wb") as fh:
  2476                                 fh.write(cert.as_pem())
  2494                                 fh.write(cert.public_bytes(
       
  2495                                     serialization.Encoding.PEM))
  2477                 except EnvironmentError as e:
  2496                 except EnvironmentError as e:
       
  2497                         if e.errno == errno.EACCES:
       
  2498                                 raise api_errors.PermissionsException(
       
  2499                                     e.filename)
  2478                         file_problem = True
  2500                         file_problem = True
  2479 
  2501 
  2480                 # Note that while we store certs by their subject hashes,
  2502                 # Note that while we store certs by their subject hashes,
  2481                 # M2Crypto's subject hashes differ from what openssl reports
  2503                 # we use our own hashing since cryptography has no interface
  2482                 # the subject hash to be.
  2504                 # for the subject hash and other crypto frameworks have been
  2483                 subj_hsh = cert.get_subject().as_hash()
  2505                 # inconsistent with OpenSSL.
       
  2506                 subj_hsh = hashlib.sha1(misc.force_bytes(
       
  2507                     cert.subject)).hexdigest()
  2484                 c = 0
  2508                 c = 0
  2485                 made_link = False
  2509                 made_link = False
  2486                 while not made_link:
  2510                 while not made_link:
  2487                         fn = os.path.join(self.__subj_root,
  2511                         fn = os.path.join(self.__subj_root,
  2488                             "{0}.{1}".format(subj_hsh, c))
  2512                             "{0}.{1}".format(subj_hsh, c))
  2542                         if h != pkg_hash:
  2566                         if h != pkg_hash:
  2543                                 raise api_errors.ModifiedCertificateException(c,
  2567                                 raise api_errors.ModifiedCertificateException(c,
  2544                                     pth)
  2568                                     pth)
  2545                 return c
  2569                 return c
  2546 
  2570 
       
  2571         def __rebuild_subj_root(self):
       
  2572                 """Rebuild subject hash metadata."""
       
  2573 
       
  2574                 # clean up the old subject hash files to prevent
       
  2575                 # junk files residing in the directory
       
  2576                 try:
       
  2577                         shutil.rmtree(self.__subj_root)
       
  2578                 except EnvironmentError:
       
  2579                         # if unprivileged user, we can't add
       
  2580                         # certs to it
       
  2581                         pass
       
  2582                 else:
       
  2583                         for p in os.listdir(self.cert_root):
       
  2584                                 path = os.path.join(self.cert_root, p)
       
  2585                                 if not os.path.isfile(path):
       
  2586                                         continue
       
  2587                                 with open(path, "rb") as fh:
       
  2588                                         s = fh.read()
       
  2589                                 cert = self.__string_to_cert(s)
       
  2590                                 self.__add_cert(cert)
       
  2591 
  2547         def __get_certs_by_name(self, name):
  2592         def __get_certs_by_name(self, name):
  2548                 """Given 'name', a M2Crypto X509_Name, return the certs with
  2593                 """Given 'name', a Cryptograhy 'Name' object, return the certs
  2549                 that name as a subject."""
  2594                 with that name as a subject."""
  2550 
  2595 
  2551                 res = []
  2596                 res = []
  2552                 c = 0
  2597                 count = 0
  2553                 name_hsh = name.as_hash()
  2598                 name_hsh = hashlib.sha1(misc.force_bytes(name)).hexdigest()
       
  2599 
       
  2600                 def load_cert(pth):
       
  2601                         with open(pth, "rb") as f:
       
  2602                                 return x509.load_pem_x509_certificate(
       
  2603                                     f.read(), default_backend())
       
  2604 
  2554                 try:
  2605                 try:
  2555                         while True:
  2606                         while True:
  2556                                 pth = os.path.join(self.__subj_root,
  2607                                 pth = os.path.join(self.__subj_root,
  2557                                     "{0}.{1}".format(name_hsh, c))
  2608                                     "{0}.{1}".format(name_hsh, count))
  2558                                 cert = m2.X509.load_cert(pth)
  2609                                 res.append(load_cert(pth))
  2559                                 res.append(cert)
  2610                                 count += 1
  2560                                 c += 1
       
  2561                 except EnvironmentError as e:
  2611                 except EnvironmentError as e:
       
  2612                         # When switching to a different hash algorithm, the hash
       
  2613                         # name of file changes so that we couldn't find the
       
  2614                         # file. We try harder to rebuild the subject's metadata
       
  2615                         # if it's the first time we fail (count == 0).
       
  2616                         if count == 0 and e.errno == errno.ENOENT:
       
  2617                                 self.__rebuild_subj_root()
       
  2618                                 try:
       
  2619                                         res.append(load_cert(pth))
       
  2620                                 except EnvironmentError as e:
       
  2621                                         if e.errno != errno.ENOENT:
       
  2622                                                 raise
       
  2623 
  2562                         t = api_errors._convert_error(e,
  2624                         t = api_errors._convert_error(e,
  2563                             [errno.ENOENT])
  2625                             [errno.ENOENT])
  2564                         if t:
  2626                         if t:
  2565                                 raise t
  2627                                 raise t
  2566                 res.extend(self.__issuers.get(name_hsh, []))
  2628                 res.extend(self.__issuers.get(name_hsh, []))
  2576                 # CA certs approved for this publisher are stored by hash to
  2638                 # CA certs approved for this publisher are stored by hash to
  2577                 # prevent the later substitution or confusion over what certs
  2639                 # prevent the later substitution or confusion over what certs
  2578                 # have or have not been approved.
  2640                 # have or have not been approved.
  2579                 for h in set(self.approved_ca_certs):
  2641                 for h in set(self.approved_ca_certs):
  2580                         c = self.get_cert_by_hash(h, verify_hash=True)
  2642                         c = self.get_cert_by_hash(h, verify_hash=True)
  2581                         s = c.get_subject().as_hash()
  2643                         s = hashlib.sha1(misc.force_bytes(
       
  2644                             c.subject)).hexdigest()
  2582                         self.ca_dict.setdefault(s, [])
  2645                         self.ca_dict.setdefault(s, [])
  2583                         self.ca_dict[s].append(c)
  2646                         self.ca_dict[s].append(c)
  2584                 return self.ca_dict
  2647                 return self.ca_dict
  2585 
  2648 
  2586         def update_props(self, set_props=EmptyI, add_prop_values=EmptyDict,
  2649         def update_props(self, set_props=EmptyI, add_prop_values=EmptyDict,
  2650 
  2713 
  2651         def __format_safe_read_crl(self, pth):
  2714         def __format_safe_read_crl(self, pth):
  2652                 """CRLs seem to frequently come in DER format, so try reading
  2715                 """CRLs seem to frequently come in DER format, so try reading
  2653                 the CRL using both of the formats before giving up."""
  2716                 the CRL using both of the formats before giving up."""
  2654 
  2717 
       
  2718                 with open(pth, "rb") as f:
       
  2719                         raw = f.read()
       
  2720 
  2655                 try:
  2721                 try:
  2656                         return m2.X509.load_crl(pth)
  2722                         return x509.load_pem_x509_crl(raw, default_backend())
  2657                 except m2.X509.X509Error:
  2723                 except ValueError:
  2658                         try:
  2724                         try:
  2659                                 return m2.X509.load_crl(pth,
  2725                                 return x509.load_der_x509_crl(raw,
  2660                                     format=m2.X509.FORMAT_DER)
  2726                                     default_backend())
  2661                         except m2.X509.X509Error:
  2727                         except ValueError:
  2662                                 raise api_errors.BadFileFormat(_("The CRL file "
  2728                                 raise api_errors.BadFileFormat(_("The CRL file "
  2663                                     "{0} is not in a recognized "
  2729                                     "{0} is not in a recognized "
  2664                                     "format.").format(pth))
  2730                                     "format.").format(pth))
  2665 
  2731 
  2666         def __get_crl(self, uri):
  2732         def __get_crl(self, uri):
  2701                         try:
  2767                         try:
  2702                                 crl = self.__format_safe_read_crl(fpath)
  2768                                 crl = self.__format_safe_read_crl(fpath)
  2703                         except EnvironmentError:
  2769                         except EnvironmentError:
  2704                                 pass
  2770                                 pass
  2705                         else:
  2771                         else:
  2706                                 nu = crl.get_next_update().get_datetime()
  2772                                 nu = crl.next_update
  2707                                 # get_datetime is supposed to return a UTC time,
  2773                                 cur_time = dt.datetime.utcnow()
  2708                                 # so assert that's the case.
  2774 
  2709                                 assert nu.tzinfo.utcoffset(nu) == \
       
  2710                                     dt.timedelta(0)
       
  2711                                 # Add timezone info to cur_time so that cur_time
       
  2712                                 # and nu can be compared.
       
  2713                                 cur_time = dt.datetime.now(nu.tzinfo)
       
  2714                                 if cur_time < nu:
  2775                                 if cur_time < nu:
  2715                                         self.__tmp_crls[uri] = crl
  2776                                         self.__tmp_crls[uri] = crl
  2716                                         return crl
  2777                                         return crl
  2717                 # If the CRL is already known to be unavailable, don't try
  2778                 # If the CRL is already known to be unavailable, don't try
  2718                 # connecting to it again.
  2779                 # connecting to it again.
  2763                                 portable.remove(tmp_pth)
  2824                                 portable.remove(tmp_pth)
  2764                         except EnvironmentError:
  2825                         except EnvironmentError:
  2765                                 pass
  2826                                 pass
  2766                 return ncrl
  2827                 return ncrl
  2767 
  2828 
  2768         def __check_crls(self, cert, ca_dict):
  2829 
  2769                 """Determines whether the certificate has been revoked by its
  2830         def __verify_x509_signature(self, c, key):
  2770                 CRL.
  2831                 """Verify the signature of a certificate or CRL 'c' against a
       
  2832                 provided public key 'key'."""
       
  2833 
       
  2834                 verifier = key.verifier(
       
  2835                     c.signature, padding.PKCS1v15(),
       
  2836                     c.signature_hash_algorithm)
       
  2837 
       
  2838                 if isinstance(c, x509.Certificate):
       
  2839                         data = c.tbs_certificate_bytes
       
  2840                 elif isinstance(c, x509.CertificateRevocationList):
       
  2841                         data = c.tbs_certlist_bytes
       
  2842                 else:
       
  2843                         raise AssertionError("Invalid x509 object for "
       
  2844                             "signature verification: {0}".format(type(c)))
       
  2845 
       
  2846                 verifier.update(data)
       
  2847                 try:
       
  2848                         verifier.verify()
       
  2849                         return True
       
  2850                 except Exception:
       
  2851                         return False
       
  2852 
       
  2853         def __check_crl(self, cert, ca_dict, crl_uri):
       
  2854                 """Determines whether the certificate has been revoked by the
       
  2855                 CRL located at 'crl_uri'.
  2771 
  2856 
  2772                 The 'cert' parameter is the certificate to check for revocation.
  2857                 The 'cert' parameter is the certificate to check for revocation.
  2773 
  2858 
  2774                 The 'ca_dict' is a dictionary which maps subject hashes to
  2859                 The 'ca_dict' is a dictionary which maps subject hashes to
  2775                 certs treated as trust anchors."""
  2860                 certs treated as trust anchors."""
  2776 
  2861 
  2777                 # If the certificate doesn't have a CRL location listed, treat
  2862                 crl = self.__get_crl(crl_uri)
  2778                 # it as valid.
  2863 
  2779                 try:
       
  2780                         ext = cert.get_ext("crlDistributionPoints")
       
  2781                 except LookupError as e:
       
  2782                         return True
       
  2783                 uri = ext.get_value()
       
  2784                 crl = self.__get_crl(uri)
       
  2785                 # If we couldn't retrieve a CRL from the distribution point
  2864                 # If we couldn't retrieve a CRL from the distribution point
  2786                 # and no CRL is cached on disk, assume the cert has not been
  2865                 # and no CRL is cached on disk, assume the cert has not been
  2787                 # revoked.  It's possible that this should be an image or
  2866                 # revoked.  It's possible that this should be an image or
  2788                 # publisher setting in the future.
  2867                 # publisher setting in the future.
  2789                 if not crl:
  2868                 if not crl:
  2790                         return True
  2869                         return True
  2791 
  2870 
  2792                 # A CRL has been found, now it needs to be validated like
  2871                 # A CRL has been found, now it needs to be validated like
  2793                 # a certificate is.
  2872                 # a certificate is.
  2794                 verified_crl = False
  2873                 verified_crl = False
  2795                 crl_issuer = crl.get_issuer()
  2874                 crl_issuer = crl.issuer
  2796                 tas = ca_dict.get(crl_issuer.as_hash(), [])
  2875                 tas = ca_dict.get(hashlib.sha1(misc.force_bytes(
       
  2876                     crl_issuer)).hexdigest(), [])
  2797                 for t in tas:
  2877                 for t in tas:
  2798                         try:
  2878                         try:
  2799                                 if crl.verify(t.get_pubkey()):
  2879                                 if self.__verify_x509_signature(crl,
       
  2880                                     t.public_key()):
  2800                                         # If t isn't approved for signing crls,
  2881                                         # If t isn't approved for signing crls,
  2801                                         # the exception __check_extensions
  2882                                         # the exception __check_extensions
  2802                                         # raises will take the code to the
  2883                                         # raises will take the code to the
  2803                                         # except below.
  2884                                         # except below.
  2804                                         self.__check_extensions(t,
  2885                                         self.__check_extensions(t,
  2807                         except api_errors.SigningException:
  2888                         except api_errors.SigningException:
  2808                                 pass
  2889                                 pass
  2809                 if not verified_crl:
  2890                 if not verified_crl:
  2810                         crl_cas = self.__get_certs_by_name(crl_issuer)
  2891                         crl_cas = self.__get_certs_by_name(crl_issuer)
  2811                         for c in crl_cas:
  2892                         for c in crl_cas:
  2812                                 if crl.verify(c.get_pubkey()):
  2893                                 if self.__verify_x509_signature(crl,
       
  2894                                     c.public_key()):
  2813                                         try:
  2895                                         try:
  2814                                                 self.verify_chain(c, ca_dict, 0,
  2896                                                 self.verify_chain(c, ca_dict, 0,
  2815                                                     True,
  2897                                                     True,
  2816                                                     usages=CRL_SIGNING_USE)
  2898                                                     usages=CRL_SIGNING_USE)
  2817                                         except api_errors.SigningException:
  2899                                         except api_errors.SigningException:
  2819                                         else:
  2901                                         else:
  2820                                                 verified_crl = True
  2902                                                 verified_crl = True
  2821                                                 break
  2903                                                 break
  2822                 if not verified_crl:
  2904                 if not verified_crl:
  2823                         return True
  2905                         return True
       
  2906 
  2824                 # For a certificate to be revoked, its CRL must be validated
  2907                 # For a certificate to be revoked, its CRL must be validated
  2825                 # and revoked the certificate.
  2908                 # and revoked the certificate.
  2826                 rev = crl.is_revoked(cert)
  2909 
  2827                 if rev:
  2910                 assert crl.issuer == cert.issuer
  2828                         raise api_errors.RevokedCertificate(cert, rev[1])
  2911                 for rev in crl:
       
  2912                         if rev.serial_number != cert.serial:
       
  2913                                 continue
       
  2914                         try:
       
  2915                                 reason = rev.extensions.get_extension_for_oid(
       
  2916                                     x509.OID_CRL_REASON).value
       
  2917                         except x509.ExtensionNotFound:
       
  2918                                 reason = None
       
  2919                         raise api_errors.RevokedCertificate(cert, reason)
       
  2920 
       
  2921         def __check_crls(self, cert, ca_dict):
       
  2922                 """Determines whether the certificate has been revoked by one of
       
  2923                 its CRLs.
       
  2924 
       
  2925                 The 'cert' parameter is the certificate to check for revocation.
       
  2926 
       
  2927                 The 'ca_dict' is a dictionary which maps subject hashes to
       
  2928                 certs treated as trust anchors."""
       
  2929 
       
  2930                 # If the certificate doesn't have a CRL location listed, treat
       
  2931                 # it as valid.
       
  2932 
       
  2933                 # The CRLs to be retrieved are stored in the
       
  2934                 # CRLDistributionPoints extensions which is structured like
       
  2935                 # this:
       
  2936                 #
       
  2937                 # CRLDitsributionPoints = [
       
  2938                 #     CRLDistributionPoint = [
       
  2939                 #         union  {
       
  2940                 #             full_name     = [ GeneralName, ... ]
       
  2941                 #             relative_name = [ GeneralName, ... ]
       
  2942                 #         }, ... ]
       
  2943                 #     , ... ]
       
  2944                 # 
       
  2945                 # Relative names are a feature in X509 certs which allow to
       
  2946                 # specify a location relative to another certificate. We are not
       
  2947                 # supporting this and I'm not sure anybody is using this for
       
  2948                 # CRLs.
       
  2949                 # Full names are absolute locations but can be in different
       
  2950                 # formats (refer to RFC5280) but in general only the URI type is
       
  2951                 # used for CRLs. So this is the only thing we support here.
       
  2952 
       
  2953                 try:
       
  2954                         dps = cert.extensions.get_extension_for_oid(
       
  2955                             x509.oid.ExtensionOID.CRL_DISTRIBUTION_POINTS).value
       
  2956                 except x509.ExtensionNotFound:
       
  2957                         return
       
  2958 
       
  2959                 for dp in dps:
       
  2960                         if not dp.full_name:
       
  2961                                 # we don't support relative names
       
  2962                                 continue
       
  2963                         for uri in dp.full_name:
       
  2964                                 if not isinstance(uri,
       
  2965                                     x509.UniformResourceIdentifier):
       
  2966                                         # we only support URIs
       
  2967                                         continue
       
  2968                                 self.__check_crl(cert, ca_dict, str(uri.value))
  2829 
  2969 
  2830         def __check_revocation(self, cert, ca_dict, use_crls):
  2970         def __check_revocation(self, cert, ca_dict, use_crls):
  2831                 hsh = self.__hash_cert(cert)
  2971                 hsh = self.__hash_cert(cert)
  2832                 if hsh in self.revoked_ca_certs:
  2972                 if hsh in self.revoked_ca_certs:
  2833                         raise api_errors.RevokedCertificate(cert,
  2973                         raise api_errors.RevokedCertificate(cert,
  2837 
  2977 
  2838         def __check_extensions(self, cert, usages, cur_pathlen):
  2978         def __check_extensions(self, cert, usages, cur_pathlen):
  2839                 """Check whether the critical extensions in this certificate
  2979                 """Check whether the critical extensions in this certificate
  2840                 are supported and allow the provided use(s)."""
  2980                 are supported and allow the provided use(s)."""
  2841 
  2981 
       
  2982                 try:
       
  2983                         exts = cert.extensions
       
  2984                 except (ValueError, x509.UnsupportedExtension) as e:
       
  2985                         raise api_errors.InvalidCertificateExtensions(
       
  2986                             cert, e)
       
  2987 
  2842                 def check_values(vs):
  2988                 def check_values(vs):
  2843                         for v in vs:
  2989                         for v in vs:
  2844                                 if v in supported_vs:
  2990                                 if v in supported_vs:
  2845                                         continue
  2991                                         continue
  2846                                 if v.startswith("PATHLEN:") and \
  2992                                 # If there is only one extension value, it must
  2847                                     "PATHLEN:" in supported_vs:
  2993                                 # be the problematic one. Otherwise, we also
  2848                                         try:
  2994                                 # output the first unsupported value as the
  2849                                                 cert_pathlen = int(v[len("PATHLEN:"):])
  2995                                 # problematic value following extension value.
  2850                                         except ValueError as e:
       
  2851                                                 raise api_errors.UnsupportedExtensionValue(cert, ext, v)
       
  2852                                         if cur_pathlen > cert_pathlen:
       
  2853                                                 raise api_errors.PathlenTooShort(cert, cur_pathlen, cert_pathlen)
       
  2854                                         continue
       
  2855                                 if len(vs) < 2:
  2996                                 if len(vs) < 2:
  2856                                         raise api_errors.UnsupportedExtensionValue(cert, ext)
  2997                                         raise api_errors.UnsupportedExtensionValue(
  2857                                 else:
  2998                                             cert, ext, ", ".join(vs))
  2858                                         raise api_errors.UnsupportedExtensionValue(cert, ext, v)
  2999                                 raise api_errors.UnsupportedExtensionValue(
  2859 
  3000                                     cert, ext, ", ".join(vs), v)
  2860 
  3001 
  2861                 for i in range(0, cert.get_ext_count()):
  3002                 for ext in exts:
  2862                         ext = cert.get_ext_at(i)
  3003                         etype = type(ext.value)
  2863                         name = ext.get_name()
  3004                         if etype in SUPPORTED_EXTENSION_VALUES:
  2864                         if name == "UNDEF":
  3005                                 supported_vs = SUPPORTED_EXTENSION_VALUES[etype]
  2865                                 continue
  3006                                 keys = EXTENSIONS_VALUES[etype]
  2866                         v = ext.get_value().upper()
  3007                                 if etype == x509.BasicConstraints:
  2867                         # Check whether the extension name is recognized.
  3008                                         pathlen = ext.value.path_length
  2868                         if name in SUPPORTED_EXTENSION_VALUES:
  3009                                         if pathlen is not None and \
  2869                                 supported_vs = \
  3010                                             cur_pathlen > pathlen:
  2870                                     SUPPORTED_EXTENSION_VALUES[name]
  3011                                                 raise api_errors.PathlenTooShort(cert,
  2871                                 vs = [s.strip() for s in v.split(",")]
  3012                                                     cur_pathlen, pathlen)
       
  3013                                 elif etype == x509.KeyUsage:
       
  3014                                         keys = list(EXTENSIONS_VALUES[etype])
       
  3015                                         if not getattr(ext.value,
       
  3016                                             "key_agreement"):
       
  3017                                                 # Cryptography error:
       
  3018                                                 # encipher_only/decipher_only is
       
  3019                                                 # undefined unless key_agreement
       
  3020                                                 # is true
       
  3021                                                 keys.remove("encipher_only")
       
  3022                                                 keys.remove("decipher_only")
       
  3023                                 vs = [
       
  3024                                     key
       
  3025                                     for key in keys
       
  3026                                     if getattr(ext.value, key)
       
  3027                                 ]
  2872                                 # Check whether the values for the extension are
  3028                                 # Check whether the values for the extension are
  2873                                 # recognized.
  3029                                 # recognized.
  2874                                 check_values(vs)
  3030                                 check_values(vs)
  2875                                 uses = usages.get(name, [])
       
  2876                                 if isinstance(uses, six.string_types):
       
  2877                                         uses = [uses]
       
  2878                                 # For each use, check to see whether it's
  3031                                 # For each use, check to see whether it's
  2879                                 # permitted by the certificate's extension
  3032                                 # permitted by the certificate's extension
  2880                                 # values.
  3033                                 # values.
  2881                                 for u in uses:
  3034                                 if etype not in usages:
       
  3035                                         continue
       
  3036                                 for u in usages[etype]:
  2882                                         if u not in vs:
  3037                                         if u not in vs:
  2883                                                 raise api_errors.InappropriateCertificateUse(cert, ext, u)
  3038                                                 raise api_errors.InappropriateCertificateUse(
       
  3039                                                     cert, ext, u, ", ".join(vs))
  2884                         # If the extension name is unrecognized and critical,
  3040                         # If the extension name is unrecognized and critical,
  2885                         # then the chain cannot be verified.
  3041                         # then the chain cannot be verified.
  2886                         elif ext.get_critical():
  3042                         elif ext.critical:
  2887                                 raise api_errors.UnsupportedCriticalExtension(
  3043                                 raise api_errors.UnsupportedCriticalExtension(
  2888                                     cert, ext)
  3044                                     cert, ext)
  2889 
  3045 
  2890         def verify_chain(self, cert, ca_dict, cur_pathlen, use_crls,
  3046         def verify_chain(self, cert, ca_dict, cur_pathlen, use_crls,
  2891             required_names=None, usages=None):
  3047             required_names=None, usages=None):
  2928                                         res[k] = d2[k]
  3084                                         res[k] = d2[k]
  2929                         return res
  3085                         return res
  2930 
  3086 
  2931                 def discard_names(cert, required_names):
  3087                 def discard_names(cert, required_names):
  2932                         for cert_cn in [
  3088                         for cert_cn in [
  2933                             str(c.get_data())
  3089                             str(c.value)
  2934                             for c
  3090                             for c
  2935                             in cert.get_subject().get_entries_by_nid(
  3091                             in cert.subject.get_attributes_for_oid(
  2936                                 m2.X509.X509_Name.nid["CN"])
  3092                                 x509.oid.NameOID.COMMON_NAME)
  2937                         ]:
  3093                         ]:
  2938                                 required_names.discard(cert_cn)
  3094                                 required_names.discard(cert_cn)
  2939 
  3095 
  2940                 if not usages:
  3096                 if not usages:
  2941                         usages = {}
  3097                         usages = {}
  2952                         # If this certificate's CN is in the set of required
  3108                         # If this certificate's CN is in the set of required
  2953                         # names, remove it.
  3109                         # names, remove it.
  2954                         discard_names(cert, required_names)
  3110                         discard_names(cert, required_names)
  2955 
  3111 
  2956                         # Find the certificate that issued this certificate.
  3112                         # Find the certificate that issued this certificate.
  2957                         issuer = cert.get_issuer()
  3113                         issuer = cert.issuer
  2958                         issuer_hash = issuer.as_hash()
  3114                         issuer_hash = hashlib.sha1(misc.force_bytes(
       
  3115                             issuer)).hexdigest()
  2959 
  3116 
  2960                         # See whether this certificate was issued by any of the
  3117                         # See whether this certificate was issued by any of the
  2961                         # given trust anchors.
  3118                         # given trust anchors.
  2962                         for c in ca_dict.get(issuer_hash, []):
  3119                         for c in ca_dict.get(issuer_hash, []):
  2963                                 if cert.verify(c.get_pubkey()):
  3120                                 if self.__verify_x509_signature(cert,
       
  3121                                     c.public_key()):
  2964                                         verified = True
  3122                                         verified = True
  2965                                         # Remove any required names found in the
  3123                                         # Remove any required names found in the
  2966                                         # trust anchor.
  3124                                         # trust anchor.
  2967                                         discard_names(c, required_names)
  3125                                         discard_names(c, required_names)
  2968                                         # If there are more names to check for
  3126                                         # If there are more names to check for
  2974 
  3132 
  2975                         # If the subject and issuer for this certificate are
  3133                         # If the subject and issuer for this certificate are
  2976                         # identical and the certificate hasn't been verified
  3134                         # identical and the certificate hasn't been verified
  2977                         # then this is an untrusted self-signed cert and should
  3135                         # then this is an untrusted self-signed cert and should
  2978                         # be rejected.
  3136                         # be rejected.
  2979                         if cert.get_subject().as_hash() == issuer_hash:
  3137                         if hashlib.sha1(misc.force_bytes(
       
  3138                             cert.subject)).hexdigest() == issuer_hash:
  2980                                 if not verified:
  3139                                 if not verified:
  2981                                         raise \
  3140                                         raise \
  2982                                             api_errors.UntrustedSelfSignedCert(
  3141                                             api_errors.UntrustedSelfSignedCert(
  2983                                             cert)
  3142                                             cert)
  2984                                 # This break should break the
  3143                                 # This break should break the
  3000                                         # the current certificate, and hasn't
  3159                                         # the current certificate, and hasn't
  3001                                         # been revoked, consider it as the
  3160                                         # been revoked, consider it as the
  3002                                         # next link in the chain.  check_ca
  3161                                         # next link in the chain.  check_ca
  3003                                         # checks both the basicConstraints
  3162                                         # checks both the basicConstraints
  3004                                         # extension and the keyUsage extension.
  3163                                         # extension and the keyUsage extension.
  3005                                         if c.check_ca() and \
  3164                                         if misc.check_ca(c) and \
  3006                                             cert.verify(c.get_pubkey()):
  3165                                             self.__verify_x509_signature(cert,
       
  3166                                             c.public_key()):
  3007                                                 problem = False
  3167                                                 problem = False
  3008                                                 # Check whether this certificate
  3168                                                 # Check whether this certificate
  3009                                                 # has a critical extension we
  3169                                                 # has a critical extension we
  3010                                                 # don't understand.
  3170                                                 # don't understand.
  3011                                                 try:
  3171                                                 try: