如何从KeyChain获取代理主机的用户名? [英] How can I get the username for a proxy host from KeyChain?

查看:87
本文介绍了如何从KeyChain获取代理主机的用户名?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在Mac上编写实用程序,需要自动确定代理信息.我已经设法(从自动代理配置文件中)获取了代理主机和端口,但是如何从钥匙串中获取用户名呢?

I'm writing an utility on the Mac and need to auto-determine proxy information. I've managed to get the proxy host and port (from an automatic proxy configuration file), however how do I obtain the username from the keychain from this?

我知道您可以使用SecKeychainAddInternetPassword获取代理密码,但是我也不知道用户名.有没有办法获取用户名和密码?

I know you can use SecKeychainAddInternetPassword to get the proxy password, but I don't know the username either. Is there a way to get the username AND the password?

推荐答案

我发现它有点复杂.首先,您必须询问系统配置代理是打开还是关闭.然后,您必须询问钥匙串,它在返回的主机名上是否具有给定代理(HTTP,HTTPS等)的任何帐户.然后,如果钥匙串说是,您可以从结果中获取用户名,并要求钥匙串提供匹配的密码.此时,用户可能会看到一条警告,要求您的应用访问密码.

I found it a little complicated. First, you have to ask the system configuration whether the proxy is on or off. Then, you have to ask the keychain if it has any account on the returned hostname for a given proxy (HTTP, HTTPS, etc.). Then, if the keychain says yes, you can get the username from the result and ask the keychain for the matching password. At this point, the user may see an alert asking to allow your app to access the password.

这是一些示例代码(Mac OS X 10.6 +,ARC).

Here's some sample code (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");
}

欢迎改进!

这篇关于如何从KeyChain获取代理主机的用户名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆