-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathPasswordHelper.m
232 lines (179 loc) · 7.91 KB
/
PasswordHelper.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
//
// PasswordHelper.m
//
// Created by Ira Cooke on 27/07/2009.
// Copyright 2009 Mudflat Software.
// Copyright (c) 2013 Codinn Studio.
//
#import "PasswordHelper.h"
@interface PasswordHelper (Private)
// These are convenience methods (see bottom of page). They are largely based on apple's examples and are simplified wrappers for C functions in the keychain API.
+ (OSStatus) storePasswordKeychain:(NSString*)password host:(NSString*) hostnameStr port:(int)hostport user:(NSString*) usernameStr;
+ (OSStatus) getPasswordKeychain:(void*)passwordData length:(UInt32*) passwordLength itemRef:(SecKeychainItemRef *)itemRef host:(NSString*) hostnameStr port:(int) hostport user:(NSString*) usernameStr;
+ (OSStatus) changePasswordKeychain:(SecKeychainItemRef)itemRef password:(NSString*) newPassword;
@end
@implementation PasswordHelper
+ (NSArray *) promptForPassword:(NSString*)hostname port:(int) hostport user:(NSString*) username {
CFUserNotificationRef passwordDialog;
SInt32 error;
CFOptionFlags responseFlags;
int button;
CFStringRef passwordRef;
NSMutableArray *returnArray = [NSMutableArray arrayWithObjects:@"PasswordString",[NSNumber numberWithInt:0],[NSNumber numberWithInt:1],nil];
NSString* hostString = [NSString stringWithFormat:@"%@@%@:%d", username, hostname, hostport];
NSString *passwordMessageString = [NSString stringWithFormat:@"Enter the password for user “%@”.",
hostString];
NSString* headerString = [NSString stringWithFormat:@"SSH Proxy connecting to the SSH server “%@”.",
hostString];
NSDictionary *panelDict = [NSDictionary dictionaryWithObjectsAndKeys:
headerString,kCFUserNotificationAlertHeaderKey,
passwordMessageString,kCFUserNotificationAlertMessageKey,
@"",kCFUserNotificationTextFieldTitlesKey,
@"Cancel",kCFUserNotificationAlternateButtonTitleKey,
@"Remember this password in my keychain",kCFUserNotificationCheckBoxTitlesKey,
nil];
passwordDialog = CFUserNotificationCreate(kCFAllocatorDefault,
0,
kCFUserNotificationPlainAlertLevel
| CFUserNotificationSecureTextField(0)
| CFUserNotificationCheckBoxChecked(0),
&error,
(__bridge CFDictionaryRef)panelDict);
if (error){
// There was an error creating the password dialog
CFRelease(passwordDialog);
[returnArray replaceObjectAtIndex:1 withObject:[NSNumber numberWithInt:error]];
return returnArray;
}
error = CFUserNotificationReceiveResponse(passwordDialog,
0,
&responseFlags);
if (error){
CFRelease(passwordDialog);
[returnArray replaceObjectAtIndex:1 withObject:[NSNumber numberWithInt:error]];
return returnArray;
}
button = responseFlags & 0x3;
if (button == kCFUserNotificationAlternateResponse) {
CFRelease(passwordDialog);
[returnArray replaceObjectAtIndex:1 withObject:[NSNumber numberWithInt:1]];
return returnArray;
}
if ( responseFlags & CFUserNotificationCheckBoxChecked(0) ){
[returnArray replaceObjectAtIndex:2 withObject:[NSNumber numberWithInt:0]];
}
passwordRef = CFUserNotificationGetResponseValue(passwordDialog,
kCFUserNotificationTextFieldValuesKey,
0);
[returnArray replaceObjectAtIndex:0 withObject:(__bridge NSString*)passwordRef];
CFRelease(passwordDialog); // Note that this will release the passwordRef as well
return returnArray;
}
//! Simply looks for the keychain entry corresponding to a username and hostname and returns it. Returns nil if the password is not found
+ (NSString*) passwordForHost:(NSString*)hostnameStr port:(int) hostport user:(NSString*) usernameStr {
if ( hostnameStr == nil || usernameStr == nil ){
return nil;
}
UInt32 passwordLen=0;
void *passwordData = NULL;
SecKeychainItemRef itemRef = NULL;
// Look for a password in the keychain
OSStatus findKeychainItemStatus = [PasswordHelper getPasswordKeychain:&passwordData length:&passwordLen itemRef:&itemRef host:hostnameStr port:hostport user:usernameStr];
if ( findKeychainItemStatus == noErr ){
NSString *returnString = [[NSString alloc] initWithBytes:passwordData
length:passwordLen encoding:NSUTF8StringEncoding];
SecKeychainItemFreeContent(NULL,passwordData);
return returnString;
} else {
return nil;
}
}
/*! Set the password into the keychain for a specific user and host. If the username/hostname combo already has an entry in the keychain then change it. If not then add a new entry */
+ (BOOL) setPassword:(NSString*)newPassword forHost:(NSString*)hostname port:(int) hostport user:(NSString*) username {
if ( hostname == nil || username == nil ){
return NO;
}
// Look for a password in the keychain
SecKeychainItemRef itemRef = nil;
UInt32 passwordLen = 0;
void *passwordData = NULL;
OSStatus findKeychainItemStatus;
findKeychainItemStatus = [PasswordHelper getPasswordKeychain:&passwordData length:&passwordLen itemRef:&itemRef host:hostname port:hostport user:username];
if ( findKeychainItemStatus == noErr ){
// The keychain item already exists but it needs to be updated
[PasswordHelper changePasswordKeychain:itemRef password:newPassword];
SecKeychainItemFreeContent(NULL,passwordData);
return YES;
} else {
[PasswordHelper storePasswordKeychain:newPassword host:hostname port:hostport user:username];
return YES;
}
}
#pragma mark simple wrappers for keychain access functions
//! Add an internet password to the default keychain.
+ (OSStatus) storePasswordKeychain:(NSString*) passwordStr
host:(NSString*) hostnameStr
port:(int) hostport
user:(NSString*) usernameStr
{
UInt32 passwordLength=(UInt32)[passwordStr length];
void* password = (void*)[passwordStr UTF8String];
UInt32 hostnameLength = (UInt32)[hostnameStr length];
const char* hostname=[hostnameStr UTF8String];
UInt32 usernameLength = (UInt32)[usernameStr length];
const char* username=[usernameStr UTF8String];
return SecKeychainAddInternetPassword(NULL,
hostnameLength,
hostname,
0,
NULL,
usernameLength,
username,
strlen(""),
"",
hostport,
kSecProtocolTypeSSH,
kSecAuthenticationTypeDefault,
passwordLength,
password,
NULL);
}
//! Get password from the keychain. If this succeeds it allocates password data and must therefore be followed by a call to SecKeychainItemFreeContent
+ (OSStatus) getPasswordKeychain:(void*)passwordData
length:(UInt32*) passwordLength
itemRef:(SecKeychainItemRef *)itemRef
host:(NSString*) hostnameStr
port:(int) hostport
user:(NSString*) usernameStr
{
UInt32 hostnameLength = (UInt32)[hostnameStr length];
const char* hostname=[hostnameStr UTF8String];
UInt32 usernameLength = (UInt32)[usernameStr length];
const char* username=[usernameStr UTF8String];
return SecKeychainFindInternetPassword(NULL,
hostnameLength,
hostname,
0,
NULL,
usernameLength,
username,
strlen(""),
"",
hostport,
kSecProtocolTypeSSH,
kSecAuthenticationTypeDefault,
passwordLength,
passwordData,
itemRef);
}
//! Cocoa wrapper for SecKeychainItemModifyAttributesAndData
+ (OSStatus) changePasswordKeychain:(SecKeychainItemRef)itemRef password:(NSString*) newPassword
{
if ( !newPassword )
newPassword=@"";
void* cnewpassword=(void*)[newPassword UTF8String];
UInt32 passwordLength = (UInt32)strlen(cnewpassword);
return SecKeychainItemModifyAttributesAndData(itemRef,
NULL, passwordLength, cnewpassword);
}
@end