{"record":{"id":"4d6f36d8617694da","repo":"puppetlabs/puppet","slug":"openprocesstoken-handle-desired-access-to-s","errorCode":null,"errorMessage":"OpenProcessToken(#{handle}, #{desired_access.to_s(8)}, #{token_handle_ptr})","messagePattern":"OpenProcessToken\\(#(.+?), #(.+?), #(.+?)\\)","errorType":"exception","errorClass":"Puppet::Util::Windows::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/windows/process.rb","lineNumber":95,"sourceCode":"      end\n\n      yield phandle\n    ensure\n      FFI::WIN32.CloseHandle(phandle) if phandle\n    end\n\n    # phandle has had CloseHandle called against it, so nothing to return\n    nil\n  end\n  module_function :open_process\n\n  def open_process_token(handle, desired_access, &block)\n    token_handle = nil\n    begin\n      FFI::MemoryPointer.new(:handle, 1) do |token_handle_ptr|\n        result = OpenProcessToken(handle, desired_access, token_handle_ptr)\n        if result == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, \"OpenProcessToken(#{handle}, #{desired_access.to_s(8)}, #{token_handle_ptr})\"\n        end\n\n        yield token_handle = token_handle_ptr.read_handle\n      end\n\n      token_handle\n    ensure\n      FFI::WIN32.CloseHandle(token_handle) if token_handle\n    end\n\n    # token_handle has had CloseHandle called against it, so nothing to return\n    nil\n  end\n  module_function :open_process_token\n\n  # Execute a block with the current process token\n  def with_process_token(access, &block)\n    handle = get_current_process","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/windows/process.rb#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before — TOKEN_ALL_ACCESS on a filtered/UAC token can be denied\nProcess.open_process_token(handle, Process::TOKEN_ALL_ACCESS) { |t| use(t) }\n\n# after — ask only for what is needed, on the current process\nProcess.open_process_token(Process.get_current_process,\n                           Process::TOKEN_QUERY | Puppet::Util::Windows::Security::TOKEN_ADJUST_PRIVILEGES) { |t| use(t) }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  Process.open_process_token(handle, desired_access) { |t| yield t }\nrescue Puppet::Util::Windows::Error => e\n  if e.code == 5 && (desired_access & ~Process::TOKEN_QUERY) != 0\n    Process.open_process_token(handle, Process::TOKEN_QUERY) { |t| yield t } # retry narrower\n  else\n    raise\n  end\nend","preventionTips":["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"],"tags":["windows","win32-api","token","permissions","uac","ffi","puppet"],"backgroundTag":"win32-openprocesstoken-access-denied","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}