2

Macでユーティリティを作成していますが、プロキシ情報を自動決定する必要があります。(自動プロキシ構成ファイルから)プロキシホストとポートを取得できましたが、これからキーチェーンからユーザー名を取得するにはどうすればよいですか?

SecKeychainAddInternetPasswordを使用してプロキシパスワードを取得できることは知っていますが、ユーザー名もわかりません。ユーザー名とパスワードを取得する方法はありますか?

4

1 に答える 1

6

少し複雑だと思いました。まず、プロキシがオンかオフかをシステム構成に確認する必要があります。次に、キーチェーンに、特定のプロキシ(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");
}

改善は大歓迎です!

于 2013-02-26T09:06:41.490 に答える