|
| 1 | +import Foundation |
| 2 | +import GSS |
| 3 | +import TableProMSSQLCore |
| 4 | + |
| 5 | +struct MSSQLKerberosCache { |
| 6 | + let name: String |
| 7 | + let filePath: String |
| 8 | + |
| 9 | + func destroy() { |
| 10 | + try? FileManager.default.removeItem(atPath: filePath) |
| 11 | + } |
| 12 | +} |
| 13 | + |
| 14 | +enum MSSQLKerberosCredentials { |
| 15 | + static func acquireTicket(principal: String, password: String) throws -> MSSQLKerberosCache { |
| 16 | + let fileName = "tablepro-krb5-\(UUID().uuidString)" |
| 17 | + let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileName) |
| 18 | + let cacheName = "FILE:\(filePath)" |
| 19 | + |
| 20 | + let importedName = try importName(principal) |
| 21 | + defer { |
| 22 | + var releaseMinor: OM_uint32 = 0 |
| 23 | + var releasable: gss_name_t? = importedName |
| 24 | + _ = gss_release_name(&releaseMinor, &releasable) |
| 25 | + } |
| 26 | + |
| 27 | + let attributes = NSMutableDictionary() |
| 28 | + attributes[kGSSICPassword] = password |
| 29 | + attributes[kGSSICKerberosCacheName] = cacheName |
| 30 | + |
| 31 | + var cred: gss_cred_id_t? |
| 32 | + var errorRef: Unmanaged<CFError>? |
| 33 | + let status = gss_aapl_initial_cred( |
| 34 | + importedName, |
| 35 | + &__gss_krb5_mechanism_oid_desc, |
| 36 | + attributes as CFDictionary, |
| 37 | + &cred, |
| 38 | + &errorRef |
| 39 | + ) |
| 40 | + |
| 41 | + guard status == 0 else { |
| 42 | + let message = errorRef?.takeRetainedValue().localizedDescription |
| 43 | + ?? String(localized: "Kerberos ticket request failed") |
| 44 | + try? FileManager.default.removeItem(atPath: filePath) |
| 45 | + throw MSSQLCoreError.kerberosAuthFailed( |
| 46 | + kind: MSSQLKerberosClassifier.classify(message) ?? .wrongPassword, |
| 47 | + serverMessage: message |
| 48 | + ) |
| 49 | + } |
| 50 | + |
| 51 | + if cred != nil { |
| 52 | + var releaseMinor: OM_uint32 = 0 |
| 53 | + _ = gss_release_cred(&releaseMinor, &cred) |
| 54 | + } |
| 55 | + |
| 56 | + return MSSQLKerberosCache(name: cacheName, filePath: filePath) |
| 57 | + } |
| 58 | + |
| 59 | + private static func importName(_ principal: String) throws -> gss_name_t { |
| 60 | + var minor: OM_uint32 = 0 |
| 61 | + var name: gss_name_t? |
| 62 | + let status = principal.withCString { cString -> OM_uint32 in |
| 63 | + var buffer = gss_buffer_desc( |
| 64 | + length: strlen(cString), |
| 65 | + value: UnsafeMutableRawPointer(mutating: cString) |
| 66 | + ) |
| 67 | + return gss_import_name(&minor, &buffer, &__gss_c_nt_user_name_oid_desc, &name) |
| 68 | + } |
| 69 | + guard status == 0, let name else { |
| 70 | + throw MSSQLCoreError.kerberosAuthFailed( |
| 71 | + kind: .principalUnknown, |
| 72 | + serverMessage: String( |
| 73 | + format: String(localized: "Invalid Kerberos principal: %@"), |
| 74 | + principal |
| 75 | + ) |
| 76 | + ) |
| 77 | + } |
| 78 | + return name |
| 79 | + } |
| 80 | +} |
0 commit comments