Skip to content

Commit 184942e

Browse files
authored
check process that makes IsSecureEventInputEnabled true (#269)
1 parent 9bd7b1d commit 184942e

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ add_executable(Fcitx5
4141
locale.swift
4242
controller.swift
4343
color.swift
44+
secure.swift
4445
${CONFIG_UI_FILES}
4546
)
4647

src/controller.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class FcitxInputController: IMKInputController {
2121
let client: Any!
2222
var accentColor = ""
2323
var selection: NSRange? = nil
24+
var obeySecureInput = true
2425

2526
// A registry of live FcitxInputController objects.
2627
// Use NSHashTable to store weak references.
@@ -70,6 +71,33 @@ class FcitxInputController: IMKInputController {
7071
return response.accepted
7172
}
7273

74+
// Normal apps like Chrome calls EnableSecureEventInput when its password input is focused,
75+
// and calls DisableSecureEventInput on blur of input or app itself. Abnormal apps call
76+
// EnableSecureEventInput but doesn't call DisableSecureEventInput on blur, so we can't
77+
// rely on IsSecureEventInputEnabled's true return value, as obeying it will lock keyboard-us.
78+
// Users observe https://discussions.apple.com/thread/253793652 but it's also possible that
79+
// other apps are abusing, see comments for getSecureInputProcessPID.
80+
func getSecureInputInfo(isOnFocus: Bool) -> Bool {
81+
if appId == "com.apple.loginwindow" {
82+
return true
83+
}
84+
if !IsSecureEventInputEnabled() {
85+
return false
86+
}
87+
if isOnFocus {
88+
let pid = getSecureInputProcessPID()
89+
let runningApp = pid == nil ? nil : NSRunningApplication(processIdentifier: pid!)
90+
obeySecureInput = runningApp?.bundleIdentifier == appId
91+
if !obeySecureInput {
92+
FCITX_WARN(
93+
"Secure input is abused by (possibly) \(runningApp?.localizedName ?? "?"): \(runningApp?.bundleIdentifier ?? "?") pid=\(pid ?? -1)"
94+
)
95+
}
96+
}
97+
// On keyDown, don't call getSecureInputProcessPID for performance.
98+
return obeySecureInput
99+
}
100+
73101
func processKey(_ unicode: UInt32, _ modsVal: UInt32, _ code: UInt16, _ isRelease: Bool) -> Bool {
74102
guard let client = client as? IMKTextInput else {
75103
return false
@@ -83,7 +111,7 @@ class FcitxInputController: IMKInputController {
83111
process_key(uuid, 0, 0, 0, false, false)
84112
}
85113
// It can change within an IMKInputController (e.g. sudo in Terminal), so must reevaluate before each key sent to IM.
86-
let isPassword = IsSecureEventInputEnabled()
114+
let isPassword = getSecureInputInfo(isOnFocus: false)
87115
let res = String(process_key(uuid, unicode, modsVal, code, isRelease, isPassword))
88116
return processRes(client, res)
89117
}
@@ -151,7 +179,7 @@ class FcitxInputController: IMKInputController {
151179
// activateServer is called when app is in foreground but not necessarily a text field is selected.
152180
hasCursor = false
153181
// Make sure status bar is updated on click password input, before first key event.
154-
let isPassword = IsSecureEventInputEnabled()
182+
let isPassword = getSecureInputInfo(isOnFocus: true)
155183
focus_in(uuid, isPassword)
156184
}
157185

src/secure.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import AppKit
2+
import Carbon
3+
import IOKit
4+
5+
// Translated from https://github.com/espanso/espanso
6+
// The return value is not guaranteed accurate. To reproduce, execute
7+
// import Carbon
8+
// EnableSecureEventInput()
9+
// in swift repl, then this function returns terminal app's pid.
10+
// However, after Ctrl+Cmd+Q and re-login, it returns com.apple.loginwindow's pid.
11+
func getSecureInputProcessPID() -> Int32? {
12+
let rootService = IORegistryGetRootEntry(kIOMainPortDefault)
13+
guard rootService != 0 else { return nil }
14+
15+
defer { IOObjectRelease(rootService) }
16+
if let cfConsoleUsers = IORegistryEntryCreateCFProperty(
17+
rootService,
18+
"IOConsoleUsers" as CFString,
19+
kCFAllocatorDefault,
20+
0
21+
)?.takeRetainedValue() as? [Any] {
22+
for user in cfConsoleUsers {
23+
if let userDict = user as? [String: Any],
24+
let secureInputPID = userDict["kCGSSessionSecureInputPID"] as? NSNumber
25+
{
26+
return secureInputPID.int32Value
27+
}
28+
}
29+
}
30+
return nil
31+
}

0 commit comments

Comments
 (0)