示例#1
0
def check_keychain_identity(identity, allowInteraction=False):
    """
    Verify that the Keychain identity exists and that the private key is accessible.

    @param identity: identity value to match
    @type identity: L{str}

    @return: empty L{str} if OK, error message if not
    @rtype: L{str}
    """

    # Always turn off user interaction
    security.SecKeychainSetUserInteractionAllowed(allowInteraction)

    try:
        secidentity = load_keychain_identity(identity)
    except Error:
        return "Unable to load Keychain identity: {}".format(identity)
    pkey = ffi.new("SecKeyRef *")
    err = security.SecIdentityCopyPrivateKey(secidentity.ref(), pkey)
    if err != 0:
        return "Unable to load private key for Keychain identity: {}".format(
            identity)
    pkey = CFObjectRef(pkey[0])

    # Try to sign some data with the pkey to check we have access
    error = ffi.new("CFErrorRef *")
    signer = security.SecSignTransformCreate(pkey.ref(), error)
    if error[0] != ffi.NULL:
        cferror = CFErrorRef(error[0])
        return "Unable to use private key for Keychain identity: {} - {}".format(
            identity, cferror.description())
    signer = CFObjectRef(signer)

    signMe = CFDataRef.fromString("sign me")
    security.SecTransformSetAttribute(signer.ref(),
                                      security.kSecTransformInputAttributeName,
                                      signMe.ref(), error)
    if error[0] != ffi.NULL:
        cferror = CFErrorRef(error[0])
        return "Unable to use private key for Keychain identity: {} - {}".format(
            identity, cferror.description())

    signature = security.SecTransformExecute(signer.ref(), error)
    if error[0] != ffi.NULL or signature == ffi.NULL:
        cferror = CFErrorRef(error[0])
        return "Unable to use private key for Keychain identity: {} - {}".format(
            identity, cferror.description())
    signature = CFObjectRef(signature)

    return ""
示例#2
0
def _load_keychain_item(identifier):
    """
    Load a certificate with the supplied identity string.

    @param identifier: name of the KeyChain item to lookup
    @type identifier: L{str}

    @return: the certificate
    @rtype: L{X509}
    """

    # First try to get the identity from the KeyChain
    name = CFStringRef.fromString(identifier)
    certificate = security.SecCertificateCopyPreferred(name.ref(), ffi.NULL)
    if certificate == ffi.NULL:
        try:
            identity = load_keychain_identity(identifier)
        except Error:
            raise Error(
                "Identity for preferred name '{}' was not found".format(
                    identifier))
        certificate = ffi.new("SecCertificateRef *")
        err = security.SecIdentityCopyCertificate(identity.ref(), certificate)
        if err != 0:
            raise Error(
                "Identity for preferred name '{}' was not found".format(
                    identifier))
        certificate = certificate[0]
    certificate = CFObjectRef(certificate)

    return X509(certificate)
示例#3
0
def _load_certificate_data(certtype, buffer, result_typeid):
    """
    Load a certificate with the supplied type and data.

    @param certtype: ignored
    @type certtype: -
    @param buffer: name of the KeyChain item to lookup
    @type buffer: L{str}
    @param result_typeid: The type to return (certificate or key)
    @type result_typeid: L{ffi.CFTypeID}

    @return: the certificate
    @rtype: L{X509}
    """

    # First try to get the identity from the KeyChain
    data = CFDataRef.fromString(buffer)
    results = ffi.new("CFArrayRef *")
    err = security.SecItemImport(data.ref(), ffi.NULL, ffi.NULL, ffi.NULL, 0,
                                 ffi.NULL, ffi.NULL, results)
    if err != 0:
        raise Error("Could not load certificate data")

    results = CFArrayRef(results[0]).toList()

    # Try to find a SecCertificateRef
    for result in results:
        if result.instanceTypeId() == result_typeid:
            return result
    else:
        raise Error("No certificate in data")
示例#4
0
    def connect(self):
        """
        Create the SecureTransport SSLContextRef object and initialize it.
        """

        self.ctx = security.SSLCreateContext(ffi.NULL, security.kSSLClientSide if self.is_client else security.kSSLServerSide, security.kSSLStreamType)

        minVersion = None
        for option, minValue in (
            (OP_NO_SSLv2, security.kSSLProtocol3),
            (OP_NO_SSLv3, security.kTLSProtocol1),
            (OP_NO_TLSv1, security.kTLSProtocol11),
            (OP_NO_TLSv1_1, security.kTLSProtocol12),
            (OP_NO_TLSv1_2, security.kTLSProtocol12),   # TLS1.2 is the highest supported right now
        ):
            if option in self.context.options:
                minVersion = minValue
        if minVersion is not None:
            security.SSLSetProtocolVersionMin(self.ctx, minVersion)

        # Make sure we have a reference back to this L{Connection} in the SecureTransport callbacks
        self.connref = ffi.new("int *", self.engine_id)
        err = security.SSLSetConnection(self.ctx, ffi.cast("SSLConnectionRef", self.connref))
        if err:
            self.shutdown()
            raise Error(err)

        # Setup the actual SecureTransport callbacks
        err = security.SSLSetIOFuncs(self.ctx, self._read, self._write)
        if err:
            self.shutdown()
            raise Error(err)

        # Must have a certificate identity if we are a server
        if not self.is_client and self.context.identity is None:
            self.shutdown()
            raise Error("No certificate")

        # Must have a peer name if we are a client
        if self.is_client and not self.context.peerName:
            self.shutdown()
            raise Error("No peer name set with client connection.")
        elif self.is_client:
            # Always set the client peer name for proper certificate validation
            err = security.SSLSetPeerDomainName(self.ctx, self.context.peerName, len(self.context.peerName))
            if err:
                self.shutdown()
                raise Error(err)

        # Add the certificate
        if self.context.identity is not None:
            certs = CFArrayRef.fromList([self.context.identity])
            err = security.SSLSetCertificate(self.ctx, certs.ref())
            if err:
                self.shutdown()
                raise Error(err)
示例#5
0
    def _sslread(self, bytes):
        """
        Wrapper for SecureTransport SSLRead method.

        @param bytes: maximum length of data to read
        @type bytes: L{int}

        @return: the data read
        @rtype: L{ffi.buffer}
        """

        read = ffi.new("char[]", bytes)
        processed = ffi.new("size_t *")
        err = security.SSLRead(self.ctx, read, bytes, processed)
        if err == 0:
            response = ffi.buffer(read, processed[0])
            return response
        elif err == security.errSSLWouldBlock:
            raise WantReadError
        elif err == security.errSSLClosedGraceful:
            raise ZeroReturnError
        else:
            self.shutdown()
            raise Error(err)
示例#6
0
    def _sslwrite(self, data):
        """
        Wrapper for SecureTransport SSLWrite method.

        @param data: data to write
        @type data: L{str}
        """

        processed = ffi.new("size_t *")
        err = security.SSLWrite(self.ctx, data, len(data), processed)
        if err == 0:
            return processed[0]
        elif err == security.errSSLWouldBlock:
            return processed[0]
        else:
            self.shutdown()
            raise Error(err)
示例#7
0
def load_keychain_identity(identity):
    """
    Retrieve a SecIdentityRef from the KeyChain with a identity that exactly matches the passed in value.

    @param identity: identity value to match
    @type identity: L{str}

    @return: matched SecIdentityRef item or L{None}
    @rtype: L{CFObjectRef}
    """

    # First try to load this from an identity preference
    cfsubject = CFStringRef.fromString(identity)
    secidentity = security.SecIdentityCopyPreferred(cfsubject.ref(), ffi.NULL,
                                                    ffi.NULL)
    if secidentity != ffi.NULL:
        return CFObjectRef(secidentity)

    # Now iterate items to find a match
    match = CFDictionaryRef.fromDict({
        CFStringRef.fromRef(security.kSecClass):
        CFStringRef.fromRef(security.kSecClassIdentity),
        CFStringRef.fromRef(security.kSecReturnRef):
        CFBooleanRef.fromBool(True),
        CFStringRef.fromRef(security.kSecReturnAttributes):
        CFBooleanRef.fromBool(True),
        CFStringRef.fromRef(security.kSecMatchLimit):
        CFStringRef.fromRef(security.kSecMatchLimitAll),
    })
    result = ffi.new("CFTypeRef *")
    err = security.SecItemCopyMatching(match.ref(), result)
    if err != 0:
        return None

    result = CFArrayRef(result[0])
    for item in result.toList():
        if item[str(CFStringRef.fromRef(security.kSecAttrLabel))] == identity:
            secidentity = item[str(CFStringRef.fromRef(security.kSecValueRef))]
            break
    else:
        raise Error("Could not find Keychain identity: {}".format(identity))

    return secidentity
示例#8
0
    def get_subject(self):
        """
        Use Security.framework to extract the componentized SubjectName field and map OID
        values to strings and store in an L{X509Name} object.
        """
        keys = CFArrayRef.fromList(
            [CFStringRef.fromRef(security.kSecOIDX509V1SubjectName)])
        error = ffi.new("CFErrorRef *")
        values = security.SecCertificateCopyValues(self._x509.ref(),
                                                   keys.ref(), error)
        if values == ffi.NULL:
            error = CFErrorRef(error[0])
            raise Error("Unable to get certificate subject")
        values = CFDictionaryRef(values).toDict()
        value = values[str(
            CFStringRef.fromRef(security.kSecOIDX509V1SubjectName))]

        components = {}
        if value[str(CFStringRef.fromRef(
                security.kSecPropertyKeyType))] == str(
                    CFStringRef.fromRef(security.kSecPropertyTypeSection)):
            for item in value[str(
                    CFStringRef.fromRef(security.kSecPropertyKeyValue))]:
                if item[str(CFStringRef.fromRef(
                        security.kSecPropertyKeyType))] == str(
                            CFStringRef.fromRef(
                                security.kSecPropertyTypeString)):
                    v = item[str(
                        CFStringRef.fromRef(security.kSecPropertyKeyValue))]
                    k = OID2STR.get(
                        item[str(
                            CFStringRef.fromRef(
                                security.kSecPropertyKeyLabel))], "Unknown")
                    components[k] = v

        return X509Name("Subject Name", components)
示例#9
0
    def test_cms_sign_verify_ok(self):
        """
        Use the simple encode.
        """

        # Sign some data
        sign = "Something to be signed"
        result = ffi.new("CFDataRef *")
        signer = load_keychain_identity("org.calendarserver.test")
        error = cms.CMSEncodeContent(
            signer.ref(),
            ffi.NULL,
            ffi.NULL,
            False,
            cms.kCMSAttrNone,
            sign,
            len(sign),
            result,
        )
        self.assertEqual(error, 0)
        result = CFDataRef(result[0])
        self.assertNotEqual(result.count(), 0)

        # Now verify the result
        decoder = ffi.new("CMSDecoderRef *")
        error = cms.CMSDecoderCreate(decoder)
        self.assertEqual(error, 0)
        decoder = CFObjectRef(decoder[0])

        error = cms.CMSDecoderUpdateMessage(decoder.ref(), result.toString(), result.count())
        self.assertEqual(error, 0)

        error = cms.CMSDecoderFinalizeMessage(decoder.ref())
        self.assertEqual(error, 0)

        number = ffi.new("size_t *")
        error = cms.CMSDecoderGetNumSigners(decoder.ref(), number)
        self.assertEqual(error, 0)
        self.assertEqual(number[0], 1)

        encrypted = ffi.new("Boolean *")
        error = cms.CMSDecoderIsContentEncrypted(decoder.ref(), encrypted)
        self.assertEqual(error, 0)
        self.assertEqual(encrypted[0], False)

        policy = cms.SecPolicyCreateBasicX509()
        policy = CFObjectRef(policy)
        status = ffi.new("CMSSignerStatus *")
        verify_result = ffi.new("OSStatus *")
        error = cms.CMSDecoderCopySignerStatus(
            decoder.ref(),
            0,
            policy.ref(),
            True,
            status,
            ffi.NULL,
            verify_result,
        )
        self.assertEqual(error, 0)
        self.assertEqual(status[0], cms.kCMSSignerValid)
        self.assertEqual(verify_result[0], 0)

        result = ffi.new("CFDataRef *")
        error = cms.CMSDecoderCopyContent(decoder.ref(), result)
        self.assertEqual(error, 0)
        result = CFDataRef(result[0])
        self.assertEqual(result.toString(), sign)