From a3fa90cd32c4d89da6e9212d765380bc3ef49a82 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Wed, 19 Jun 2024 16:27:45 -0400 Subject: [PATCH] Fixes from real testing --- Daemon/Daemon.swift | 36 ++++++++++++++++++++++++--- Daemon/main.swift | 12 ++++----- Daemon/org.bluestatic.InterAuth.plist | 10 +++++--- InterAuth.xcodeproj/project.pbxproj | 4 +++ PAM/pam_interauth.c | 16 ++++++------ README.md | 34 +++++++++++++++++++++++++ Tool/main.swift | 17 ++++++++++--- 7 files changed, 105 insertions(+), 24 deletions(-) create mode 100644 README.md diff --git a/Daemon/Daemon.swift b/Daemon/Daemon.swift index 2baa813..7e3ef41 100644 --- a/Daemon/Daemon.swift +++ b/Daemon/Daemon.swift @@ -15,42 +15,69 @@ extension OS_xpc_object { free(d) return r } + + func retain() -> Self { + xpc_retain(self) + return self + } + + func release() { + xpc_release(self) + } } class Daemon { private var connection: xpc_connection_t? private var log = Logger(subsystem: "org.bluestatic.InterAuth", category: "Daemon") + init() { + log.log("Daemon starting") + } + func onControlConnection(_ obj: xpc_object_t) { + log.log("New connection received \(obj.description, privacy: .public) (existing: \(self.connection?.description ?? "null", privacy: .public))") + guard xpc_get_type(obj) == XPC_TYPE_CONNECTION else { - log.error("Control connection failure: \(obj.description, privacy: .public)") + log.error("Control connection failure") return } guard xpc_connection_get_euid(obj) == 0 else { log.error("Rejecting control connection from non-root user") + xpc_connection_cancel(obj) return } guard connection == nil else { log.warning("Rejecting control connection, existing connection established") + xpc_connection_cancel(obj) return } log.log("Establishing new control connection") - connection = obj as xpc_connection_t + connection = obj.retain() xpc_connection_set_event_handler(connection!) { self.onControlMessage($0) } - xpc_connection_resume(connection!) + xpc_connection_activate(connection!) } private func onControlMessage(_ obj: xpc_object_t) { if xpc_equal(obj, XPC_ERROR_CONNECTION_INVALID) || xpc_equal(obj, XPC_ERROR_CONNECTION_INTERRUPTED) { log.log("Control connection disconnected") + connection?.release() connection = nil return } + guard xpc_get_type(obj) == XPC_TYPE_DICTIONARY, + let action = xpc_dictionary_get_string(obj, "action") else { + log.error("Got malformed message on control channel, disconnecting") + connection?.release() + connection = nil + return + } + + log.log("Got control connection action \(String(cString: action), privacy: .public)") } func onAuthConnection(_ obj: xpc_object_t) { @@ -60,6 +87,7 @@ class Daemon { } guard connection != nil else { log.log("Received authentication connection, but there is no active control connection") + xpc_connection_cancel(obj) return } @@ -74,7 +102,7 @@ class Daemon { xpc_get_type(obj) == XPC_TYPE_DICTIONARY else { log.log("Authentication connection disconnected") xpc_connection_set_event_handler(authConn) { _ in } - xpc_release(authConn) + xpc_connection_cancel(authConn) return } diff --git a/Daemon/main.swift b/Daemon/main.swift index bb38d76..95150f1 100644 --- a/Daemon/main.swift +++ b/Daemon/main.swift @@ -12,15 +12,15 @@ let daemon = Daemon() let q = DispatchQueue(label: "org.bluestatic.InterAuth", qos: .userInteractive) let ctrlConn = xpc_connection_create_mach_service("org.bluestatic.InterAuth.Control", q, UInt64(XPC_CONNECTION_MACH_SERVICE_LISTENER)) -xpc_connection_set_event_handler(ctrlConn) { - daemon.onControlConnection($0) +xpc_connection_set_event_handler(ctrlConn) { evt in + daemon.onControlConnection(evt) } -xpc_connection_resume(ctrlConn) +xpc_connection_activate(ctrlConn) let authConn = xpc_connection_create_mach_service("org.bluestatic.InterAuth.Auth", q, UInt64(XPC_CONNECTION_MACH_SERVICE_LISTENER)) -xpc_connection_set_event_handler(authConn) { - daemon.onAuthConnection($0) +xpc_connection_set_event_handler(authConn) { evt in + daemon.onAuthConnection(evt) } -xpc_connection_resume(authConn) +xpc_connection_activate(authConn) dispatchMain() diff --git a/Daemon/org.bluestatic.InterAuth.plist b/Daemon/org.bluestatic.InterAuth.plist index ead533f..81360d2 100644 --- a/Daemon/org.bluestatic.InterAuth.plist +++ b/Daemon/org.bluestatic.InterAuth.plist @@ -9,9 +9,11 @@ /Library/InterAuth.bundle/Contents/MacOS/InterAuth MachServices - - org.bluestatic.InterAuth.Auth - org.bluestatic.InterAuth.Control - + + org.bluestatic.InterAuth.Auth + + org.bluestatic.InterAuth.Control + + diff --git a/InterAuth.xcodeproj/project.pbxproj b/InterAuth.xcodeproj/project.pbxproj index ae6633b..595002c 100644 --- a/InterAuth.xcodeproj/project.pbxproj +++ b/InterAuth.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 1EC7F7B12C232302001CCD94 /* pam_interauth.so.2 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1EC7F79A2C231AD7001CCD94 /* pam_interauth.so.2 */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 1EC7F7B62C2323C3001CCD94 /* libpam.2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EC7F7B52C2323C0001CCD94 /* libpam.2.tbd */; }; 1EC7F7B82C232939001CCD94 /* libSystem.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EC7F7B72C232932001CCD94 /* libSystem.tbd */; }; + 1EC7F7BA2C236671001CCD94 /* org.bluestatic.InterAuth.plist in Resources */ = {isa = PBXBuildFile; fileRef = 1EC7F7B92C233006001CCD94 /* org.bluestatic.InterAuth.plist */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -97,6 +98,7 @@ 1EC7F7B52C2323C0001CCD94 /* libpam.2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libpam.2.tbd; path = usr/lib/libpam.2.tbd; sourceTree = SDKROOT; }; 1EC7F7B72C232932001CCD94 /* libSystem.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libSystem.tbd; path = usr/lib/libSystem.tbd; sourceTree = SDKROOT; }; 1EC7F7B92C233006001CCD94 /* org.bluestatic.InterAuth.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = org.bluestatic.InterAuth.plist; sourceTree = ""; }; + 1EC7F7BB2C237507001CCD94 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -136,6 +138,7 @@ 1EC7F7702C231A3E001CCD94 = { isa = PBXGroup; children = ( + 1EC7F7BB2C237507001CCD94 /* README.md */, 1EC7F7852C231A9B001CCD94 /* Daemon */, 1EC7F7AD2C232085001CCD94 /* PAM */, 1EC7F7902C231AB2001CCD94 /* Tool */, @@ -326,6 +329,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1EC7F7BA2C236671001CCD94 /* org.bluestatic.InterAuth.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/PAM/pam_interauth.c b/PAM/pam_interauth.c index 8ea1b9d..fada8de 100644 --- a/PAM/pam_interauth.c +++ b/PAM/pam_interauth.c @@ -23,9 +23,7 @@ static os_log_t get_log(void) { return log; } -static void message_handler(xpc_object_t obj, dispatch_semaphore_t sema, int* pam_return) { - dispatch_semaphore_signal(sema); - +static void message_handler(xpc_object_t obj, int* pam_return) { if (obj == XPC_ERROR_CONNECTION_INVALID) { os_log(get_log(), "Connection invalid"); *pam_return = PAM_AUTHINFO_UNAVAIL; @@ -33,7 +31,7 @@ static void message_handler(xpc_object_t obj, dispatch_semaphore_t sema, int* pa } if (obj == XPC_ERROR_CONNECTION_INTERRUPTED) { os_log(get_log(), "Connection interrupted"); - *pam_return = PAM_SYSTEM_ERR; + *pam_return = PAM_AUTHINFO_UNAVAIL; return; } @@ -46,8 +44,10 @@ static void message_handler(xpc_object_t obj, dispatch_semaphore_t sema, int* pa } if (xpc_dictionary_get_bool(obj, "result")) { + os_log(get_log(), "ALLOW from control daemon"); *pam_return = PAM_SUCCESS; } else { + os_log(get_log(), "DENY from control daemon"); *pam_return = PAM_PERM_DENIED; } } @@ -80,7 +80,8 @@ int pam_sm_authenticate(pam_handle_t* pamh, int flags, int argc, const char** ar dispatch_queue_t q = dispatch_queue_create("org.bluestatic.InterAuth.PAM", DISPATCH_QUEUE_SERIAL); xpc_connection_t conn = xpc_connection_create_mach_service("org.bluestatic.InterAuth.Auth", q, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); xpc_connection_set_event_handler(conn, ^(xpc_object_t _Nonnull object) { - message_handler(object, sema, &auth_result); + message_handler(object, &auth_result); + dispatch_semaphore_signal(sema); }); xpc_connection_resume(conn); @@ -96,7 +97,8 @@ int pam_sm_authenticate(pam_handle_t* pamh, int flags, int argc, const char** ar xpc_dictionary_set_string(msg, "applicant", applicant); } xpc_connection_send_message_with_reply(conn, msg, q, ^(xpc_object_t _Nonnull object) { - message_handler(object, sema, &auth_result); + message_handler(object, &auth_result); + dispatch_semaphore_signal(sema); }); if (dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_WALLTIME_NOW, 30 * NSEC_PER_SEC))) { @@ -108,7 +110,7 @@ int pam_sm_authenticate(pam_handle_t* pamh, int flags, int argc, const char** ar dispatch_release(q); dispatch_release(sema); - os_log(get_log(), "Final auth disposition = %d", auth_result); + os_log(get_log(), "Final auth disposition = %d (success? %d)", auth_result, (auth_result == PAM_SUCCESS)); return auth_result; } diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d3bb04 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# InterAuth + +## Installation + +``` +sudo mv InterAuth.bundle /Library/ +sudo chown root:wheel /Library/InterAuth.bundle +sudo chmod 644 /Library/InterAuth.bundle +sudo cp /Library/InterAuth.bundle/Contents/Resources/org.bluestatic.InterAuth.plist /Library/LaunchDaemons +sudo launchctl bootstrap system /Library/LaunchDaemons/org.bluestatic.InterAuth.plist +``` + +Edit these files and insert the following as the first line: +- /etc/pam.d/login +- /etc/pam.d/authorization +- /etc/pam.d/screensaver + +``` +auth sufficient /Library/InterAuth.bundle/Contents/Libraries/pam_interauth.so.2 +``` + +## Usage + +``` +sudo /Library/InterAuth.bundle/Contents/MacOS/interauthctl +``` + +... and wait for authentication requests. + +For debugging: + +``` +log stream --predicate 'subsystem == "org.bluestatic.InterAuth"' --style compact +``` diff --git a/Tool/main.swift b/Tool/main.swift index 415f915..2b13b23 100644 --- a/Tool/main.swift +++ b/Tool/main.swift @@ -15,6 +15,13 @@ if getuid() != 0 { print("Starting InterAuth control session.") +func getXPCString(_ obj: xpc_object_t, _ key: String) -> String { + guard let val = xpc_dictionary_get_string(obj, key) else { + return "(nil)" + } + return String(cString: val) +} + let conn = xpc_connection_create_mach_service("org.bluestatic.InterAuth.Control", DispatchQueue.main, UInt64(XPC_CONNECTION_MACH_SERVICE_PRIVILEGED)) xpc_connection_set_event_handler(conn) { (msg: xpc_object_t) in if xpc_equal(msg, XPC_ERROR_CONNECTION_INVALID) { @@ -32,9 +39,9 @@ xpc_connection_set_event_handler(conn) { (msg: xpc_object_t) in } print("\n*** Authentication Request ***") - print(" User = \(String(describing: xpc_dictionary_get_string(req, "user")))") - print(" Service = \(String(describing: xpc_dictionary_get_string(req, "service")))") - print(" Applicant = \(String(describing: xpc_dictionary_get_string(req, "applicant")))") + print(" User = \(getXPCString(req, "user"))") + print(" Service = \(getXPCString(req, "service"))") + print(" Applicant = \(getXPCString(req, "applicant"))") var authorize = false while true { @@ -58,5 +65,9 @@ xpc_connection_set_event_handler(conn) { (msg: xpc_object_t) in } xpc_connection_resume(conn) +let ping = xpc_dictionary_create_empty() +xpc_dictionary_set_string(ping, "action", "ping") +xpc_connection_send_message(conn, ping) + dispatchMain() -- 2.43.5