Browse Source

Merge pull request #1790 from nextcloud/serverTrust

Server trust
Marino Faggiana 3 years ago
parent
commit
7972c89b5f

+ 2 - 2
Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -113,9 +113,9 @@
         "package": "NCCommunication",
         "repositoryURL": "https://github.com/nextcloud/ios-communication-library/",
         "state": {
-          "branch": "develop",
+          "branch": null,
           "revision": "c8e3eac61a846775d570b1d252612a8d2d02930d",
-          "version": null
+          "version": "0.99.3"
         }
       },
       {

+ 40 - 51
iOSClient/AppDelegate.swift

@@ -300,10 +300,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         if account == "" { return }
 
         NCCommunicationCommon.shared.writeLog("initialize Main")
-        
-        // Clear error certificate
-        NCNetworking.shared.certificatesError = nil
-        
+                
         // Registeration push notification
         NCPushNotification.shared().pushNotification()
         
@@ -543,62 +540,55 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
     }
     
-    func viewCertificateDetailsDismiss() {
-        self.startTimerErrorNetworking()
-    }
-    
     @objc func startTimerErrorNetworking() {
         timerErrorNetworking = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(checkErrorNetworking), userInfo: nil, repeats: true)
     }
     
     @objc private func checkErrorNetworking() {
-                
-        if account == "" { return }
-        guard let currentHost = URL(string: self.urlBase)?.host else { return }
-                
+        
         // check unauthorized server (401/403)
-        if CCUtility.getPassword(account)!.count == 0 {
+        if account != "" && CCUtility.getPassword(account)!.count == 0 {
             openLogin(viewController: window?.rootViewController, selector: NCGlobal.shared.introLogin, openLoginWeb: true)
         }
+    }
+    
+    func trustCertificateError(host: String) {
+
+        guard let currentHost = URL(string: self.urlBase)?.host,
+              let pushNotificationServerProxyHost = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host,
+              host != pushNotificationServerProxyHost,
+              host == currentHost
+        else { return }
+
+        let certificateHostSavedPath = CCUtility.getDirectoryCerificates()! + "/" + host + ".der"
+        var title = NSLocalizedString("_ssl_certificate_changed_", comment: "")
         
-        // check certificate untrusted (-1202)
-        if NCNetworking.shared.certificatesError == currentHost {
-            
-            let certificateHostSavedPath = CCUtility.getDirectoryCerificates()! + "/" + currentHost + ".der"
-            var title = NSLocalizedString("_ssl_certificate_changed_", comment: "")
-            
-            if !FileManager.default.fileExists(atPath: certificateHostSavedPath) {
-                title = NSLocalizedString("_connect_server_anyway_", comment: "")
-            }
-            
-            let alertController = UIAlertController(title: title, message: NSLocalizedString("_server_is_trusted_", comment: ""), preferredStyle: .alert)
-            
-            alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { action in
-                
-                NCNetworking.shared.writeCertificate(host: currentHost)
-                NCNetworking.shared.certificatesError = nil
-                self.startTimerErrorNetworking()
-            }))
-            
-            alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { action in
-                
-                NCNetworking.shared.certificatesError = nil
-                self.startTimerErrorNetworking()
-            }))
-            
-            alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { action in
-                if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController {
-                    let viewController = navigationController.topViewController as! NCViewCertificateDetails
-                    viewController.delegate = self
-                    viewController.host = currentHost
-                    self.window?.rootViewController?.present(navigationController, animated: true)
-                }
-            }))
-            
-            window?.rootViewController?.present(alertController, animated: true, completion: {
-                self.timerErrorNetworking?.invalidate()
-            })
+        if !FileManager.default.fileExists(atPath: certificateHostSavedPath) {
+            title = NSLocalizedString("_connect_server_anyway_", comment: "")
         }
+        
+        let alertController = UIAlertController(title: title, message: NSLocalizedString("_server_is_trusted_", comment: ""), preferredStyle: .alert)
+        
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { action in
+            NCNetworking.shared.writeCertificate(host: host)
+        }))
+        
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { action in }))
+        
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { action in
+            if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController {
+                let viewController = navigationController.topViewController as! NCViewCertificateDetails
+                viewController.delegate = self
+                viewController.host = host
+                self.window?.rootViewController?.present(navigationController, animated: true)
+            }
+        }))
+        
+        window?.rootViewController?.present(alertController, animated: true)
+    }
+    
+    func viewCertificateDetailsDismiss(host: String) {
+        trustCertificateError(host: host)
     }
     
     // MARK: - Account
@@ -633,7 +623,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
         NCManageDatabase.shared.clearDatabase(account: account, removeAccount: true)
         
-        NCNetworking.shared.certificatesError = nil
         CCUtility.clearAllKeysEnd(toEnd: account)
         CCUtility.clearAllKeysPushNotification(account)
         CCUtility.setPassword(account, password: nil)

+ 1 - 4
iOSClient/Login/NCLogin.swift

@@ -344,10 +344,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
             if NCManageDatabase.shared.getAccounts() == nil {
                 NCUtility.shared.removeAllSettings()
             }
-               
-            // Clear certificate error
-            NCNetworking.shared.certificatesError = nil
-            
+                           
             NCManageDatabase.shared.deleteAccount(account)
             NCManageDatabase.shared.addAccount(account, urlBase: url, user: user, password: password)
             

+ 0 - 3
iOSClient/Login/NCLoginWeb.swift

@@ -270,9 +270,6 @@ extension NCLoginWeb: WKNavigationDelegate {
             NCUtility.shared.removeAllSettings()
         }
         
-        // Clear certificate error
-        NCNetworking.shared.certificatesError = nil
-
         // Add new account
         NCManageDatabase.shared.deleteAccount(account)
         NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: username, password: password)

+ 58 - 65
iOSClient/Networking/NCNetworking.swift

@@ -48,8 +48,6 @@ import Queuer
     var uploadRequest: [String: UploadRequest] = [:]
     var uploadMetadataInBackground: [String: tableMetadata] = [:]
     
-    var certificatesError: String?
-
     @objc public let sessionMaximumConnectionsPerHost = 5
     @objc public let sessionIdentifierBackground: String = "com.nextcloud.session.upload.background"
     @objc public let sessionIdentifierBackgroundWWan: String = "com.nextcloud.session.upload.backgroundWWan"
@@ -163,39 +161,49 @@ import Queuer
         let protectionSpace: URLProtectionSpace = challenge.protectionSpace
         let directoryCertificate = CCUtility.getDirectoryCerificates()!
         let host = challenge.protectionSpace.host
-        let pushNotificationServerProxyHost = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host
-            
+        let certificateSavedPath = directoryCertificate + "/" + host + ".der"
+        var isTrusted: Bool
+
+        #if !EXTENSION
+        defer {
+            DispatchQueue.main.async {
+                if !isTrusted {
+                    (UIApplication.shared.delegate as? AppDelegate)?.trustCertificateError(host: host)
+                }
+            }
+        }
+        #endif
+        
         print("SSL host: \(host)")
         
-        if let serverTrust: SecTrust = protectionSpace.serverTrust {
-            
-            saveX509Certificate(serverTrust, host: host, directoryCertificate: directoryCertificate)
+        if let serverTrust: SecTrust = protectionSpace.serverTrust, let certificate = SecTrustGetCertificateAtIndex(serverTrust, 0)  {
             
+            // extarct certificate txt
+            saveX509Certificate(certificate, host: host, directoryCertificate: directoryCertificate)
+           
             var secresult = SecTrustResultType.invalid
             let status = SecTrustEvaluate(serverTrust, &secresult)
-            if errSecSuccess == status, let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) {
-                    
-                let serverCertificateData = SecCertificateCopyData(serverCertificate)
-                let data = CFDataGetBytePtr(serverCertificateData);
-                let size = CFDataGetLength(serverCertificateData);
-                let certificate = NSData(bytes: data, length: size)
-                
-                // write certificate tmp to disk
-                certificate.write(toFile: directoryCertificate + "/" + host + ".tmp", atomically: true)
+            let isServerTrusted = SecTrustEvaluateWithError(serverTrust, nil)
+            
+            let certificateCopyData = SecCertificateCopyData(certificate)
+            let data = CFDataGetBytePtr(certificateCopyData);
+            let size = CFDataGetLength(certificateCopyData);
+            let certificateData = NSData(bytes: data, length: size)
                 
-                // verify
-                let certificateSavedPath = directoryCertificate + "/" + host + ".der"
-                if let certificateSaved = NSData(contentsOfFile: certificateSavedPath), certificate.isEqual(to: certificateSaved as Data) {
-                    return true
-                }
+            certificateData.write(toFile: directoryCertificate + "/" + host + ".tmp", atomically: true)
+            
+            if isServerTrusted {
+                isTrusted = true
+            } else if status == errSecSuccess, let certificateDataSaved = NSData(contentsOfFile: certificateSavedPath), certificateData.isEqual(to: certificateDataSaved as Data) {
+                isTrusted = true
+            } else {
+                isTrusted = false
             }
+        } else {
+            isTrusted = false
         }
         
-        if host != pushNotificationServerProxyHost {
-            NCNetworking.shared.certificatesError = host
-        }
-        
-        return false
+        return isTrusted
     }
     
     func writeCertificate(host: String) {
@@ -209,50 +217,35 @@ import Queuer
         }
     }
     
-    private func saveX509Certificate(_ serverTrust: SecTrust, host: String, directoryCertificate: String) {
+    private func saveX509Certificate(_ certificate: SecCertificate, host: String, directoryCertificate: String) {
         
-        if let currentServerCert = SecTrustGetCertificateAtIndex(serverTrust, 0) {
-            
-            let certNamePathTXT = directoryCertificate + "/" + host + ".txt"
-            let data: CFData = SecCertificateCopyData(currentServerCert)
-            let mem = BIO_new_mem_buf(CFDataGetBytePtr(data), Int32(CFDataGetLength(data)))
-            let x509cert = d2i_X509_bio(mem, nil)
+        let certNamePathTXT = directoryCertificate + "/" + host + ".txt"
+        let data: CFData = SecCertificateCopyData(certificate)
+        let mem = BIO_new_mem_buf(CFDataGetBytePtr(data), Int32(CFDataGetLength(data)))
+        let x509cert = d2i_X509_bio(mem, nil)
 
-            if x509cert == nil {
-                print("[LOG] OpenSSL couldn't parse X509 Certificate")
-            } else {
-                
-                // save certificate
-//                if FileManager.default.fileExists(atPath: certNamePath) {
-//                    do {
-//                        try FileManager.default.removeItem(atPath: certNamePath)
-//                    } catch { }
-//                }
-//                let fileCert = fopen(certNamePath, "w")
-//                if fileCert != nil {
-//                    PEM_write_X509(fileCert, x509cert)
-//                }
-//                fclose(fileCert)
-                
-                // save details
-                if FileManager.default.fileExists(atPath: certNamePathTXT) {
-                    do {
-                        try FileManager.default.removeItem(atPath: certNamePathTXT)
-                    } catch { }
-                }
-                let fileCertInfo = fopen(certNamePathTXT, "w")
-                if fileCertInfo != nil {
-                    let output = BIO_new_fp(fileCertInfo, BIO_NOCLOSE)
-                    X509_print_ex(output, x509cert, UInt(XN_FLAG_COMPAT), UInt(X509_FLAG_COMPAT))
-                    BIO_free(output)
-                }
-                fclose(fileCertInfo)
+        if x509cert == nil {
+            print("[LOG] OpenSSL couldn't parse X509 Certificate")
+        } else {
 
-                X509_free(x509cert)
+            // save details
+            if FileManager.default.fileExists(atPath: certNamePathTXT) {
+                do {
+                    try FileManager.default.removeItem(atPath: certNamePathTXT)
+                } catch { }
             }
-                
-            BIO_free(mem)
+            let fileCertInfo = fopen(certNamePathTXT, "w")
+            if fileCertInfo != nil {
+                let output = BIO_new_fp(fileCertInfo, BIO_NOCLOSE)
+                X509_print_ex(output, x509cert, UInt(XN_FLAG_COMPAT), UInt(X509_FLAG_COMPAT))
+                BIO_free(output)
+            }
+            fclose(fileCertInfo)
+
+            X509_free(x509cert)
         }
+
+        BIO_free(mem)
     }
     
     func checkPushNotificationServerProxyCertificateUntrusted(viewController: UIViewController?, completion: @escaping (_ errorCode: Int)->()) {

+ 1 - 8
iOSClient/Networking/NCNetworkingE2EE.swift

@@ -324,14 +324,7 @@ import Alamofire
                                 NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: metadata.account, errorCode: errorCode, errorDescription: errorDescription)
                                 #endif
                                 NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError)
-                            
-                            } else if errorCode == Int(CFNetworkErrors.cfurlErrorServerCertificateUntrusted.rawValue) {
-                            
-                                if let host = URL(string: metadata.urlBase)?.host {
-                                    NCNetworking.shared.certificatesError = host
-                                }
-                                NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError)
-                                                    
+                                
                             } else {
                             
                                 NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError)

+ 3 - 3
iOSClient/Security/NCViewCertificateDetails.swift

@@ -24,12 +24,12 @@
 import UIKit
 
 public protocol NCViewCertificateDetailsDelegate {
-    func viewCertificateDetailsDismiss()
+    func viewCertificateDetailsDismiss(host: String)
 }
 
 // optional func
 public extension NCViewCertificateDetailsDelegate {
-    func viewCertificateDetailsDismiss() {}
+    func viewCertificateDetailsDismiss(host: String) {}
 }
 
 class NCViewCertificateDetails: UIViewController  {
@@ -87,7 +87,7 @@ class NCViewCertificateDetails: UIViewController  {
     override func viewWillDisappear(_ animated: Bool) {
         super.viewWillDisappear(animated)
         
-        self.delegate?.viewCertificateDetailsDismiss()
+        self.delegate?.viewCertificateDetailsDismiss(host: host)
     }
     
     // MARK: ACTION