Skip to content

Commit

Permalink
added keychain updating with tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
twocanoes committed Jul 7, 2022
1 parent f417788 commit 2e3114e
Show file tree
Hide file tree
Showing 18 changed files with 374 additions and 260 deletions.
49 changes: 49 additions & 0 deletions DataExtension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// DataExtension.swift
// NoMADLoginAD
//
// Created by Josh Wisenbaker on 4/10/18.
// Copyright © 2018 Orchard & Grove. All rights reserved.
//

import Foundation

extension Data {

init?(fromHexEncodedString string: String) {

// Convert 0 ... 9, a ... f, A ...F to their decimal value,
// return nil for all other input characters
func decodeNibble(u: UInt16) -> UInt8? {
switch(u) {
case 0x30 ... 0x39:
return UInt8(u - 0x30)
case 0x41 ... 0x46:
return UInt8(u - 0x41 + 10)
case 0x61 ... 0x66:
return UInt8(u - 0x61 + 10)
default:
return nil
}
}

self.init(capacity: string.utf16.count/2)
var even = true
var byte: UInt8 = 0
for c in string.utf16 {
guard let val = decodeNibble(u: c) else { return nil }
if even {
byte = val << 4
} else {
byte += val
self.append(byte)
}
even = !even
}
guard even else { return nil }
}

func hexEncodedString() -> String {
return map { String(format: "%02hhx", $0) }.joined()
}
}
77 changes: 76 additions & 1 deletion XCreds/KeychainUtil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class KeychainUtil {

func setPassword(_ name: String, pass: String) -> OSStatus {

myErr = SecKeychainAddGenericPassword(nil, UInt32(serviceName.count), serviceName, UInt32(name.count), name, UInt32(pass.count), pass, nil)
myErr = SecKeychainAddGenericPassword(nil, UInt32(serviceName.count), serviceName, UInt32(name.count), name, UInt32(pass.count), pass, &myKeychainItem)

return myErr
}
Expand Down Expand Up @@ -101,6 +101,81 @@ class KeychainUtil {
return false
}
}
func updateACL(password:String){
var myACLs : CFArray? = nil
var itemAccess: SecAccess? = nil

guard let keychainItem = myKeychainItem else {
TCSLogWithMark("Keychain item not found")
return
}
var err = SecKeychainItemCopyAccess(keychainItem, &itemAccess)

guard let itemAccess = itemAccess else {
TCSLogWithMark("item access invalid")
return
}

SecAccessCopyACLList(itemAccess, &myACLs)

var appList: CFArray? = nil
var desc: CFString? = nil

var prompt = SecKeychainPromptSelector()
var secApps = [ SecTrustedApplication ]()

var trust : SecTrustedApplication? = nil
if FileManager.default.fileExists(atPath: "/Applications/XCreds.app", isDirectory: nil) {
err = SecTrustedApplicationCreateFromPath("/Applications/XCreds.app", &trust)
if err == 0 {
secApps.append(trust!)
}
}
for acl in myACLs as! Array<SecACL> {
SecACLCopyContents(acl, &appList, &desc, &prompt)
let authArray = SecACLCopyAuthorizations(acl)

if (authArray as! [String]).contains("ACLAuthorizationDecrypt") {

TCSLogWithMark("Found AUTHORIZATION_CHANGE_ACL.")

SecACLSetContents(acl, secApps as CFArray, "" as CFString, prompt)
continue
}

if !(authArray as! [String]).contains("ACLAuthorizationPartitionID") {
continue
}

TCSLogWithMark("Found ACLAuthorizationPartitionID.")

// pull in the description that's really a functional plist <sigh>
let rawData = Data.init(fromHexEncodedString: desc! as String)
var format: PropertyListSerialization.PropertyListFormat = .xml

var propertyListObject = [ String: [String]]()

do {
propertyListObject = try PropertyListSerialization.propertyList(from: rawData!, options: [], format: &format) as! [ String: [String]]
} catch {
TCSLogWithMark("No teamid in ACLAuthorizationPartitionID.")
}
let teamIds = [ "teamid:UXP6YEHSPW" ]

propertyListObject["Partitions"] = teamIds

// now serialize it back into a plist

let xmlObject = try? PropertyListSerialization.data(fromPropertyList: propertyListObject as Any, format: format, options: 0)

// now that all ACLs has been adjusted, we can update the item

err = SecACLSetContents(acl, appList, xmlObject!.hexEncodedString() as CFString, prompt)

// smack it again to set the ACL
err = SecKeychainItemSetAccessWithPassword(keychainItem, itemAccess, UInt32(password.count), password)
}
}
//
// // return the last expiration date for any certs that match the domain and user
//
Expand Down
2 changes: 1 addition & 1 deletion XCreds/PasswordUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class PasswordUtils: NSObject {
let castError = error as NSError
switch castError.code {
case Int(kODErrorCredentialsInvalid.rawValue):
TCSLogWithMark("Tested password for user account: %{public}@ is not valid.")
TCSLogWithMark("Tested password for user account: \(userName) is not valid.")
return false
default:
throw error
Expand Down
44 changes: 39 additions & 5 deletions XCreds/TokenManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,38 +27,72 @@ class TokenManager {
let defaults = UserDefaults.standard
var timer: Timer?

func saveTokensToKeychain(tokens:Tokens) -> Bool {
func saveTokensToKeychain(tokens:Tokens, setACL:Bool=false, password:String?=nil) -> Bool {
let keychainUtil = KeychainUtil()

if tokens.accessToken.count>0{
TCSLogWithMark("Saving Access Token")
if keychainUtil.updatePassword(PrefKeys.accessToken.rawValue, pass: tokens.accessToken) == false {
TCSLogWithMark("Error Updating Access Token")

return false
}
}
if setACL==true, let password = password {
TCSLogWithMark("Updating ACL")
keychainUtil.updateACL(password:password)
}
}

if tokens.idToken.count>0{
TCSLogWithMark("Saving idToken Token")
if keychainUtil.updatePassword(PrefKeys.idToken.rawValue, pass: tokens.idToken) == false {
TCSLogWithMark("Error Updating idToken Token")

return false
}
if setACL==true, let password = password {
TCSLogWithMark("Updating ACL")

keychainUtil.updateACL(password:password)
}
}


if tokens.refreshToken.count>0 {
TCSLogWithMark("Saving refresh Token")

if keychainUtil.updatePassword(PrefKeys.refreshToken.rawValue, pass: tokens.refreshToken) == false {
TCSLogWithMark("Error Updating refreshToken Token")

return false
}
if setACL==true, let password = password {
TCSLogWithMark("Updating ACL")



keychainUtil.updateACL(password:password)
}
}

let cloudPassword = tokens.password

if cloudPassword.count>0 {
if keychainUtil.updatePassword(PrefKeys.password.rawValue, pass: tokens.refreshToken) == false {
TCSLogWithMark("Saving cloud password")

if keychainUtil.updatePassword(PrefKeys.password.rawValue, pass: tokens.password) == false {
TCSLogWithMark("Error Updating password")

return false
}
if setACL==true, let password = password {
TCSLogWithMark("Updating ACL")

keychainUtil.updateACL(password:password)
}

}
return true
}

func getNewAccessToken(completion:@escaping (_ isSuccessful:Bool,_ hadConnectionError:Bool)->Void) -> Void {

guard let url = URL(string: defaults.string(forKey: PrefKeys.tokenEndpoint.rawValue) ?? "") else {
Expand Down
39 changes: 28 additions & 11 deletions XCredsLoginPlugIn/LoginWindow/LoginWebViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ class LoginWebViewController: WebViewController {
//if we have tokens, that means that authentication was successful.
//we have to check the password here so we can prompt.


guard let delegate = delegate else {
TCSLogWithMark("invalid delegate")
return
}

let isLocal = try? PasswordUtils.isUserLocal("tperfitt")

guard let isLocal = isLocal else {
Expand All @@ -62,19 +66,20 @@ class LoginWebViewController: WebViewController {

if isLocal == false {
TCSLogWithMark("User is not on system. for now, just abort")
delegate?.denyLogin()

delegate.denyLogin()
return
}
let isValidPassword = try? PasswordUtils.isLocalPasswordValid(userName: "tperfitt", userPass: tokens.password)

if isValidPassword==false{
TCSLogWithMark("local password is different from cloud password. ")
TCSLogWithMark("local password is different from cloud password. Prompting for local password.")

let passwordWindowController = LoginPasswordWindowController.init(windowNibName: NSNib.Name("LoginPasswordWindowController"))

if passwordWindowController.window==nil {
TCSLogWithMark("no window!")
TCSLogWithMark("no passwordWindowController window")
delegate.denyLogin()
return
}
passwordWindowController.window?.canBecomeVisibleWithoutLogin=true
passwordWindowController.window?.isMovable = false
Expand All @@ -101,8 +106,22 @@ TCSLogWithMark("no window!")
let isValidPassword = try? PasswordUtils.isLocalPasswordValid(userName: "tperfitt", userPass: localPassword)

if isValidPassword==true {
TCSLogWithMark("setting password to migrate later")
delegate?.setHint(type: .migratePass, hint: localPassword)
let localUser = try? PasswordUtils.getLocalRecord("tperfitt")
guard let localUser = localUser else {
TCSLogWithMark("invalid local user")
delegate.denyLogin()
return
}
do {
try localUser.changePassword(localPassword, toPassword: tokens.password)
}
catch {
TCSLogWithMark("Error setting local password to cloud password")
delegate.denyLogin()
return
}
TCSLogWithMark("setting original password to use to unlock keychain later")
delegate.setHint(type: .migratePass, hint: localPassword)
passwordWindowController.window?.close()
break

Expand All @@ -114,18 +133,16 @@ TCSLogWithMark("no window!")

}
TCSLogWithMark("updating username, password, and tokens")
guard let delegate = delegate else {
return
}
delegate.setContextString(type: kAuthorizationEnvironmentUsername, value: "tperfitt")
delegate.setContextString(type: kAuthorizationEnvironmentPassword, value: tokens.password)

delegate.setHint(type: .tokens, hint: [tokens.idToken,tokens.refreshToken,tokens.accessToken])

delegate.allowLogin()
RunLoop.main.perform {
self.loginTransition()
}
delegate.allowLogin()


}
}
Expand Down
6 changes: 3 additions & 3 deletions XCredsLoginPlugIn/LoginWindow/LoginWindowControls.xib
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<rect key="frame" x="0.0" y="0.0" width="872" height="109"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sxl-0U-ne5">
<customView translatesAutoresizingMaskIntoConstraints="NO" id="sxl-0U-ne5">
<rect key="frame" x="252" y="20" width="369" height="78"/>
<subviews>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6f9-0z-Xqg">
Expand Down Expand Up @@ -62,7 +62,7 @@
</button>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="341" id="aKl-rA-eDh"/>
<constraint firstAttribute="width" constant="369" id="aKl-rA-eDh"/>
<constraint firstAttribute="height" constant="78" id="cmM-6M-FrE"/>
</constraints>
</customView>
Expand All @@ -72,7 +72,7 @@
<constraint firstItem="sxl-0U-ne5" firstAttribute="centerX" secondItem="Tpj-ss-EMv" secondAttribute="centerX" id="eIH-vl-2ht"/>
</constraints>
</view>
<point key="canvasLocation" x="-602" y="703.5"/>
<point key="canvasLocation" x="-622" y="729"/>
</window>
</objects>
<resources>
Expand Down
1 change: 0 additions & 1 deletion XCredsLoginPlugIn/LoginWindow/TCSReturnWindow.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//
// TCSReturnWindow.h
// Boot Runner
//
// Created by Tim Perfitt on 9/6/17.
//
Expand Down
1 change: 0 additions & 1 deletion XCredsLoginPlugIn/LoginWindow/TCSReturnWindow.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//
// TCSReturnWindow.m
// Boot Runner
//
// Created by Tim Perfitt on 9/6/17.
//
Expand Down
1 change: 0 additions & 1 deletion XCredsLoginPlugIn/Mechanisms/LogShim.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
//os_log("Unable to get uid.", log: keychainAddLog, type: .error)

let noLoMechlog = ""
enum ErrorType {
Expand Down
2 changes: 2 additions & 0 deletions XCredsLoginPlugIn/Mechanisms/XCredsBaseMechanism.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ protocol XCredsMechanismProtocol {
var xcredsUser: String? {
get {
guard let userName = getHint(type: .user) as? String else {
TCSLogWithMark("no username!")

return nil
}
TCSLogWithMark("Computed user accessed")
Expand Down

0 comments on commit 2e3114e

Please sign in to comment.