puppetlabs/puppet · error · Puppet::Util::Windows::Error
OpenProcessToken(#{handle}, #{desired_access.to_s(8)}, #{tok
Error message
OpenProcessToken(#{handle}, #{desired_access.to_s(8)}, #{token_handle_ptr}) What it means
Raised by Process.open_process_token (lib/puppet/util/windows/process.rb:95) when OpenProcessToken returns FALSE for the given process handle and requested access mask. ERROR_ACCESS_DENIED (5) dominates: the token does not grant everything in the mask. Notably Puppet itself calls this with TOKEN_ALL_ACCESS (0xF01FF) in process_privilege_symlink?, which can be denied for a filtered (UAC-de-elevated) token, while TOKEN_QUERY (0x0008) succeeds.
Source
Thrown at lib/puppet/util/windows/process.rb:95
end
yield phandle
ensure
FFI::WIN32.CloseHandle(phandle) if phandle
end
# phandle has had CloseHandle called against it, so nothing to return
nil
end
module_function :open_process
def open_process_token(handle, desired_access, &block)
token_handle = nil
begin
FFI::MemoryPointer.new(:handle, 1) do |token_handle_ptr|
result = OpenProcessToken(handle, desired_access, token_handle_ptr)
if result == FFI::WIN32_FALSE
raise Puppet::Util::Windows::Error, "OpenProcessToken(#{handle}, #{desired_access.to_s(8)}, #{token_handle_ptr})"
end
yield token_handle = token_handle_ptr.read_handle
end
token_handle
ensure
FFI::WIN32.CloseHandle(token_handle) if token_handle
end
# token_handle has had CloseHandle called against it, so nothing to return
nil
end
module_function :open_process_token
# Execute a block with the current process token
def with_process_token(access, &block)
handle = get_current_processView on GitHub (pinned to e227c27540)
Solutions
- Downgrade the access request: TOKEN_QUERY for reads, TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES when enabling privileges.
- For the current process use Process.get_current_process (pseudo-handle) instead of opening by pid.
- Run elevated (or as an agent under SYSTEM) when inspecting other processes' tokens.
- Rescue and read e.code — 5 (access denied) vs 6 (invalid handle from a lifecycle bug).
Example fix
# before — TOKEN_ALL_ACCESS on a filtered/UAC token can be denied
Process.open_process_token(handle, Process::TOKEN_ALL_ACCESS) { |t| use(t) }
# after — ask only for what is needed, on the current process
Process.open_process_token(Process.get_current_process,
Process::TOKEN_QUERY | Puppet::Util::Windows::Security::TOKEN_ADJUST_PRIVILEGES) { |t| use(t) } Defensive patterns
Strategy: try-catch
Try / catch
begin
Process.open_process_token(handle, desired_access) { |t| yield t }
rescue Puppet::Util::Windows::Error => e
if e.code == 5 && (desired_access & ~Process::TOKEN_QUERY) != 0
Process.open_process_token(handle, Process::TOKEN_QUERY) { |t| yield t } # retry narrower
else
raise
end
end Prevention
- Default to TOKEN_QUERY; request adjust rights only when changing privileges
- Use get_current_process for the running process instead of OpenProcess(pid)
- Keep token usage inside the open_process_token block — the handle closes afterwards
- Test under both elevated and filtered (UAC) tokens
When it happens
Trigger: Requesting TOKEN_ALL_ACCESS or TOKEN_ADJUST_PRIVILEGES on a process the caller does not fully own (other session, elevated process seen from a non-elevated agent, filtered admin token); passing a NULL or already-closed process handle; opening a protected process's token.
Common situations: Non-elevated Ruby code adjusting privileges or querying token state; agents inspecting service processes; handles saved beyond the open_process_token block that guaranteed their lifetime.
Related errors
- OpenProcess(#{desired_access.to_s(8)}, #{inherit}, #{process
- GetTokenInformation(#{token_handle}, #{token_information}, n
- GetTokenInformation(#{token_handle}, #{token_information}, #
- ReplaceFile(#{target}, #{source})
- MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/4d6f36d8617694da.
Report an issue: GitHub.