forked from arqbackup/arq_restore
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AppKeychain.m
271 lines (260 loc) · 10.2 KB
/
AppKeychain.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
//
// AppKeychain.m
// Backup
//
// Created by Stefan Reitshamer on 8/26/09.
// Copyright 2009 PhotoMinds LLC. All rights reserved.
//
#import "AppKeychain.h"
#import "SetNSError.h"
#import "UserLibrary.h"
#define ARQ_S3_LABEL @"Arq S3"
#define ARQ_TWITTER_LABEL @"Arq Twitter Access Token"
#define ARQ_ENCRYPTION_ACCOUNT_LABEL @"EncryptionKey"
@interface AppKeychain (internal)
+ (NSString *)encryptionPasswordLabelForS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID;
+ (BOOL)account:(NSString **)account password:(NSString **)password forLabel:(NSString *)label error:(NSError **)error;
+ (BOOL)findItem:(NSString *)theLabel item:(SecKeychainItemRef *)item error:(NSError **)error;
- (BOOL)loadSecAccess:(NSError **)error;
- (BOOL)deleteLabel:(NSString *)theLabel error:(NSError **)error;
- (BOOL)addLabel:(NSString *)theLabel account:(NSString *)theAccount password:(NSString *)thePassword error:(NSError **)error;
@end
@implementation AppKeychain
+ (BOOL)accessKeyID:(NSString **)accessKey secretAccessKey:(NSString **)secret error:(NSError **)error {
NSString *account = nil;
NSString *password = nil;
if (![AppKeychain account:&account password:&password forLabel:ARQ_S3_LABEL error:error]) {
return NO;
}
if (accessKey != nil) {
*accessKey = account;
}
if (secret != nil) {
*secret = password;
}
return YES;
}
+ (BOOL)containsEncryptionPasswordForS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID {
NSString *encryptionPassword = nil;
NSError *myError = nil;
return [AppKeychain encryptionPassword:&encryptionPassword forS3BucketName:theS3BucketName computerUUID:theComputerUUID error:&myError];
}
+ (BOOL)encryptionPassword:(NSString **)encryptionPassword forS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID error:(NSError **)error {
NSString *label = [AppKeychain encryptionPasswordLabelForS3BucketName:theS3BucketName computerUUID:theComputerUUID];
NSString *account = nil;
NSString *password = nil;
if (![AppKeychain account:&account password:&password forLabel:label error:error]) {
return NO;
}
if (encryptionPassword != nil) {
*encryptionPassword = password;
}
return YES;
}
+ (BOOL)twitterAccessKey:(NSString **)theAccessKey secret:(NSString **)secret error:(NSError **)error {
return [AppKeychain account:theAccessKey password:secret forLabel:ARQ_TWITTER_LABEL error:error];
}
- (id)initWithBackupAppPath:(NSString *)theBackupAppPath
agentAppPath:(NSString *)theAgentAppPath {
if (self = [super init]) {
backupAppPath = [theBackupAppPath copy];
agentAppPath = [theAgentAppPath copy];
}
return self;
}
- (void)dealloc {
if (access != NULL) {
CFRelease(access);
}
[backupAppPath release];
[agentAppPath release];
[super dealloc];
}
- (BOOL)setAccessKeyID:(NSString *)theAccessKeyID
secretAccessKey:(NSString *)theSecretAccessKey
error:(NSError **)error {
return [self addLabel:ARQ_S3_LABEL account:theAccessKeyID password:theSecretAccessKey error:error];
}
- (BOOL)setEncryptionKey:(NSString *)theEncryptionPassword
forS3BucketName:(NSString *)theS3BucketName
computerUUID:(NSString *)theComputerUUID
error:(NSError **)error {
NSString *label = [AppKeychain encryptionPasswordLabelForS3BucketName:theS3BucketName computerUUID:theComputerUUID];
return [self addLabel:label account:ARQ_ENCRYPTION_ACCOUNT_LABEL password:theEncryptionPassword error:error];
}
- (BOOL)setTwitterAccessKey:(NSString *)theKey secret:(NSString *)theSecret error:(NSError **)error {
return [self addLabel:ARQ_TWITTER_LABEL account:theKey password:theSecret error:error];
}
- (BOOL)deleteTwitterAccessKey:(NSError **)error {
return [self deleteLabel:ARQ_TWITTER_LABEL error:error];
}
@end
@implementation AppKeychain (internal)
+ (NSString *)encryptionPasswordLabelForS3BucketName:(NSString *)theS3BucketName computerUUID:(NSString *)theComputerUUID {
return [NSString stringWithFormat:@"Arq Encryption:%@:%@", theS3BucketName, theComputerUUID];
}
+ (BOOL)account:(NSString **)account password:(NSString **)password forLabel:(NSString *)theLabel error:(NSError **)error {
SecKeychainItemRef item = NULL;
if (![AppKeychain findItem:theLabel item:&item error:error]) {
return NO;
}
if (item == NULL) {
SETNSERROR(@"AppKeychainErrorDomain", -1, @"Keychain item %@ not found", theLabel);
return NO;
}
SecKeychainAttributeList *outAttrList = NULL;
UInt32 length;
void *data;
UInt32 tags[] = {
kSecAccountItemAttr
};
UInt32 formats[] = {
CSSM_DB_ATTRIBUTE_FORMAT_STRING
};
SecKeychainAttributeInfo info = {
1,
tags,
formats
};
OSStatus oss = SecKeychainItemCopyAttributesAndData(item, &info, NULL, &outAttrList, &length, &data);
if (oss != noErr) {
NSString *errorMessage = (NSString *)SecCopyErrorMessageString(oss, NULL);
SETNSERROR(@"AppKeychainErrorDomain", -1, @"Error reading data from Keychain item %@: %@ (code %d)", theLabel, errorMessage, oss);
[errorMessage release];
return NO;
}
if (account != nil) {
*account = nil;
for (UInt32 index = 0; index < outAttrList->count; index++) {
SecKeychainAttribute *attr = outAttrList->attr + index;
if (attr->tag == kSecAccountItemAttr) {
*account = [[[NSString alloc] initWithBytes:attr->data length:attr->length encoding:NSUTF8StringEncoding] autorelease];
break;
}
}
}
if (password != nil) {
*password = [[[NSString alloc] initWithBytes:data length:length encoding:NSUTF8StringEncoding] autorelease];
}
SecKeychainItemFreeAttributesAndData(outAttrList, data);
CFRelease(item);
return YES;
}
+ (BOOL)findItem:(NSString *)theLabel item:(SecKeychainItemRef *)item error:(NSError **)error {
*item = NULL;
const char *label = [theLabel UTF8String];
SecKeychainAttribute attrs[] = {
{
kSecLabelItemAttr,
strlen(label),
(char *)label
}
};
SecKeychainAttributeList attributes = {
sizeof(attrs) / sizeof(attrs[0]),
attrs
};
SecKeychainSearchRef searchRef;
OSStatus oss;
oss = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef);
if (oss != noErr) {
SETNSERROR(@"AppKeychainErrorDomain", -1, @"error creating keychain search");
return NO;
}
SecKeychainSearchCopyNext(searchRef, item);
CFRelease(searchRef);
return YES;
}
- (BOOL)loadSecAccess:(NSError **)error {
if (access == NULL) {
NSMutableArray *trustedApplications = [NSMutableArray array];
OSStatus oss;
SecTrustedApplicationRef agentApp;
oss = SecTrustedApplicationCreateFromPath([agentAppPath fileSystemRepresentation], &agentApp);
if (oss != noErr) {
CFStringRef msg = SecCopyErrorMessageString(oss, NULL);
SETNSERROR(@"AppKeychainErrorDomain", -1, @"Error creating Agent trusted application %@: %@ (code %d)", agentAppPath, (NSString *)msg, oss);
CFRelease(msg);
return NO;
}
[trustedApplications addObject:(id)agentApp];
CFRelease(agentApp);
SecTrustedApplicationRef backupApp;
oss = SecTrustedApplicationCreateFromPath([backupAppPath fileSystemRepresentation], &backupApp);
if (oss != noErr) {
CFStringRef msg = SecCopyErrorMessageString(oss, NULL);
SETNSERROR(@"AppKeychainErrorDomain", -1, @"Error creating trusted application: %@ (code %d)", (NSString *)msg, oss);
CFRelease(agentApp);
CFRelease(msg);
return NO;
}
[trustedApplications addObject:(id)backupApp];
CFRelease(backupApp);
oss = SecAccessCreate((CFStringRef)@"Arq", (CFArrayRef)trustedApplications, &access);
if (oss != noErr) {
CFStringRef msg = SecCopyErrorMessageString(oss, NULL);
SETNSERROR(@"AppKeychainErrorDomain", -1, @"Error creating SecAccessRef: %@ (code %d)", (NSString *)msg, oss);
CFRelease(msg);
return NO;
}
}
return YES;
}
- (BOOL)deleteLabel:(NSString *)theLabel error:(NSError **)error {
SecKeychainItemRef item = NULL;
if (![AppKeychain findItem:theLabel item:&item error:error]) {
return NO;
}
if (item != NULL) {
OSStatus oss = SecKeychainItemDelete(item);
CFRelease(item);
if (oss != noErr) {
NSString *errorMessage = (NSString *)SecCopyErrorMessageString(oss, NULL);
SETNSERROR(@"AppKeychainErrorDomain", -1, @"error deleting item for label %@: %@ (code %d)", theLabel, errorMessage, oss);
[errorMessage release];
return NO;
}
}
return YES;
}
- (BOOL)addLabel:(NSString *)theLabel account:(NSString *)theAccount password:(NSString *)thePassword error:(NSError **)error {
if (![self loadSecAccess:error]) {
return NO;
}
if (![self deleteLabel:theLabel error:error]) {
return NO;
}
const char *label = [theLabel UTF8String];
const char *account = [theAccount UTF8String];
const char *password = [thePassword UTF8String];
SecKeychainAttribute attrs[] = {
{
kSecLabelItemAttr,
strlen(label),
(char *)label
},
{
kSecAccountItemAttr,
strlen(account),
(char *)account
},
{
kSecServiceItemAttr,
strlen(label),
(char *)label
}
};
SecKeychainAttributeList attributes = {
sizeof(attrs) / sizeof(attrs[0]),
attrs
};
OSStatus oss = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attributes, strlen(password), password, NULL, access, NULL);
if (oss != noErr) {
NSString *errorMessage = (NSString *)SecCopyErrorMessageString(oss, NULL);
SETNSERROR(@"AppKeychainErrorDomain", -1, @"Error creating keychain item: %@ (code %d)", errorMessage, oss);
[errorMessage release];
return NO;
}
return YES;
}
@end