Thank you very much for your assistance Quinn. In a non-privileged plugin, I was able to read the certificate from my Yubikey in both system.login.console and authenticate rights with this code:
CFTypeRef watcherRef = nil;
OSStatus status = [mPluginRef engineCallback]->GetTKTokenWatcher(mEngineRef, &watcherRef);
if (status) {
os_log(OS_LOG_DEFAULT, "Error fetching token watcher: %{public}@\n", SecCopyErrorMessageString(status, nil));
return;
}
TKTokenWatcher *watcher = (TKTokenWatcher *)watcherRef;
[watcher setInsertionHandler:^(NSString * _Nonnull tokenID) {
if (![tokenID containsString:@"pivtoken"])
return;
os_log(OS_LOG_DEFAULT, "Inserted token: %{public}@\n", tokenID);
CFTypeRef ctx = nil;
OSStatus status = [mPluginRef engineCallback]->GetLAContext(mEngineRef, &ctx);
if (status) {
os_log(OS_LOG_DEFAULT, "Error fetching context: %{public}@\n", SecCopyErrorMessageString(status, nil));
return;
}
CFArrayRef identities = nil;
status = [mPluginRef engineCallback]->GetTokenIdentities(mEngineRef, ctx, &identities);
if (status) {
os_log(OS_LOG_DEFAULT, "Error fetching identities: %{public}@\n", SecCopyErrorMessageString(status, nil));
return;
}
for (CFIndex idx = 0; idx < CFArrayGetCount(identities); idx++) {
CFTypeRef identityArray = CFArrayGetValueAtIndex(identities, idx);
if (CFGetTypeID(identityArray) != CFArrayGetTypeID() || !CFArrayGetCount(identityArray))
continue;
CFTypeRef identityRef = CFArrayGetValueAtIndex(identityArray, 0);
if (CFGetTypeID(identityRef) != SecIdentityGetTypeID())
continue;
SecIdentityRef identity = (SecIdentityRef)identityRef;
SecKeyRef privateKey = nil;
status = SecIdentityCopyPrivateKey(identity, &privateKey);
if (status) {
os_log(OS_LOG_DEFAULT, "Error fetcing private key: %{public}@\n", SecCopyErrorMessageString(status, nil));
continue;
}
NSDictionary *attrs = (__bridge NSDictionary *)SecKeyCopyAttributes(privateKey);
id tkid = [attrs objectForKey:@"tkid"];
if (!tkid || ![tkid isKindOfClass:[NSString class]] || ![tokenID isEqualToString:(NSString *)tkid])
continue;
SecCertificateRef certificate = nil;
status = SecIdentityCopyCertificate(identity, &certificate);
if (status) {
os_log(OS_LOG_DEFAULT, "Error fetching certificate: %{public}@\n", SecCopyErrorMessageString(status, nil));
continue;
}
os_log(OS_LOG_DEFAULT, "Fetched certificate: %{public}@\n", certificate);
}
}];
Here is the output:
Inserted token: com.apple.pivtoken:<redacted>
Fetched certificate: <cert(0x8c4ea1e00) s: Yubico Authentication i: Yubico Authentication>
Curiously, this also seems to work when I create watcher with [TKTokenWatcher new]. I assume it is still preferable to use GetTKTokenWatcher.
In a privileged plugin, this code fails to fetch identities with errAuthorizationDenied, but I think I can live with that.
Topic:
Privacy & Security
SubTopic:
General
Tags: