Skip to content

Commit

Permalink
Move all LDAP related code to CryptoLib
Browse files Browse the repository at this point in the history
Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Jan 28, 2025
1 parent 16b4cd2 commit 92693b6
Show file tree
Hide file tree
Showing 17 changed files with 194 additions and 432 deletions.
28 changes: 24 additions & 4 deletions CryptoLib/CryptoLib.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
39852A5420AB2418004CB100 /* DdocParserDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 39852A4A20AB2418004CB100 /* DdocParserDelegate.h */; };
39E2B4D920AD7A3400CA74A8 /* Addressee.m in Sources */ = {isa = PBXBuildFile; fileRef = 39E2B4CF20AD7A3400CA74A8 /* Addressee.m */; };
39E2B4DF20AD7A3400CA74A8 /* Addressee.h in Headers */ = {isa = PBXBuildFile; fileRef = 39E2B4D520AD7A3400CA74A8 /* Addressee.h */; settings = {ATTRIBUTES = (Public, ); }; };
4E3681D82D40EAAD00D76DAB /* LDAPResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E3681D62D40EAAD00D76DAB /* LDAPResponse.swift */; };
4E3681D92D40EAAD00D76DAB /* OpenLdap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E3681D72D40EAAD00D76DAB /* OpenLdap.swift */; };
4E3681DB2D40EAE800D76DAB /* MoppLdapConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E3681DA2D40EAE800D76DAB /* MoppLdapConfiguration.swift */; };
4EAC69122D481D1C00A53079 /* ASN1Decoder in Frameworks */ = {isa = PBXBuildFile; productRef = 4EAC69112D481D1C00A53079 /* ASN1Decoder */; };
DFA40D2F2ADF635F003EF945 /* 3513523f.0 in Resources */ = {isa = PBXBuildFile; fileRef = DFA40D2E2ADF635F003EF945 /* 3513523f.0 */; };
DFC7CA452AE010C9009D85FF /* 9f4c149e.0 in Resources */ = {isa = PBXBuildFile; fileRef = DFC7CA442AE010C9009D85FF /* 9f4c149e.0 */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -71,7 +71,6 @@
39E2B4CF20AD7A3400CA74A8 /* Addressee.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Addressee.m; sourceTree = "<group>"; };
39E2B4D520AD7A3400CA74A8 /* Addressee.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Addressee.h; sourceTree = "<group>"; };
4E01B4A62AEFDD3B00941723 /* build-cdoc.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-cdoc.sh"; sourceTree = "<group>"; };
4E3681D62D40EAAD00D76DAB /* LDAPResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDAPResponse.swift; sourceTree = "<group>"; };
4E3681D72D40EAAD00D76DAB /* OpenLdap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenLdap.swift; sourceTree = "<group>"; };
4E3681DA2D40EAE800D76DAB /* MoppLdapConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoppLdapConfiguration.swift; sourceTree = "<group>"; };
DFA40D2E2ADF635F003EF945 /* 3513523f.0 */ = {isa = PBXFileReference; lastKnownFileType = text; path = 3513523f.0; sourceTree = "<group>"; };
Expand All @@ -83,6 +82,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4EAC69122D481D1C00A53079 /* ASN1Decoder in Frameworks */,
393B66E020DB94B4001DC89B /* cdoc.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -141,7 +141,6 @@
isa = PBXGroup;
children = (
4E3681D72D40EAAD00D76DAB /* OpenLdap.swift */,
4E3681D62D40EAAD00D76DAB /* LDAPResponse.swift */,
4E3681DA2D40EAE800D76DAB /* MoppLdapConfiguration.swift */,
);
name = Ldap;
Expand Down Expand Up @@ -253,6 +252,9 @@
en,
);
mainGroup = 39231FA020AB1C6C00E1E2B4;
packageReferences = (
4EAC68FF2D47839500A53079 /* XCRemoteSwiftPackageReference "ASN1Decoder" */,
);
productRefGroup = 39231FAB20AB1C6C00E1E2B4 /* Products */;
projectDirPath = "";
projectRoot = "";
Expand Down Expand Up @@ -290,7 +292,6 @@
39E2B4D920AD7A3400CA74A8 /* Addressee.m in Sources */,
39852A5320AB2418004CB100 /* Encrypt.mm in Sources */,
39266A5120CFBDF8002E3F23 /* SmartCardTokenWrapper.mm in Sources */,
4E3681D82D40EAAD00D76DAB /* LDAPResponse.swift in Sources */,
4E3681D92D40EAAD00D76DAB /* OpenLdap.swift in Sources */,
4E3681DB2D40EAE800D76DAB /* MoppLdapConfiguration.swift in Sources */,
);
Expand Down Expand Up @@ -542,6 +543,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */

/* Begin XCRemoteSwiftPackageReference section */
4EAC68FF2D47839500A53079 /* XCRemoteSwiftPackageReference "ASN1Decoder" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/filom/ASN1Decoder";
requirement = {
kind = exactVersion;
version = 1.9.0;
};
};
/* End XCRemoteSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
4EAC69112D481D1C00A53079 /* ASN1Decoder */ = {
isa = XCSwiftPackageProductDependency;
package = 4EAC68FF2D47839500A53079 /* XCRemoteSwiftPackageReference "ASN1Decoder" */;
productName = ASN1Decoder;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 39231FA120AB1C6C00E1E2B4 /* Project object */;
}
1 change: 1 addition & 0 deletions CryptoLib/CryptoLib/Addressee.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
@property (nonatomic, strong) NSString *identifier;
@property (nonatomic, strong) NSData *cert;
@property (nonatomic, strong) NSDate *validTo;
@property (nonatomic, strong) NSArray<NSString*> *policyIdentifiers;

@end
90 changes: 0 additions & 90 deletions CryptoLib/CryptoLib/Ldap/LDAPResponse.swift

This file was deleted.

170 changes: 149 additions & 21 deletions CryptoLib/CryptoLib/Ldap/OpenLdap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,61 @@

import Foundation
import LDAP

typealias LDAP = OpaquePointer
typealias LDAPMessage = OpaquePointer
import ASN1Decoder

public class OpenLdap: NSObject {
typealias LDAP = OpaquePointer
typealias LDAPMessage = OpaquePointer
typealias BerElement = OpaquePointer

enum KeyUsage: Int {
case digitalSignature = 0
case nonRepudiation = 1
case keyEncipherment = 2
case dataEncipherment = 3
case keyAgreement = 4
case keyCertSign = 5
case cRLSign = 6
case encipherOnly = 7
case decipherOnly = 8
}

private override init() {}

@objc static public func search(identityCode: String, configuration: MoppLdapConfiguration, withCertificate cert: String?) -> [LDAPResponse] {
if configuration.LDAPCERTS.isEmpty {
var result = search(identityCode: identityCode, url: configuration.LDAPPERSONURL, certificatePath: nil)
@objc static public func search(identityCode: String, configuration: MoppLdapConfiguration,
success: @escaping ([Addressee]) -> Void) {
DispatchQueue.global(qos: .default).async {
var filePath: String? = nil
if let libraryPath = FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first {
filePath = libraryPath.appendingPathComponent("LDAPCerts/ldapCerts.pem").path
if !FileManager.default.fileExists(atPath: filePath!) {
print("File ldapCerts.pem does not exist at directory path: \(filePath!)")
filePath = nil
}
}

if result.isEmpty {
result = search(identityCode: identityCode, url: configuration.LDAPCORPURL, certificatePath: nil)
var result = [Addressee]()
if configuration.LDAPCERTS.isEmpty {
var result = search(identityCode: identityCode, url: configuration.LDAPPERSONURL, certificatePath: nil)
if result.isEmpty {
result = search(identityCode: identityCode, url: configuration.LDAPCORPURL, certificatePath: nil)
}
}
else if isPersonalCode(identityCode) {
print("Searching with personal code from LDAP")
result = search(identityCode: identityCode, url: configuration.LDAPPERSONURL, certificatePath: filePath)
} else {
print("Searching with corporation keyword from LDAP")
result = search(identityCode: identityCode, url: configuration.LDAPCORPURL, certificatePath: filePath)
}
return result
}

if isPersonalCode(identityCode) {
print("Searching with personal code from LDAP")
return search(identityCode: identityCode, url: configuration.LDAPPERSONURL, certificatePath: cert)
} else {
print("Searching with corporation keyword from LDAP")
return search(identityCode: identityCode, url: configuration.LDAPCORPURL, certificatePath: cert)
DispatchQueue.main.async {
success(result)
}
}
}

static private func search(identityCode: String, url: String, certificatePath: String?) -> [LDAPResponse] {
static private func search(identityCode: String, url: String, certificatePath: String?) -> [Addressee] {
let secureLdap = url.lowercased().hasPrefix("ldaps")
if secureLdap {
if let certificatePath = certificatePath, !certificatePath.isEmpty {
Expand All @@ -66,7 +94,7 @@ public class OpenLdap: NSObject {
}

var ldap: LDAP?
let ldapReturnCode = ldap_initialize(&ldap, url.cString(using: .utf8))
let ldapReturnCode = ldap_initialize(&ldap, url)
defer {
if let ldap = ldap { ldap_unbind_ext_s(ldap, nil, nil) }
}
Expand All @@ -91,18 +119,32 @@ public class OpenLdap: NSObject {
}
var msg: LDAPMessage?
print("Searching from LDAP. Url: \(url) \(filter)")
ldap_search_ext_s(ldap, "c=EE", LDAP_SCOPE_SUBTREE, filter, nil, 0, nil, nil, nil, 0, &msg)
var attr = Array("userCertificate;binary".utf8CString)
_ = attr.withUnsafeMutableBufferPointer { attr in
var attrs = [attr.baseAddress, nil]
return attrs.withUnsafeMutableBufferPointer { attrs in
ldap_search_ext_s(ldap, "c=EE", LDAP_SCOPE_SUBTREE, filter, attrs.baseAddress, 0, nil, nil, nil, 0, &msg)
}
}

if let msg = msg {
defer { ldap_msgfree(msg) }
return LDAPResponse.from(ldap: ldap!, msg: msg)
var result = [Addressee]()
var message = ldap_first_message(ldap, msg)
while let currentMessage = message {
if ldap_msgtype(currentMessage) == LDAP_RES_SEARCH_ENTRY {
result.append(contentsOf: attributes(ldap: ldap!, msg: currentMessage))
}
message = ldap_next_message(ldap, currentMessage)
}
return result
}

return []
}

static private func setLdapOption(option: Int32, value: String) -> Bool {
let result = ldap_set_option(nil, option, value.cString(using: .utf8))
let result = ldap_set_option(nil, option, value)
if result != LDAP_SUCCESS {
print("ldap_set_option failed: \(String(describing: ldap_err2string(result)))")
return false
Expand All @@ -113,4 +155,90 @@ public class OpenLdap: NSObject {
static private func isPersonalCode(_ inputString: String) -> Bool {
return inputString.count == 11 && inputString.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
}

static private func attributes(ldap: LDAP, msg: LDAPMessage) -> [Addressee] {
var result = [Addressee]()
var ber: BerElement?
var attrPointer = ldap_first_attribute(ldap, msg, &ber)
while let attr = attrPointer {
defer { ldap_memfree(attr) }
result.append(contentsOf: values(ldap: ldap, msg: msg, tag: String(cString: attr)))
attrPointer = ldap_next_attribute(ldap, msg, ber)
}
if let ber = ber {
ber_free(ber, 0)
}

if let namePointer = ldap_get_dn(ldap, msg) {
print("Result (\(result.count)) \(String(cString: namePointer))")
ldap_memfree(namePointer)
}
return result
}

static private func values(ldap: LDAP, msg: LDAPMessage, tag: String) -> [Addressee] {
var result = [Addressee]()
guard let bvals = ldap_get_values_len(ldap, msg, tag) else {
return result
}
defer { ldap_value_free_len(bvals) }

var i = 0
while let bval = bvals[i] {
let data = Data(bytes: bval.pointee.bv_val, count: Int(bval.pointee.bv_len))
if let x509 = try? X509Certificate(der: data) {
var isIdCardType = false
var isDigiIdType = false
var isMobileID = false
var isESeal = false
var policyIdentifiers = [String]()
if let ext = x509.extensionObject(oid: OID.certificatePolicies) as? X509Certificate.CertificatePoliciesExtension {
for policy in ext.policies ?? [] {
policyIdentifiers.append(policy.oid)
switch policy.oid {
case let oid where oid.starts(with: "1.3.6.1.4.1.10015.1.1"),
let oid where oid.starts(with: "1.3.6.1.4.1.51361.1.1.1"):
isIdCardType = true
case let oid where oid.starts(with: "1.3.6.1.4.1.10015.1.2"),
let oid where oid.starts(with: "1.3.6.1.4.1.51361.1.1"),
let oid where oid.starts(with: "1.3.6.1.4.1.51455.1.1"):
isDigiIdType = true
case let oid where oid.starts(with: "1.3.6.1.4.1.10015.1.3"),
let oid where oid.starts(with: "1.3.6.1.4.1.10015.11.1"):
isMobileID = true
case let oid where oid.starts(with: "1.3.6.1.4.1.10015.7.3"),
let oid where oid.starts(with: "1.3.6.1.4.1.10015.7.1"),
let oid where oid.starts(with: "1.3.6.1.4.1.10015.2.1"):
isESeal = true
default:
break
}
}
}
let isUnknown = !isIdCardType && !isDigiIdType && !isMobileID && !isESeal

if (x509.keyUsage[KeyUsage.keyEncipherment.rawValue] || x509.keyUsage[KeyUsage.keyAgreement.rawValue]),
!x509.extendedKeyUsage.contains(OID.serverAuth.rawValue),
(!isESeal || !x509.extendedKeyUsage.contains(OID.clientAuth.rawValue)),
!isMobileID && !isUnknown {
let cn = x509.subject(oid: OID.commonName)?.joined(separator: ",") ?? ""
let split = cn.split(separator: ",").map { String($0) }
let addressee = Addressee()
if split.count == 3 {
addressee.surname = split[0]
addressee.givenName = split[1]
addressee.identifier = split[2]
} else {
addressee.identifier = cn
}
addressee.cert = data
addressee.validTo = x509.notAfter ?? Date()
addressee.policyIdentifiers = policyIdentifiers
result.append(addressee)
}
}
i += 1
}
return result
}
}
Loading

0 comments on commit 92693b6

Please sign in to comment.