Skip to content

Commit

Permalink
[Feature Request] Local User Behavior #174
Browse files Browse the repository at this point in the history
  • Loading branch information
twocanoes committed Feb 24, 2024
1 parent fb8ca59 commit 009d1bf
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 113 deletions.
1 change: 0 additions & 1 deletion DefaultsOverride.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ public class DefaultsOverride: UserDefaults {
}
}
override public func string(forKey defaultName: String) -> String? {
TCSLogWithMark()

if let defaultName = cachedPrefs[defaultName] as? String{
return defaultName
Expand Down
3 changes: 2 additions & 1 deletion KerbUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ extern OSStatus SecKeychainItemSetAccessWithPassword(SecKeychainItemRef item, Se
@interface KerbUtil : NSObject
@property (nonatomic, assign, readonly) BOOL finished; // observable

- (void)getKerberosCredentials:(NSString *)password :(NSString *)userPrincipal completion:(void(^)(NSString *))callback;
- (NSDictionary *)getKerbCredentialWithPassword:password userPrincipal:(NSString *)userPrincipal;
- (void)getKerberosCredentials:(NSString *)password :(NSString *)userPrincipal completion:(void(^)(NSDictionary *))callback;
- (NSString *)getKerbCredentials:(NSString *)password :(NSString *)userPrincipal;
- (void)changeKerberosPassword:(NSString *)oldPassword :(NSString *)newPassword :(NSString *)userPrincipal completion:(void(^)(NSString *))callback;
- (NSString *)changeKerbPassword:(NSString *)oldPassword :(NSString *)newPassword :(NSString *)userPrincipal;
Expand Down
58 changes: 23 additions & 35 deletions KerbUtil.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

@interface KerbUtil ()

@property (nonatomic, assign, readwrite) BOOL finished;
//@property (nonatomic, assign, readwrite) BOOL finished;

@end

Expand All @@ -30,38 +30,18 @@ @implementation KerbUtil

extern OSStatus SecKeychainResetLogin(UInt32 passwordLength, const void* password, Boolean resetSearchList);

- (void)getKerberosCredentials:(NSString *)password :(NSString *)userPrincipal completion:(void(^)(NSString *))callback {
OM_uint32 maj_stat;
gss_name_t gname = GSS_C_NO_NAME;
gss_cred_id_t cred = NULL;
CFErrorRef error = NULL;

// preflight for spaces in the userPrincipal
gname = GSSCreateName((__bridge CFTypeRef _Nonnull)(userPrincipal), GSS_C_NT_USER_NAME, NULL);
if (gname == NULL) {
callback(@"error: failed to create GSS name");
return;
}
- (void)getKerberosCredentials:(NSString *)password :(NSString *)userPrincipal completion:(void(^)(NSDictionary *))callback {

NSDictionary *attrs = @{ (id)kGSSICPassword : password };
maj_stat = gss_aapl_initial_cred(gname, GSS_KRB5_MECHANISM, (__bridge CFDictionaryRef)attrs, &cred, &error);
NSDictionary *dict = [self getKerbCredentialWithPassword:password userPrincipal:userPrincipal];

CFRelease(gname);
if (maj_stat) {
NSLog(@"error: %d %@", (int)maj_stat, error);
NSDictionary *errorDict = CFBridgingRelease(CFErrorCopyUserInfo(error));
NSString *errorMessage = [errorDict valueForKey:(@"NSDescription")];
callback(errorMessage);
if (!dict) {
callback(nil);
return;
}

CFRelease(cred);
callback(nil);
callback(dict);
}

- (NSString *)getKerbCredentials:(NSString *)password :(NSString *)userPrincipal {

self.finished = NO;
- (NSDictionary *)getKerbCredentialWithPassword:password userPrincipal:(NSString *)userPrincipal {

OM_uint32 maj_stat;
gss_name_t gname = GSS_C_NO_NAME;
Expand All @@ -72,7 +52,9 @@ - (NSString *)getKerbCredentials:(NSString *)password :(NSString *)userPrincipal

gname = GSSCreateName((__bridge CFTypeRef _Nonnull)(userPrincipal), GSS_C_NT_USER_NAME, NULL);
if (gname == NULL)
return @"error: creating GSS name";
{
return nil;
}

NSDictionary *attrs = @{
(id)kGSSICPassword : password
Expand All @@ -85,15 +67,23 @@ - (NSString *)getKerbCredentials:(NSString *)password :(NSString *)userPrincipal
if (maj_stat) {
NSLog(@"error: %d %@", (int)maj_stat, error);
NSDictionary *errorDict = CFBridgingRelease(CFErrorCopyUserInfo(error)) ;
self.finished = YES;
return [ errorDict valueForKey:(@"NSDescription")];
return errorDict;
}

CFRelease(cred);
self.finished = YES;

return nil ;
}

- (NSString *)getKerbCredentials:(NSString *)password :(NSString *)userPrincipal {

NSDictionary *errorDict = [self getKerbCredentialWithPassword:password userPrincipal:userPrincipal];
if (!errorDict) {return nil;}

return [ errorDict valueForKey:@"NSDescription"];

}

- (void)changeKerberosPassword:(NSString *)oldPassword :(NSString *)newPassword :(NSString *)userPrincipal completion:(void(^)(NSString *))callback {
OM_uint32 maj_stat;
gss_name_t gname = GSS_C_NO_NAME;
Expand Down Expand Up @@ -122,8 +112,6 @@ - (void)changeKerberosPassword:(NSString *)oldPassword :(NSString *)newPassword

- (NSString *)changeKerbPassword:(NSString *)oldPassword :(NSString *)newPassword :(NSString *)userPrincipal {

self.finished = NO;

OM_uint32 maj_stat ;
gss_name_t gname = GSS_C_NO_NAME;
CFErrorRef error = NULL;
Expand All @@ -145,11 +133,11 @@ - (NSString *)changeKerbPassword:(NSString *)oldPassword :(NSString *)newPasswor
if (maj_stat) {
NSLog(@"error: %d %@", (int)maj_stat, error);
NSDictionary *errorDict = CFBridgingRelease(CFErrorCopyUserInfo(error)) ;
self.finished = YES;

return [ errorDict valueForKey:(@"NSDescription")];
}
// CFRelease(error);
self.finished = YES;

return nil;
}

Expand Down
134 changes: 83 additions & 51 deletions NoMADSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public enum NoMADSessionError: String, Error {
case AuthenticationFailure
case KerbError
case PasswordExpired = "Password has expired"
case unknownPrincipal
case UnknownPrincipal
case wrongRealm = "Wrong realm"
}

Expand All @@ -55,6 +55,23 @@ public enum LDAPType {
case OD
}

public enum GSSErrorKey : String {

case mechanismKey = "kGSSMechanism"
case mechanismOIDKey = "kGSSMechanismOID"
case majorErrorCodeKey = "kGSSMajorErrorCode"
case minorErrorCodeKey = "kGSSMinorErrorCode"
case descriptionKey = "NSDescription"

}
public struct GSSError {
var mechanism:String
var mechanismOID:String
var majorErrorCode:Int
var minorErrorCode:UInt
var description:String

}
public struct NoMADLDAPServer {
var host: String
var status: String
Expand Down Expand Up @@ -1062,11 +1079,14 @@ extension NoMADSession: NoMADUserSession {
return
}

KerbUtil().getKerberosCredentials(userPass, userPrincipal) { [unowned self] errorValue in
KerbUtil().getKerberosCredentials(userPass, userPrincipal) { errorDict in
self.userPass = ""
if let errorValue = errorValue {
if let errorDict = errorDict {
self.state = .kerbError
let sessionError: NoMADSessionError

let errorValue = errorDict["NSDescription"] as? String ?? "Unknown error"

switch errorValue {
case NoMADSessionError.PasswordExpired.rawValue:
sessionError = .PasswordExpired
Expand Down Expand Up @@ -1147,40 +1167,53 @@ extension NoMADSession: NoMADUserSession {
public func authenticate(authTestOnly: Bool = false) {
// authenticate
let kerbUtil = KerbUtil()
let kerbError = kerbUtil.getKerbCredentials(userPass, userPrincipal)
// let kerbError = kerbUtil.getKerbCredentials(userPass, userPrincipal)

//TODO: Make this not a war crime - Josh
// wait for auth to finish
while !kerbUtil.finished {
RunLoop.current.run(mode: RunLoop.Mode.default, before: Date.distantFuture)
}
kerbUtil.getKerberosCredentials(userPass, userPrincipal) { errorDict in
// scrub the password field
self.userPass = ""

// scrub the password field
userPass = ""

if let kerbError = kerbError {
// error
state = .kerbError
//TODO: Change to actual throws and error handling
switch kerbError {
case "Password has expired" :
delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.PasswordExpired, description: kerbError)
break
case "Wrong realm" :
delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.wrongRealm, description: kerbError)
break
case _ where kerbError.range(of: "unable to reach any KDC in realm") != nil :
delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.OffDomain, description: kerbError)
break
default:
delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.KerbError, description: kerbError)
}
} else {
if authTestOnly {
klistUtil.kdestroy(princ: userPrincipal)
if let errorDict = errorDict as? Dictionary<String,Any>,
let description = errorDict[GSSErrorKey.descriptionKey.rawValue] as? String,
let majorErrorCode = errorDict[GSSErrorKey.majorErrorCodeKey.rawValue] as? Int,
let minorErrorCode = errorDict[GSSErrorKey.minorErrorCodeKey.rawValue] as? NSNumber,
let mechanism = errorDict[GSSErrorKey.mechanismKey.rawValue] as? String,
let mechanismOID = errorDict[GSSErrorKey.mechanismOIDKey.rawValue] as? String

{
let error = GSSError(mechanism: mechanism, mechanismOID: mechanismOID, majorErrorCode: majorErrorCode, minorErrorCode: UInt(UInt32(truncating:minorErrorCode)), description: description)

// error
self.state = .kerbError

switch error.description {
case "Password has expired" :
self.delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.PasswordExpired, description: error.description)
break
case "Wrong realm" :
self.delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.wrongRealm, description: error.description)
break
case _ where error.description.range(of: "unable to reach any KDC in realm") != nil :
self.delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.OffDomain, description: error.description)
break
default:
//user not found
if error.majorErrorCode == 0x0D0000, error.minorErrorCode == 0x96C73A06, mechanismOID == "1 2 840 113554 1 2 2" {
self.delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.UnknownPrincipal, description: error.description)

return
}
//other error
self.delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.KerbError, description: error.description)
}
} else {
if authTestOnly {
klistUtil.kdestroy(princ: self.userPrincipal)
}
self.delegate?.NoMADAuthenticationSucceded()
}
delegate?.NoMADAuthenticationSucceded()
}

}

/// Change the password for the current user session via closure
Expand Down Expand Up @@ -1208,28 +1241,27 @@ extension NoMADSession: NoMADUserSession {
let kerbUtil = KerbUtil()
TCSLogWithMark("Change password.")

let error = kerbUtil.changeKerbPassword(oldPass, newPass, userPrincipal)
kerbUtil.changeKerberosPassword(oldPass, newPass, userPrincipal) { errorString in
// let error = kerbUtil.changeKerbPassword(oldPass, newPass, userPrincipal)

while !kerbUtil.finished {
RunLoop.current.run(mode: RunLoop.Mode.default, before: Date.distantFuture)
}
if let errorString = errorString {
// error
self.state = .kerbError
self.delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.KerbError, description: errorString)
} else {
// If the password change worked then we are online. Reauthenticate with new password.
self.userPass = self.newPass
self.authenticate(authTestOnly: false)
}

if let error = error {
// error
state = .kerbError
delegate?.NoMADAuthenticationFailed(error: NoMADSessionError.KerbError, description: error)
} else {
// If the password change worked then we are online. Reauthenticate with new password.
userPass = newPass
authenticate(authTestOnly: false)
}
// scrub the passwords
self.oldPass = ""
self.newPass = ""

// scrub the passwords
oldPass = ""
newPass = ""
// clean the kerb prefs so we don't reuse the KDCs
self.cleanKerbPrefs()
}

// clean the kerb prefs so we don't reuse the KDCs
cleanKerbPrefs()
}

public func userInfo() {
Expand Down
6 changes: 3 additions & 3 deletions NomadLogin/LocalCheckAndMigrate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Foundation
import OpenDirectory

enum MigrationType {
case errorSkipMigration // unable to complete migration
case errorSkipMigration(String) // unable to complete migration
case fullMigration // perform full migration
case skipMigration // no need to migrate
case syncPassword // local password needs to be synced with local
Expand Down Expand Up @@ -94,8 +94,8 @@ class LocalCheckAndMigrate : NSObject, DSQueryable {
return .fullMigration

} catch {
TCSLogWithMark("Unknown migration check error. skipping migration.")
return .errorSkipMigration
TCSLogWithMark("Unknown migration check error. skipping migration:\(error.localizedDescription)")
return .errorSkipMigration(error.localizedDescription)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Profile Manifest/com.twocanoes.xcreds.plist
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
<key>pfm_app_url</key>
<string>https://github.com/twocanoes/xcreds</string>
<key>pfm_description</key>
<string>XCreds 4.1 (6363) OAuth Settings</string>
<string>XCreds 4.1 (6364) OAuth Settings</string>
<key>pfm_documentation_url</key>
<string>https://twocanoes.com/knowledge-base/xcreds-admin-guide/#preferences</string>
<key>pfm_domain</key>
<string>com.twocanoes.xcreds</string>
<key>pfm_format_version</key>
<integer>1</integer>
<key>pfm_last_modified</key>
<date>2024-02-23T17:21:19Z</date>
<date>2024-02-23T17:44:54Z</date>
<key>pfm_platforms</key>
<array>
<string>macOS</string>
Expand Down
6 changes: 5 additions & 1 deletion XCreds/MainController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ class MainController: NSObject, UpdateCredentialsFeedbackProtocol {

else if (DefaultsOverride.standardOverride.bool(forKey: PrefKeys.shouldUseROPGForMenuLogin.rawValue) == true || DefaultsOverride.standardOverride.value(forKey: PrefKeys.aDDomain.rawValue) != nil )
{
windowController.webViewController.webView.isHidden=true
if let webView = windowController.webViewController?.webView {
webView.isHidden=true
}

if let window = windowController.window{
let bundle = Bundle.findBundleWithName(name: "XCreds")
Expand Down Expand Up @@ -366,6 +368,8 @@ class MainController: NSObject, UpdateCredentialsFeedbackProtocol {
case .OffDomain:
TCSLogWithMark("Off domain so not prompting")

case .UnknownPrincipal:
TCSLogWithMark("UnknownPrincipal so not prompting")

default:
if WifiManager().isConnectedToNetwork()==true {
Expand Down
Loading

0 comments on commit 009d1bf

Please sign in to comment.