# The module to which FFI attaches constants module Sec extend FFI::Library ffi_lib '/System/Library/Frameworks/Security.framework/Security' typedef :int32, :osstatus typedef :pointer, :keychainref enum [ :errSecItemNotFound, -25300, :errSecDuplicateItem, -25299, :errSecAuthFailed, -25293, :errSecNoSuchKeychain, -25294, :errCancelled, -128, :errSecInteractionNotAllowed, -25308 ] attach_variable 'kSecClass', :pointer attach_variable 'kSecClassInternetPassword', :pointer attach_variable 'kSecClassGenericPassword', :pointer attach_variable 'kSecClassCertificate', :pointer attach_variable 'kSecClassIdentity', :pointer attach_variable 'kSecClassKey', :pointer attach_variable 'kSecAttrAccess', :pointer attach_variable 'kSecAttrAccount', :pointer attach_variable 'kSecAttrAuthenticationType', :pointer attach_variable 'kSecAttrComment', :pointer attach_variable 'kSecAttrCreationDate', :pointer attach_variable 'kSecAttrCreator', :pointer attach_variable 'kSecAttrDescription', :pointer attach_variable 'kSecAttrGeneric', :pointer attach_variable 'kSecAttrIsInvisible', :pointer attach_variable 'kSecAttrIsNegative', :pointer attach_variable 'kSecAttrLabel', :pointer attach_variable 'kSecAttrModificationDate', :pointer attach_variable 'kSecAttrPath', :pointer attach_variable 'kSecAttrPort', :pointer attach_variable 'kSecAttrProtocol', :pointer attach_variable 'kSecAttrSecurityDomain', :pointer attach_variable 'kSecAttrServer', :pointer attach_variable 'kSecAttrService', :pointer attach_variable 'kSecAttrType', :pointer attach_variable 'kSecMatchSearchList', :pointer attach_variable 'kSecMatchLimit', :pointer attach_variable 'kSecMatchLimitOne', :pointer attach_variable 'kSecMatchLimitAll', :pointer attach_variable 'kSecMatchItemList', :pointer attach_variable 'kSecReturnAttributes', :pointer attach_variable 'kSecReturnRef', :pointer attach_variable 'kSecReturnData', :pointer attach_variable 'kSecValueRef', :pointer attach_variable 'kSecValueData', :pointer attach_variable 'kSecUseKeychain', :pointer # Query options for use with SecCopyMatching, SecItemUpdate # module Query #key identifying the class of an item (kSecClass) CLASS = CF::Base.typecast(Sec.kSecClass) #key speciying the list of keychains to search (kSecMatchSearchList) SEARCH_LIST = CF::Base.typecast(Sec.kSecMatchSearchList) #key indicating the list of specific keychain items to the scope the search to ITEM_LIST = CF::Base.typecast(Sec.kSecMatchItemList) #key indicating whether to return attributes (kSecReturnAttributes) RETURN_ATTRIBUTES = CF::Base.typecast(Sec.kSecReturnAttributes) #key indicating whether to return the SecKeychainItemRef (kSecReturnRef) RETURN_REF = CF::Base.typecast(Sec.kSecReturnRef) #key indicating whether to return the password data (kSecReturnData) RETURN_DATA = CF::Base.typecast(Sec.kSecReturnData) #key indicating which keychain to use for the operation (kSecUseKeychain) KEYCHAIN = CF::Base.typecast(Sec.kSecUseKeychain) end # defines constants for use as the class of an item module Classes # constant identifying certificates (kSecClassCertificate) CERTIFICATE = CF::Base.typecast(Sec.kSecClassCertificate) # constant identifying generic passwords (kSecClassGenericPassword) GENERIC = CF::Base.typecast(Sec.kSecClassGenericPassword) # constant identifying generic passwords (kSecClassIdentity) IDENTITY = CF::Base.typecast(Sec.kSecClassIdentity) # constant identifying internet passwords (kSecClassInternetPassword) INTERNET = CF::Base.typecast(Sec.kSecClassInternetPassword) # constant identifying public/private key items (kSecClassKey) KEY = CF::Base.typecast(Sec.kSecClassKey) end # Search match types for use with SecCopyMatching module Search #meta value for {Sec::Search::LIMIT} indicating that all items be returned (kSecMatchLimitAll) ALL = CF::Base.typecast(Sec.kSecMatchLimitAll) # hash key indicating the maximum number of items (kSecMatchLimit) LIMIT = CF::Base.typecast(Sec.kSecMatchLimit) end # Constants for use with SecItemAdd/SecItemUpdate module Value # The hash key for the SecKeychainItemRef (in the dictionary returned by SecCopyMatching) (kSecValueRef) REF = CF::Base.typecast(Sec.kSecValueRef) # The hash key for the password data (in the dictionary returned by SecCopyMatching) (kSecValueData) DATA = CF::Base.typecast(Sec.kSecValueData) end # The base class of all CF types from the security framework # # @abstract class Base < CF::Base attr_reader :attributes def self.register_type(type_name) Sec.attach_function "#{type_name}GetTypeID", [], CF.find_type(:cftypeid) @@type_map[Sec.send("#{type_name}GetTypeID")] = self end def self.define_attributes(attr_map) attr_map.values.each do |ruby_name| unless method_defined?(ruby_name) define_method ruby_name do self.attributes[ruby_name] end define_method ruby_name.to_s+'=' do |value| self.attributes[ruby_name] = value end end end end def initialize(ptr) super @attributes = {} end def update_self_from_dictionary(cf_dict) @attributes = cf_dict.inject({}) do |memo, (k,v)| if ruby_name = self.class::ATTR_MAP[k] memo[ruby_name] = v.to_ruby end memo end end # Returns the keychain the item is in # # @return [Keychain::Keychain] def keychain out = FFI::MemoryPointer.new :pointer status = Sec.SecKeychainItemCopyKeychain(self,out) Sec.check_osstatus(status) CF::Base.new(out.read_pointer).release_on_gc end def load_attributes result = FFI::MemoryPointer.new :pointer status = Sec.SecItemCopyMatching({Sec::Query::SEARCH_LIST => [self.keychain], Sec::Query::ITEM_LIST => [self], Sec::Query::CLASS => self.klass, Sec::Query::RETURN_ATTRIBUTES => true, Sec::Query::RETURN_REF => false}.to_cf, result) Sec.check_osstatus(status) cf_dict = CF::Base.typecast(result.read_pointer) update_self_from_dictionary(cf_dict) end end # If the result is non-zero raises an exception. # # The exception will have the result code as well as a human readable description # # @param [Integer] result the status code to check # @raise [Keychain::Error] is the result is non zero def self.check_osstatus result if result != 0 case result when Sec.enum_value(:errSecDuplicateItem) raise Keychain::DuplicateItemError.new(result) when Sec.enum_value(:errCancelled) raise Keychain::UserCancelledError.new(result) when Sec.enum_value(:errSecAuthFailed) raise Keychain::AuthFailedError.new(result) when Sec.enum_value(:errSecNoSuchKeychain) raise Keychain::NoSuchKeychainError.new(result) when Sec.enum_value(:errSecInteractionNotAllowed) raise Keychain::InteractionNotAllowedError.new(result) else raise Keychain::Error.new(result) end end end end