2 Replies
      Latest reply on Sep 17, 2018 1:03 AM by Ti-mm
      Ti-mm Level 1 Level 1 (0 points)

        I am working on an Networkextension using NEPacketTunnelProvider. I am using a configuration profile with com.apple.vpn.managed payload. Furthermore, I use ClientCertificate authentication with an com.apple.security.pkcs12 payload. According to NETunnelProviderManager documentation https://developer.apple.com/documentation/networkextension/netunnelprovidermanager#1661706  it should be possible for my extension to retrieve this identity using the com.apple.managed.vpn.shared keychain access group.

         

        If I query the Keychain for the identity, I always get error code `-25300`. According to https://www.osstatus.com/search/results?platform=all&framework=all&search=-25300 this means: "The item cannot be found."

         

        Code 1:

        I try to use the persistent reference provided by the protocolConfiguration to retrieve the identity.

         

         class PacketTunnelProvider: NEPacketTunnelProvider {
                
                override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
                    
                    NSLog("vpn-service startTunnel")
                    
                    let pc = self.protocolConfiguration
                    NSLog("vpn-service protocolConfiguration " + pc.debugDescription)
                    if let identity = pc.identityReference {
                        let persistentRef = identity as NSData
                        NSLog("vpn-service persistentRef "  + persistentRef.description)
                        var copyResult: AnyObject?
                        let copyErr = SecItemCopyMatching([
                            kSecValuePersistentRef as String: persistentRef,
                            kSecReturnData as String: true
                            ] as CFDictionary, ©Result)
                        NSLog("vpn-service getCert copyErr "  + copyErr.description)
                    }
                }
            }
           

         

        Output is:

         

        Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] : vpn-service startTunnel
            Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] : vpn-service protocolConfiguration
                type = plugin
                identifier = 3B39941E-AF39-45CE-B869-68AF392FBCA0
                serverAddress = DEFAULT
                password = {
                    domain = user
                    accessGroup = com.apple.managed.vpn.shared
                }
                identity = {
                    identifier = 
                    persistentReference = <69646e74 00000000 00000011>
                    domain = user
                }
                identityDataImported = NO
                identityReference = <69646e74 00000000 00000011>
                proxySettings = {
                    autoProxyDiscovery = NO
                    autoProxyConfigurationEnabled = NO
                    HTTPEnabled = NO
                    HTTPSEnabled = NO
                    FTPEnabled = NO
                    SOCKSEnabled = NO
                    RTSPEnabled = NO
                    gopherEnabled = NO
                    excludeSimpleHostnames = NO
                            usePassiveFTP = YES
                }
                disconnectOnSleep = NO
                disconnectOnIdle = NO
                disconnectOnIdleTimeout = 0
                disconnectOnWake = NO
                disconnectOnWakeTimeout = 0
                pluginType = 
                authenticationMethod = 1
            Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] : vpn-service persistentRef <69646e74 00000000 00000011>
            Jul 27 10:07:53 Tims-iPhone vpn(libswiftFoundation.dylib)[4994] : vpn-service getCert copyErr -25300

         

        Code 2:

        If I try to retrieve all identitys without the reference, I get `-25300` too.

         

         let getQuery: [String: Any] = [
                kSecClass as String: kSecClassIdentity,
                kSecMatchLimit as String: kSecMatchLimitAll,
                kSecReturnAttributes as String: true,
            ]
          
            var item: CFTypeRef?
            let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
            NSLog("vpn-service status: " + status.description)
        
        

         

        I rechecked that the build result got the keychain access group:

         

        codesign -d --ent :- build/Debug-iphoneos/agent.app/PlugIns/vpn.appex/

         

        Result:

         

         Executable=/Users/timb/projects/xcode/ios-client/build/Debug-iphoneos/agent.app/PlugIns/vpn.appex/vpn
            
            
            
            
            application-identifier
            
            com.apple.developer.networking.networkextension
            
            com.apple.developer.team-identifier
            
            get-task-allow
            
            keychain-access-groups
            
            .com.apple.managed.vpn.shared
            
            
            

         

        How to get an identity for NEPacketTunnelProvider Networkextension from com.apple.managed.vpn.shared keychain access group?

         

        Edit: According to https://forums.developer.apple.com/thread/67613 #9 I need a special entitlement to get com.apple.managed.vpn.shared keychain access group. I created a TSI to get this entitlement.

        • Re: How to get an identity for NEPacketTunnelProvider Networkextension from com.apple.managed.vpn.shared keychain access group?
          eskimo Apple Staff Apple Staff (10,295 points)

          According to https://forums.developer.apple.com/thread/67613 #9 I need a special entitlement to get com.apple.managed.vpn.sharedkeychain access group.

          That’s correct.  You won’t make any progress with this until you have access to this special entitlement.

          Share and Enjoy

          Quinn “The Eskimo!”
          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
          let myEmail = "eskimo" + "1" + "@apple.com"

            • Re: How to get an identity for NEPacketTunnelProvider Networkextension from com.apple.managed.vpn.shared keychain access group?
              Ti-mm Level 1 Level 1 (0 points)

              I got it working now, I will post some example code, I hope it will be useful for somebody

               

              1: When you have the special entitlement, make sure your container app and network extension have the entitlement set. You can check with `codesign -d --entitlements :- <filename>`

               

              It should look like this:

              <key>keychain-access-groups</key>
              <array>
                   <string>com.apple.managed.vpn.shared</string>
              </array>
              

               

              I am using XCode 10 Beta and there is a problem when you use the GUI. It sets the value like

              <string>$(AppIdentifierPrefix)com.apple.managed.vpn.shared</string>

              You have to fix it with a texteditor.

               

              2: Access the data from the keychain. I will post some example code to use the data as base64 encoded strings.

               

               

              guard let identityReference = protocolConfiguration.identityReference else {
                  throw ConfigError.errorIdentityNotFound
              }
              let query: [String : Any] = [
                  kSecValuePersistentRef as String: identityReference as CFData,
                  kSecReturnRef as String: true,
              ]
                    
              var copyResult: AnyObject?
              SecItemCopyMatching(query as CFDictionary, ©Result)
                    
              guard let existingCopyResult = copyResult else {
                  throw ConfigError.errorIdentityNotFound
              }
              let identity = existingCopyResult as! SecIdentity
                    
              var certificate: SecCertificate!
              SecIdentityCopyCertificate(identity, &certificate)
                    
              let DERCertificate = SecCertificateCopyData(certificate) as NSData
              let DERCertificateStringBase64Encoded = DERCertificate.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
                    
              let PEMCertificateString = "-----BEGIN CERTIFICATE-----\n\(DERCertificateStringBase64Encoded)\n-----END CERTIFICATE-----"
                    
              var privateKey: SecKey!
              SecIdentityCopyPrivateKey(identity, &privateKey)
              let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, nil) as! NSData
              let privateKeyDataBase64Encoded = privateKeyData.base64EncodedString(options: NSData.Base64EncodingOptions.lineLength64Characters)
              let privateKeyPKCS1String = "-----BEGIN RSA PRIVATE KEY-----\n\(privateKeyDataBase64Encoded)\n-----END RSA PRIVATE KEY-----"