Macでユーティリティを作成していますが、プロキシ情報を自動決定する必要があります。(自動プロキシ構成ファイルから)プロキシホストとポートを取得できましたが、これからキーチェーンからユーザー名を取得するにはどうすればよいですか?
SecKeychainAddInternetPasswordを使用してプロキシパスワードを取得できることは知っていますが、ユーザー名もわかりません。ユーザー名とパスワードを取得する方法はありますか?
Macでユーティリティを作成していますが、プロキシ情報を自動決定する必要があります。(自動プロキシ構成ファイルから)プロキシホストとポートを取得できましたが、これからキーチェーンからユーザー名を取得するにはどうすればよいですか?
SecKeychainAddInternetPasswordを使用してプロキシパスワードを取得できることは知っていますが、ユーザー名もわかりません。ユーザー名とパスワードを取得する方法はありますか?
少し複雑だと思いました。まず、プロキシがオンかオフかをシステム構成に確認する必要があります。次に、キーチェーンに、特定のプロキシ(HTTP、HTTPSなど)に対して返されたホスト名にアカウントがあるかどうかを確認する必要があります。次に、キーチェーンが「はい」と答えた場合、結果からユーザー名を取得し、一致するパスワードをキーチェーンに要求できます。この時点で、ユーザーはアプリにパスワードへのアクセスを許可するように求めるアラートが表示される場合があります。
ここにいくつかのサンプルコードがあります(Mac OS X 10.6+、ARC)。
ProxyDetector.h:
#import <Foundation/Foundation.h>
@interface ProxyDetector : NSObject
-(ProxyDetector *)init;
-(void)detectHttpProxyReturningHostname:(NSString **)hostName port:(int *)port username:(NSString **)username password:(NSString **)password;
-(void)detectHttpsProxyReturningHostname:(NSString **)hostName port:(int *)port username:(NSString **)username password:(NSString **)password;
@end
ProxyDetector.m:
#import "ProxyDetector.h"
#import <SystemConfiguration/SCDynamicStoreCopySpecific.h>
#import <SystemConfiguration/SCSchemaDefinitions.h>
#import <Security/Security.h>
@implementation ProxyDetector
-(ProxyDetector *)init;
{
if ((self = [super init])) {
// init
}
return self;
}
void detectProxyWithParams(CFStringRef proxyEnableKey, CFStringRef proxyHostNameKey, CFStringRef proxyPortKey, CFTypeRef proxyProtocol, UInt32 proxyProtocolCode, NSString **hostNamePtr, int *portPtr, NSString **usernamePtr, NSString **passwordPtr)
{
// get general proxy info
CFDictionaryRef proxyInfoCPtr = SCDynamicStoreCopyProxies(NULL);
NSDictionary *proxyInfo = (__bridge NSDictionary *) proxyInfoCPtr;
NSNumber *proxyEnabled = proxyInfo[(__bridge NSString *)proxyEnableKey];
// prefill null values for data we may not set later
*usernamePtr = nil;
*passwordPtr = nil;
// is it enabled?
if (![proxyEnabled intValue]) {
*hostNamePtr = nil;
*portPtr = 0;
return;
}
// we can get hostname and port number from this info, but not username and password
*hostNamePtr = proxyInfo[(__bridge NSString *)proxyHostNameKey];
NSNumber *portNumber = proxyInfo[(__bridge NSString *)proxyPortKey];
*portPtr = [portNumber intValue];
// check in the keychain for username and password
CFArrayRef result = NULL;
OSStatus status = SecItemCopyMatching(
(__bridge CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassInternetPassword, kSecClass,
kSecMatchLimitAll, kSecMatchLimit,
kCFBooleanTrue, kSecReturnAttributes,
proxyProtocol, kSecAttrProtocol,
nil],
(CFTypeRef *) &result
);
if (status != noErr) {
if (status != errSecItemNotFound) {
// unexpected error (else, just no password)
NSString *errorStr = (__bridge NSString *)SecCopyErrorMessageString(status, NULL);
NSLog(@"Error while trying to find proxy username and password for hostname %@; assuming no password: %@", *hostNamePtr, errorStr);
}
return;
}
// check what the keychain got us as results
CFIndex resultCount = CFArrayGetCount(result);
for (CFIndex resultIndex = 0; resultIndex < resultCount; resultIndex++) {
NSDictionary *attrs = (NSDictionary *) CFArrayGetValueAtIndex(result, resultIndex);
// check if the found host matches the host we got earlier
NSString *host = [attrs objectForKey:(id)kSecAttrServer];
if (![host isEqualToString:*hostNamePtr])
continue;
const char *hostCStr = [host UTF8String];
NSString *username = [attrs objectForKey:(id)kSecAttrAccount];
const char *usernameCStr = [username UTF8String];
// we know the username now, so ask keychain for the password
UInt32 passwordLength;
void *passwordData;
// this may trigger UI interaction to allow the password to be accessed by this app
status = SecKeychainFindInternetPassword(NULL, // default user keychains
(UInt32)strlen(hostCStr), hostCStr,
0, NULL, // no security domain
(UInt32)strlen(usernameCStr), usernameCStr,
0, NULL, // no path
0, // ignore port
proxyProtocolCode,
kSecAuthenticationTypeAny,
&passwordLength, &passwordData, NULL);
if (status != noErr) {
// error getting or accessing this password
NSString *errorStr = (__bridge NSString *)SecCopyErrorMessageString(status, NULL);
NSLog(@"Error while trying to find proxy username and password for hostname %@; assuming no password: %@", *hostNamePtr, errorStr);
} else {
// we got everything we needed
*usernamePtr = username;
*passwordPtr = [NSString stringWithUTF8String:passwordData];
break; // only one valid item in the results here anyway
}
}
CFRelease(result);
}
-(void)detectHttpProxyReturningHostname:(NSString **)hostName port:(int *)port username:(NSString **)username password:(NSString **)password;
{
detectProxyWithParams(kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort, kSecAttrProtocolHTTPProxy, kSecProtocolTypeHTTPProxy, hostName, port, username, password);
}
-(void)detectHttpsProxyReturningHostname:(NSString **)hostName port:(int *)port username:(NSString **)username password:(NSString **)password;
{
detectProxyWithParams(kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort, kSecAttrProtocolHTTPSProxy, kSecProtocolTypeHTTPSProxy, hostName, port, username, password);
}
@end
使用例:
NSString *hostName;
int port;
NSString *username;
NSString *password;
ProxyDetector *proxyDetector = [[ProxyDetector alloc] init];
[proxyDetector detectHttpProxyReturningHostname:&hostName port:&port username:&username password:&password];
if (hostName) {
if (username) {
NSLog(@"HTTP proxy with authentication: http://%@:%@@%@:%d", username, password, hostName, port);
} else {
NSLog(@"HTTP proxy without authentication: http://%@:%d", hostName, port);
}
} else {
NSLog(@"No HTTP proxy");
}
[proxyDetector detectHttpsProxyReturningHostname:&hostName port:&port username:&username password:&password];
if (hostName) {
if (username) {
NSLog(@"HTTPS proxy with authentication: http://%@:%@@%@:%d", username, password, hostName, port);
} else {
NSLog(@"HTTPS proxy without authentication: http://%@:%d", hostName, port);
}
} else {
NSLog(@"No HTTPS proxy");
}
改善は大歓迎です!