Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CZiti.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,7 @@
OTHER_SWIFT_FLAGS = "";
SWIFT_INCLUDE_PATHS = "$(SRCROOT)/lib/ProjectModule $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/tlsuv-src/include $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/tlsuv-src/deps/uv_link_t/include $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/ziti-sdk-c-src/includes $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/vcpkg_installed/$(VCPKG_ARCH)-$(VCPKG_OS)/include $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/include $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/lib/ziti-tunnel/include $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/lib/ziti-tunnel-cbs/include $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/lib/ziti-tunnel/lwip $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/lwip-src/src/include $(PROJECT_DIR)/deps/ziti-tunnel-sdk-c/build-$(PLATFORM_NAME)-$(CURRENT_ARCH)/_deps/lwip-contrib-src/ports/unix/port/include";
SWIFT_OBJC_BRIDGING_HEADER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
Expand Down
47 changes: 33 additions & 14 deletions lib/Ziti.swift
Original file line number Diff line number Diff line change
Expand Up @@ -329,11 +329,12 @@ import CZitiPrivate
return
}

// Store certificate
let cert = dropFirst("pem:", resp.id.cert)
// Store certificates
let certs = dropFirst("pem:", resp.id.cert)
_ = zkc.deleteCertificate(silent: true)
let (err, cns) = zkc.storeCertificates(cert)
guard err == nil else {
// storeCertificate only stores the first (leaf) certificate in the pem. that's ok - the full chain of certs is stored in the .zid
// file. only the leaf/pubkey needs to be in the keychain.
guard zkc.storeCertificate(fromPem: certs) == nil else {
let errStr = "Unable to store certificate\n"
log.error(errStr, function:"enroll()")
enrollCallback(nil, ZitiError(errStr))
Expand All @@ -346,8 +347,8 @@ import CZitiPrivate
ca = dropFirst("pem:", idCa)
}

let zid = ZitiIdentity(id: subj, ztAPIs: resp.ztAPIs, certCNs: cns, ca: ca)
log.info("Enrolled id:\(subj) with controller: \(zid.ztAPI) with cns: \(zid.getCertCNs())", function:"enroll()")
let zid = ZitiIdentity(id: subj, ztAPIs: resp.ztAPIs, certs: certs, ca: ca)
log.info("Enrolled id:\(subj) with controller: \(zid.ztAPI)", function:"enroll()")

enrollCallback(zid, nil)
}
Expand Down Expand Up @@ -384,14 +385,12 @@ import CZitiPrivate
@objc public func run(_ postureChecks:ZitiPostureChecks?, _ initCallback: @escaping InitCallback) {
// Get certificate
let zkc = ZitiKeychain(tag: id.id)
let (maybeCerts, zErr) = zkc.getCertificates(id.getCertCNs())
guard let certs = maybeCerts, zErr == nil else {
let errStr = zErr != nil ? zErr!.localizedDescription : "unable to retrieve certificates from keychain"
guard let certPEM = id.getCertificates(zkc) else {
let errStr = "unable to retrieve certificates"
log.error(errStr)
initCallback(zErr ?? ZitiError(errStr))
initCallback(ZitiError(errStr))
return
}
let certPEM = zkc.convertToPEM("CERTIFICATE", ders: certs)

// Get private key
guard let privKey = zkc.getPrivateKey() else {
Expand All @@ -418,6 +417,7 @@ import CZitiPrivate
model_list_append(&ctrls, c.cstring)
}

// ziti_context_init copies strings (strdup) for its own use, so it's ok to use references to swift strings here.
var zitiCfg = ziti_config(
controller_url: id.ztAPI.cstring,
controllers: ctrls,
Expand Down Expand Up @@ -445,7 +445,7 @@ import CZitiPrivate
pq_domain_cb: postureChecks?.domainQuery != nil ? Ziti.onDomainQuery : nil,
app_ctx: self.toVoidPtr(),
events: ZitiContextEvent.rawValue | ZitiRouterEvent.rawValue | ZitiServiceEvent.rawValue | ZitiAuthEvent.rawValue | ZitiConfigEvent.rawValue,
event_cb: Ziti.onEvent, cert_extension_window: 0)
event_cb: Ziti.onEvent, cert_extension_window: 30)

zitiStatus = ziti_context_set_options(self.ztx, &zitiOpts)
guard zitiStatus == Ziti.ZITI_OK else {
Expand Down Expand Up @@ -901,8 +901,20 @@ import CZitiPrivate

// update ourself
if event.type == ZitiEvent.EventType.ConfigEvent {
mySelf.id.ztAPI = event.configEvent!.controllerUrl
mySelf.id.ca = event.configEvent!.caBundle
let cfgEvent = event.configEvent!
if !cfgEvent.controllerUrl.isEmpty { mySelf.id.ztAPI = cfgEvent.controllerUrl }
if !cfgEvent.controllers.isEmpty { mySelf.id.ztAPIs = cfgEvent.controllers }
if !cfgEvent.cert.isEmpty {
mySelf.id.certs = cfgEvent.cert
let zkc = ZitiKeychain(tag: mySelf.id.id)
_ = zkc.deleteCertificate()
// store the first/leaf certificate in the keychain so it can be used in a key pair.
let zErr = zkc.storeCertificate(fromPem: event.configEvent!.cert)
if zErr != nil {
log.warn("failed to store certificate: \(zErr!.localizedDescription)", function:"onEvent()")
}
}
if !cfgEvent.caBundle.isEmpty { mySelf.id.ca = cfgEvent.caBundle }
}

mySelf.eventCallbacksLock.lock()
Expand Down Expand Up @@ -1125,7 +1137,14 @@ func scan<
}

extension String {
// use only when scope of c string matches scope of swift string.
var cstring: UnsafePointer<CChar> {
(self as NSString).cString(using: String.Encoding.utf8.rawValue)!
}
// use when c string needs to outlive swift string. caller must deallocate() the returned buffer when no longer needed.
var allocatedcString: UnsafeMutablePointer<CChar> {
let buf = UnsafeMutablePointer<CChar>.allocate(capacity: self.count + 1)
buf.initialize(from: self, count: self.count + 1)
return buf
}
}
11 changes: 9 additions & 2 deletions lib/ZitiEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ import CZitiPrivate
@objc public let controllerUrl:String
@objc public let controllers:[String]
@objc public let cfgSource:String

@objc public let caBundle:String // todo encapsulate ziti_id_cfg_s?
@objc public let cert:String
@objc public let caBundle:String

init( _ cEvent:ziti_config_event) {
var str = ""
Expand All @@ -295,6 +295,12 @@ import CZitiPrivate
caStr = String(cString: cStr)
}
caBundle = caStr

var certStr = ""
if let cStr = cEvent.config.pointee.id.cert {
certStr = String(cString: cStr)
}
cert = certStr

var ctrlsArray:[String] = []
var ctrlList = cEvent.config.pointee.controllers
Expand Down Expand Up @@ -383,6 +389,7 @@ import CZitiPrivate
str += " controllers: \(e.controllers))\n"
str += " cfgSource: \(e.cfgSource)\n"
str += " caBundle: \(e.caBundle)\n"
str += " cert: \(e.cert)\n"
}
return str
}
Expand Down
41 changes: 25 additions & 16 deletions lib/ZitiIdentity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ import Foundation
/// Initially the `sub` field from the one-time enrollment JWT. Used by `Ziti` to store and retrieve identity-related items in the Keychain
@objc public let id:String

/// Certificate CNs
///
/// Common names for the certificates in this identities certificate chain. The first element will always match `id`.
/// Used by `Ziti` to retrieve certificates from the Keychain.
@objc public var certCNs:[String]?

/// Common names for the certificates in this identities certificate chain.
/// This is only set when the identity is decoded from an older representation. When `certs` is not set, this value can be used to
/// retrieve the identity's certificates from the Keychain.
@available(*, deprecated, message: "store certificates in `certs` instead")
@objc private var certCNs:[String]?
/// scheme, host, and port used to communicate with Ziti controller
@objc public var ztAPI:String

Expand All @@ -51,6 +51,9 @@ import Foundation
/// Note that this name is unknown until a session with Ziti is active
@objc public var name:String?

/// Certificates (PEM)
@objc public var certs:String?

/// CA pool verified as part of enrollment that can be used to establish trust with of the Ziti controller
@objc public var ca:String?

Expand All @@ -65,23 +68,29 @@ import Foundation
/// - certCNs: common names of certififcates
/// - name: name currently configured for this identity
/// - ca: CA pool that can be used to verify trust of the Ziti controller
@objc public init(id:String, ztAPIs:[String], certCNs:[String]?=nil, name:String?=nil, ca:String?=nil) {
@objc public init(id:String, ztAPIs:[String], name:String?=nil, certs:String?=nil, ca:String?=nil) {
self.id = id
self.certCNs = [ id ]
if let certCNs {
for cn in certCNs {
if cn == id { continue }
self.certCNs?.append(cn)
}
}
self.ztAPI = ztAPIs.first ?? ""
self.ztAPIs = ztAPIs
self.name = name
self.certs = certs
self.ca = ca
}

@objc public func getCertCNs() -> [String] {
return certCNs ?? [self.id]
@objc public func getCertificates(_ zkc:ZitiKeychain?) -> String? {
if let certs = self.certs {
log.debug("returning certificates from identity")
return certs
}
// get certs using certCNs and the keychain if available.
let certCNs = self.certCNs ?? [self.id]
if let ders = zkc?.getCertificates(certCNs) {
log.debug("returning certfificates from keychain CNs=\(certCNs)")
self.certs = zkc?.convertToPEM("CERTIFICATE", ders: ders)
self.certCNs = nil
return self.certs
}
return nil
}

/// Save this object to a JSON file
Expand Down
18 changes: 9 additions & 9 deletions lib/ZitiKeychain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ public class ZitiKeychain : NSObject {
return sceStatus
}
#endif
@available(*, deprecated, message: "This function only stores the first certificate in `pem`. Use storeCertificates(pem:String) to store all certificates.")

/// This function only stores the first certificate in `pem`. use storeCertificates(pem:String) if you need to store all certificates in the keychain.
func storeCertificate(fromPem pem:String) -> ZitiError? {
let (_, zErr) = storeCertificate(fromDer: convertToDER(pem))
if zErr != nil {
Expand All @@ -267,7 +267,6 @@ public class ZitiKeychain : NSObject {
return zErr
}

@available(*, deprecated)
func storeCertificate(fromDer der:Data) -> (SecCertificate?, ZitiError?) {
guard let certificate = SecCertificateCreateWithData(nil, der as CFData) else {
let errStr = "Unable to create certificate from data for \(tag)"
Expand Down Expand Up @@ -316,17 +315,17 @@ public class ZitiKeychain : NSObject {
return (certData, nil)
}

func getCertificates(_ certCNs:[String]) -> ([Data]?, ZitiError?) {
func getCertificates(_ certCNs:[String]) -> [Data]? {
var certs:[Data] = []
for cn in certCNs {
let (cert, err) = getCertificate(cn)
guard let cert = cert, err == nil else {
return (nil, err)
return nil
}
certs.append(cert)
}

return (certs, nil)
return certs
}

func deleteCertificate(silent:Bool=false) -> ZitiError? {
Expand Down Expand Up @@ -362,12 +361,13 @@ public class ZitiKeychain : NSObject {
return nil
}

func convertToPEM(_ type:String, ders:[Data]) -> String {
func convertToPEM(_ type:String, ders:[Data]) -> String? {
var pem = ""
ders.forEach { der in
pem.append(convertToPEM("CERTIFICATE", der: der))
pem.append(convertToPEM(type, der: der))
}
return pem
if pem.count > 0 { return pem }
return nil
}

func convertToPEM(_ type:String, der:Data) -> String {
Expand Down
10 changes: 10 additions & 0 deletions lib/ZitiTunnel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,16 @@ public class ZitiTunnel : NSObject, ZitiUnretained {
if !event.caBundle.isEmpty {
ziti.id.ca = event.caBundle
}
if !event.certPEM.isEmpty {
ziti.id.certs = event.certPEM
// store the first/leaf certificate in the keychain so it can be used in a key pair.
let zkc = ZitiKeychain(tag: ziti.id.id)
_ = zkc.deleteCertificate()
let zErr = zkc.storeCertificate(fromPem: event.certPEM)
if zErr != nil {
log.warn("failed to store certificates: \(zErr!.localizedDescription)", function:"onEventCallback()")
}
}
// pass event to application
mySelf.tunnelProvider?.tunnelEventCallback(event)
default:
Expand Down
7 changes: 6 additions & 1 deletion lib/ZitiTunnelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ import CZitiPrivate
/// CA bundle
public var caBundle:String = ""

/// Certificte PEM (possibly multiple certificates)
public var certPEM:String = ""

init(_ ziti:Ziti, _ evt:UnsafePointer<config_event>) {
super.init(ziti)
var ziti_cfg_ptr:UnsafeMutablePointer<ziti_config>?
Expand All @@ -239,6 +242,7 @@ import CZitiPrivate
}
}
self.caBundle = toStr(ziti_cfg_ptr?.pointee.id.ca)
self.certPEM = toStr(ziti_cfg_ptr?.pointee.id.cert)
}

/// Debug description
Expand All @@ -247,6 +251,7 @@ import CZitiPrivate
return super.debugDescription + "\n" +
" controller_url: \(controllerUrl)\n" +
" contrlollers: \(controllers)\n" +
" caBundle: \(caBundle)"
" caBundle: \(caBundle)\n" +
" cert: \(certPEM)"
}
}
Loading