How do I officially get the password for a proxy server?

Using a Swift playground (from Xcode 8.3.3) with the "SCDynamicStoreCopyProxies(_:)" function while messing around with "System Preferences" -> Network -> Advanced -> Proxies and "Keychain Access", I fiigured out how the user-name and password for a proxy server is kept if needed. Before I read up on the Keychain API, I want to know how I'm offically supposed to get this information (instead of guessing & hacking it). How do I get the identifier for the correct keychain (It was "login" for me.) and the identifier(s) for the correct key within the keychain?

Replies

For folks reading along at home, my general advice is that you use high-level APIs for your networking (like NSURLSession) and those take care of proxy authentication automatically. CTMacUser is dealing with a specific problem where they can’t use such high-level APIs and thus they need to deal with proxies manually.

How do I get the identifier for the correct keychain (It was "login" for me.) …

macOS has the concept of keychain search list and you should rely on that. There’s no need to do customisation beyond that.

and the identifier(s) for the correct key within the keychain?

The easiest option here is to use

NSURLCredentialStorage
. If you craft the correct protection space then
-credentialsForProtectionSpace:
will return a value from the keychain.

If you want to look in the keychain directly you should take a look at the attributes lists in

<Security/SecItem.h>
. These items are stored as class
kSecClassInternetPassword
. If you compare the supported attributes for
kSecClassInternetPassword
and
kSecClassGenericPassword
, you’ll easily see which attributes are specific to Internet passwords. And from there it’s relatively straightforward to map from protection space properties to keychain attributes. For example, the
realm
property of
NSURLProtectionSpace
maps to the
kSecAttrSecurityDomain
keychain attribute.

IMPORTANT There’s no guarantee that you’ll be able to see the system’s proxy keychain items. Specifically, on iOS-based platforms that item is in the Apple ‘slice’ of the keychain, and is thus not accessible to your app (when you use

NSURLSession
it runs the authentication request via a system helper process that does have access to it). If you’re dealing with proxies directly you should always be prepared to ask the user to supply the password and store that password yourself.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I tried URLCredentialStorage by guessing what to put down as a protection space. And, of course, it didn't work. Now to directly walk the keychain....


If you look up the potential attributes, they'll already say if they're useful for kSecClassInternetPassword or not. My first try got no results. I looked at the entry in the Keychain Access app and copied the attributes to my playground code. Used the stored username, host, and port as search parameters. The keychain entry had a special protocol scheme ("gphx") and I put that under the search parameters too. Removing that fourth one fixed it; the protocol attribute is actually missing for that entry! (So how did Keychain Access know to put the "gphx" scheme there?) I better hope that I don't have two entries with the same username, host, and port.


Now there's another problem. Getting the password data triggers the keychain permission dialog box. I know that URLProtocol/URLProtocolClient provide a way to let server challenges through, but can it work with any GUI interruption, even from the user's own keychain? Any answer here won't matter due to another problem, the keychain attribute to turn off UI interaction (to return an error instead) doesn't work; setting it to either fail or skip is ignored and the dialog is always shown. {Sarcasm}Better hope the user always runs the URLProtocol subclass at least once where s/he can see the GUI to press "Always Allow."{/Sarcasm}

So how did Keychain Access know to put the "gphx" scheme there?

Well, that’s interesting.

gphx
follows the same logic as the other protocols by replacing the last character of the value with an
x
. So, if you print
kSecAttrProtocolRTSP
you’ll find that the actual value is
rtsp
and if you print
kSecAttrProtocolRTSPProxy
you’ll find that it’s value is
rtsx
. The interesting thing is that the Gopher values are not available in any header. It seems that knowledge of this protocol type is strictly confined to the Network preferences panel!

This means, alas, that

NSURLCredentialStorage
will definitely not work here. It has no mechanism to map
NSURLProtectionSpace
values to keychain attributes, and there is no mapping for the Gopher proxy.

Getting the password data triggers the keychain permission dialog box.

Right. This is because of the ACL on the keychain item. This is a variant of the iOS problem I discussed above. For proxies with built-in support a system daemon coordinates access to the keychain item and thus your app doesn’t need access.

… the keychain attribute to turn off UI interaction (to return an error instead) doesn't work …

Which attribute are were talking about here?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
… the keychain attribute to turn off UI interaction (to return an error instead) doesn't work …
Which attribute are were talking about here?

let keychainSearchParameters: NSDictionary = [..., kSecUseAuthenticationUI: kSecUseAuthenticationUIFail, ...]


It doesn't matter the setting, the UI is always triggered.

Getting the password data triggers the keychain permission dialog box.

Right. This is because of the ACL on the keychain item. This is a variant of the iOS problem I discussed above. For proxies with built-in support a system daemon coordinates access to the keychain item and thus your app doesn’t need access.

On the old mailing list, I read about someone that put downloading code like this in an XPC. Now I wonder if I did something similar, could that new XPC be "blessed" to not need GUI permission for the keychain ACL.

kSecUseAuthenticationUI

I can’t be 100% sure without spending a bunch more time digging into this but I suspect that this value is only relevant on macOS when you’re talking to the iOS-style keychain.

Note I realise that this stuff about keychain styles is confusing. You can read this post to learn more about the two keychain styles on macOS.

Given that these keychain items are in the macOS-style keychain, you can access them via the old school macOS API (

<Security/SecKeychain.h>
), at which point you can suppress interaction using
SecKeychainSetUserInteractionAllowed
.

Now I wonder if I did something similar, could that new XPC be "blessed" to not need GUI permission for the keychain ACL.

No. In the macOS-style keychain the ACL is set when the item is created and you have no control over that. You could change the ACL, but that always triggers a keychain authentication dialog.

You can dump this ACL with the

security
tool. For example:
$ security dump-keychain -a
…
keychain: "/Users/quinn/Library/Keychains/login.keychain-db"
version: 512
class: "inet"
attributes:
    0x00000007 <blob>="example.com"
    0x00000008 <blob>=<NULL>
    "acct"<blob>="mrgumby"
    "atyp"<blob>=<NULL>
    "cdat"<timedate>=0x32303137303931333039303832385A00  "20170913090828Z\000"
    "crtr"<uint32>=<NULL>
    "cusi"<sint32>=<NULL>
    "desc"<blob>=<NULL>
    "icmt"<blob>=<NULL>
    "invi"<sint32>=<NULL>
    "mdat"<timedate>=0x32303137303931333039303832385A00  "20170913090828Z\000"
    "nega"<sint32>=<NULL>
    "path"<blob>=<NULL>
    "port"<uint32>=0x00003039  "\000\00009"
    "prot"<blob>=<NULL>
    "ptcl"<uint32>="gphx"
    "scrp"<sint32>=<NULL>
    "sdmn"<blob>=<NULL>
    "srvr"<blob>="example.com"
    "type"<uint32>=<NULL>
access: 5 entries
    entry 0:
        authorizations (1): encrypt
        don't-require-password
        description: Proxies
        applications: <null>
    entry 1:
        authorizations (6): decrypt derive export_clear export_wrapped mac sign
        don't-require-password
        description: Proxies
        applications (2):
            0: /System/Library/PreferencePanes/Network.prefPane/Contents/XPCServices/com.apple.preference.network.remoteservice.xpc (OK)
            1: 0x67726F75703A2F2F4E6574776F726B5072656647726F757000  "group://NetworkPrefGroup\000"
    entry 2:
        authorizations (1): integrity
        don't-require-password
        description: 937b570466f1728c92538766ac5354ad2632e0045befaf0f43602929a59db785
        applications: <null>
    entry 3:
        authorizations (1): partition_id
        don't-require-password
        description: apple:
        applications: <null>
    entry 4:
        authorizations (1): change_acl
        don't-require-password
        description: Proxies
        applications (0):
…

So, the list of apps allowed to use the item is:

  • com.apple.preference.network.remoteservice.xpc
    , an XPC Service embedded within the Network preferences panel
  • Code in the

    NetworkPrefGroup
    , which you can’t join (it’s an Apple-only thing)

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"