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



11
12
13
# File 'lib/yakg/backend/macos-keychain.rb', line 11

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

.s2i(s) ⇒ Object



122
123
124
# File 'lib/yakg/backend/macos-keychain.rb', line 122

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

Instance Method Details

#delete(acct, svc) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/yakg/backend/macos-keychain.rb', line 68

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



59
60
61
# File 'lib/yakg/backend/macos-keychain.rb', line 59

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

#get(acct, svc) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/yakg/backend/macos-keychain.rb', line 83

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



171
172
173
174
175
176
177
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
# File 'lib/yakg/backend/macos-keychain.rb', line 171

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:



63
64
65
66
# File 'lib/yakg/backend/macos-keychain.rb', line 63

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

#s2i(s) ⇒ Object



125
126
127
# File 'lib/yakg/backend/macos-keychain.rb', line 125

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

#set(acct, value, svc) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/yakg/backend/macos-keychain.rb', line 97

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