|
| 1 | +diff --git a/swift-corelibs-foundation/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift b/swift-corelibs-foundation/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift |
| 2 | +index cdf8875fce..b9b50a2361 100644 |
| 3 | +--- a/swift-corelibs-foundation/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift |
| 4 | ++++ b/swift-corelibs-foundation/Sources/FoundationNetworking/URLSession/libcurl/EasyHandle.swift |
| 5 | +@@ -220,6 +220,66 @@ extension _EasyHandle { |
| 6 | + try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionCAINFO, caInfo).asError() |
| 7 | + } |
| 8 | + return |
| 9 | ++ } else { |
| 10 | ++ // When no certificate file has been specified, assemble all the certificate files |
| 11 | ++ // from the Android certificate store and writes them to a single `cacerts.pem` file |
| 12 | ++ |
| 13 | ++ // See https://github.com/apple/swift-nio-ssl/blob/main/Sources/NIOSSL/AndroidCABundle.swift |
| 14 | ++ let certsFolders = [ |
| 15 | ++ "/apex/com.android.conscrypt/cacerts", // >= Android14 |
| 16 | ++ "/system/etc/security/cacerts" // < Android14 |
| 17 | ++ ] |
| 18 | ++ |
| 19 | ++ let aggregateCertPath = NSTemporaryDirectory() + "/cacerts-\(UUID().uuidString).pem" |
| 20 | ++ |
| 21 | ++ if FileManager.default.createFile(atPath: aggregateCertPath, contents: nil) == false { |
| 22 | ++ return |
| 23 | ++ } |
| 24 | ++ |
| 25 | ++ guard let fs = FileHandle(forWritingAtPath: aggregateCertPath) else { |
| 26 | ++ return |
| 27 | ++ } |
| 28 | ++ |
| 29 | ++ // write a header |
| 30 | ++ fs.write(""" |
| 31 | ++ ## Bundle of CA Root Certificates |
| 32 | ++ ## Auto-generated on \(Date()) |
| 33 | ++ ## by aggregating certificates from: \(certsFolders) |
| 34 | ++ |
| 35 | ++ """.data(using: .utf8)!) |
| 36 | ++ |
| 37 | ++ // Go through each folder and load each certificate file (ending with ".0"), |
| 38 | ++ // and append them together into a single aggreagate file that curl can load. |
| 39 | ++ // The .0 files will contain some extra metadata, but libcurl only cares about the |
| 40 | ++ // -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- sections, |
| 41 | ++ // so we can naïvely concatenate them all and libcurl will understand the bundle. |
| 42 | ++ for certsFolder in certsFolders { |
| 43 | ++ let certsFolderURL = URL(fileURLWithPath: certsFolder) |
| 44 | ++ if (try? certsFolderURL.resourceValues(forKeys: [.isDirectoryKey]).isDirectory) != true { continue } |
| 45 | ++ let certURLs = try! FileManager.default.contentsOfDirectory(at: certsFolderURL, includingPropertiesForKeys: [.isRegularFileKey, .isReadableKey]) |
| 46 | ++ for certURL in certURLs { |
| 47 | ++ // certificate files have names like "53a1b57a.0" |
| 48 | ++ if certURL.pathExtension != "0" { continue } |
| 49 | ++ do { |
| 50 | ++ if try certURL.resourceValues(forKeys: [.isRegularFileKey]).isRegularFile != true { continue } |
| 51 | ++ if try certURL.resourceValues(forKeys: [.isReadableKey]).isReadable != true { continue } |
| 52 | ++ try fs.write(contentsOf: try Data(contentsOf: certURL)) |
| 53 | ++ } catch { |
| 54 | ++ // ignore individual errors and soldier on… |
| 55 | ++ continue |
| 56 | ++ } |
| 57 | ++ } |
| 58 | ++ } |
| 59 | ++ |
| 60 | ++ try! fs.close() |
| 61 | ++ |
| 62 | ++ aggregateCertPath.withCString { pathPtr in |
| 63 | ++ // note that it would be nice to use CFURLSessionOptionCAPATH instead |
| 64 | ++ // (see https://curl.se/libcurl/c/CURLOPT_CAPATH.html) |
| 65 | ++ // but it requires `c_rehash` to be run on the folder, which Android doesn't do |
| 66 | ++ try! CFURLSession_easy_setopt_ptr(rawHandle, CFURLSessionOptionCAINFO, UnsafeMutablePointer(mutating: pathPtr)).asError() |
| 67 | ++ } |
| 68 | ++ return |
| 69 | + } |
| 70 | + #endif |
| 71 | + |
0 commit comments