Module: Yakg::Backend::MacosKeychain

Extended by:
FFI::Library
Included in:
Yakg::Backend
Defined in:
lib/yakg/backend/macos-keychain.rb

Defined Under Namespace

Classes: KeychainError, SecKeychainAttribute, SecKeychainAttributeInfo, SecKeychainAttributeList

Constant Summary collapse

NULL =
FFI::Pointer::NULL

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.framework(name) ⇒ Object



18
19
20
# File 'lib/yakg/backend/macos-keychain.rb', line 18

def self.framework name
  "/System/Library/Frameworks/#{name}.framework/#{name}"
end

.s2i(s) ⇒ Object



129
130
131
# File 'lib/yakg/backend/macos-keychain.rb', line 129

def self.s2i s
  s.unpack("N")[0].to_i
end

Instance Method Details

#delete(acct, svc) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/yakg/backend/macos-keychain.rb', line 75

def delete acct, svc
  pw_length = FFI::MemoryPointer.new :uint32
  pw_val = FFI::MemoryPointer.new :pointer
  item_ref = FFI::MemoryPointer.new :pointer
  retval = SecKeychainFindGenericPassword(NULL, svc.length, svc,
                                          acct.length, acct,
                                          pw_length, pw_val,
                                          item_ref)
  return nil unless 0 == retval
  raise_error? SecKeychainItemFreeContent(NULL, pw_val.read_pointer)
  raise_error? SecKeychainItemDelete(item_ref.read_pointer)
  CFRelease item_ref.read_pointer
  true
end

#error_message(code) ⇒ Object



66
67
68
# File 'lib/yakg/backend/macos-keychain.rb', line 66

def error_message code
  CF::String.new(SecCopyErrorMessageString(code, NULL)).to_s
end

#get(acct, svc) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/yakg/backend/macos-keychain.rb', line 90

def get acct, svc
  pw_length = FFI::MemoryPointer.new :uint32
  pw_val = FFI::MemoryPointer.new :pointer
  retval = SecKeychainFindGenericPassword(NULL, svc.length, svc,
                                          acct.length, acct,
                                          pw_length, pw_val, NULL)
  return nil unless 0 == retval
  pw = pw_val.read_pointer.read_string(pw_length.read_int)
  retval = SecKeychainItemFreeContent(NULL, pw_val.read_pointer)
  return nil unless 0 == retval

  pw
end

#list(svc) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/yakg/backend/macos-keychain.rb', line 178

def list svc
  search_ref = FFI::MemoryPointer.new :pointer
  found_item = FFI::MemoryPointer.new :pointer
  found_attr_list = FFI::MemoryPointer.new :pointer
  svc_attr = SecKeychainAttribute.new

  acct_info = SecKeychainAttributeInfo.new
  acct_info[:count] = 1
  acct_info[:tag] = FFI::MemoryPointer.new :uint32
  acct_info[:tag].write_array_of_int [s2i("acct")]
  acct_info[:format] = FFI::MemoryPointer.new :uint32
  acct_info[:format].write_array_of_int [0]

  svc_attr[:tag] = s2i "svce"
  svc_attr[:length] = svc.length
  svc_attr[:data] = FFI::MemoryPointer.from_string(svc)

  search_attr_list = SecKeychainAttributeList.new
  search_attr_list[:count] = 1
  search_attr_list[:attr] = svc_attr.pointer

  raise_error? SecKeychainSearchCreateFromAttributes(NULL,
                                                     s2i("genp"),
                                                     search_attr_list,
                                                     search_ref)

  acct_names = []
  while true
    retval = SecKeychainSearchCopyNext(search_ref.read_pointer,
                                       found_item)
    # the magic "no more items" number
    break if retval == 4294941996
    raise_error? retval

    raise_error? SecKeychainItemCopyAttributesAndData(found_item.read_pointer,
                                                      acct_info,
                                                      NULL,
                                                      found_attr_list,
                                                      NULL, NULL)
    f = SecKeychainAttributeList.new found_attr_list.read_pointer
    next if f[:count] != 1

    a = SecKeychainAttribute.new f[:attr]
    next if a[:tag] != :kSecAccountItemAttr

    acct_names.push a[:data].get_string(0, a[:length])
  end

  raise_error? SecKeychainItemFreeAttributesAndData(found_attr_list.read_pointer,
                                                    NULL)
  CFRelease search_ref.read_pointer

  acct_names
end

#raise_error?(code) ⇒ Boolean

Returns:

  • (Boolean)

Raises:



70
71
72
73
# File 'lib/yakg/backend/macos-keychain.rb', line 70

def raise_error? code
  raise KeychainError.new(error_message code) if code != 0
  code
end

#s2i(s) ⇒ Object



132
133
134
# File 'lib/yakg/backend/macos-keychain.rb', line 132

def s2i s
  s.unpack("N")[0].to_i
end

#set(acct, value, svc) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/yakg/backend/macos-keychain.rb', line 104

def set acct, value, svc
  pw_length = FFI::MemoryPointer.new :uint32
  pw_val = FFI::MemoryPointer.new :pointer
  item_ref = FFI::MemoryPointer.new :pointer
  retval = SecKeychainFindGenericPassword(NULL, svc.length, svc,
                                          acct.length, acct,
                                          pw_length, pw_val, item_ref)
  if retval != 0
    raise_error? SecKeychainAddGenericPassword(NULL, svc.length, svc,
                                               acct.length, acct,
                                               value.length, value, NULL)
  else
    raise_error? SecKeychainItemFreeContent(NULL, pw_val.read_pointer)
    raise_error? SecKeychainItemModifyContent(item_ref.read_pointer, NULL,
                                              value.length, value)
    CFRelease item_ref.read_pointer
  end
  true
end