From d1ecb5f552577afea267968f00358be9f9dac6ce Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 24 Feb 2019 10:06:21 -0800 Subject: [PATCH 1/5] remove useless function --- psutil/arch/windows/security.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/psutil/arch/windows/security.c b/psutil/arch/windows/security.c index d5f8f8d3da..fa108b1f70 100644 --- a/psutil/arch/windows/security.c +++ b/psutil/arch/windows/security.c @@ -11,19 +11,6 @@ #include -/* - * Convert a process handle to a process token handle. - */ -HANDLE -psutil_token_from_handle(HANDLE hProcess) { - HANDLE hToken = NULL; - - if (! OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) - return PyErr_SetFromWindowsErr(0); - return hToken; -} - - /* * http://www.ddj.com/windows/184405986 * @@ -47,9 +34,9 @@ psutil_has_system_privilege(HANDLE hProcess) { // PTOKEN_PRIVILEGES tp = NULL; BYTE *pBuffer = NULL; TOKEN_PRIVILEGES *tp = NULL; - HANDLE hToken = psutil_token_from_handle(hProcess); + HANDLE hToken = NULL; - if (NULL == hToken) + if (! OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) return -1; // call GetTokenInformation first to get the buffer size if (! GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize)) { From 1a0c0446cbeb93b7efd15cd1512212e8ddead0c7 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 24 Feb 2019 14:07:31 -0800 Subject: [PATCH 2/5] refactor all functions around SE DEBUG; we weren't checking errors (did we ever set SE DEBUG at all?) - if SE DEBUG can't be set we now raise an exception on import; that's bad - I'll change it later, and I will probably replace it with a warning --- psutil/_psutil_windows.c | 3 +- psutil/arch/windows/security.c | 222 ++++++++++----------------------- psutil/arch/windows/security.h | 4 - 3 files changed, 70 insertions(+), 159 deletions(-) diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 7dc37ee171..1e2894a8ce 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -3724,7 +3724,8 @@ void init_psutil_windows(void) module, "ERROR_SERVICE_DOES_NOT_EXIST", ERROR_SERVICE_DOES_NOT_EXIST); // set SeDebug for the current process - psutil_set_se_debug(); + if (psutil_set_se_debug() != 0) + return NULL; psutil_setup(); if (psutil_load_globals() != 0) return NULL; diff --git a/psutil/arch/windows/security.c b/psutil/arch/windows/security.c index fa108b1f70..50e38c5078 100644 --- a/psutil/arch/windows/security.c +++ b/psutil/arch/windows/security.c @@ -9,115 +9,38 @@ #include #include +#include "../../_psutil_common.h" -/* - * http://www.ddj.com/windows/184405986 - * - * There's a way to determine whether we're running under the Local System - * account. However (you guessed it), we have to call more Win32 functions to - * determine this. Backing up through the code listing, we need to make another - * call to GetTokenInformation, but instead of passing through the TOKEN_USER - * constant, we pass through the TOKEN_PRIVILEGES constant. This value returns - * an array of privileges that the account has in the environment. Iterating - * through the array, we call the function LookupPrivilegeName looking for the - * string “SeTcbPrivilege. If the function returns this string, then this - * account has Local System privileges - */ -int -psutil_has_system_privilege(HANDLE hProcess) { - DWORD i; - DWORD dwSize = 0; - DWORD dwRetval = 0; - TCHAR privName[256]; - DWORD dwNameSize = 256; - // PTOKEN_PRIVILEGES tp = NULL; - BYTE *pBuffer = NULL; - TOKEN_PRIVILEGES *tp = NULL; - HANDLE hToken = NULL; - - if (! OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) - return -1; - // call GetTokenInformation first to get the buffer size - if (! GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize)) { - dwRetval = GetLastError(); - // if it failed for a reason other than the buffer, bail out - if (dwRetval != ERROR_INSUFFICIENT_BUFFER ) { - PyErr_SetFromWindowsErr(dwRetval); - return 0; - } - } - - // allocate buffer and call GetTokenInformation again - // tp = (PTOKEN_PRIVILEGES) GlobalAlloc(GPTR, dwSize); - pBuffer = (BYTE *) malloc(dwSize); - if (pBuffer == NULL) { - PyErr_NoMemory(); - return -1; - } - - if (! GetTokenInformation(hToken, TokenPrivileges, pBuffer, - dwSize, &dwSize)) - { - PyErr_SetFromWindowsErr(0); - free(pBuffer); - return -1; - } - - // convert the BYTE buffer to a TOKEN_PRIVILEGES struct pointer - tp = (TOKEN_PRIVILEGES *)pBuffer; - - // check all the privileges looking for SeTcbPrivilege - for (i = 0; i < tp->PrivilegeCount; i++) { - // reset the buffer contents and the buffer size - strcpy(privName, ""); - dwNameSize = sizeof(privName) / sizeof(TCHAR); - if (! LookupPrivilegeName(NULL, - &tp->Privileges[i].Luid, - (LPTSTR)privName, - &dwNameSize)) - { - PyErr_SetFromWindowsErr(0); - free(pBuffer); - return -1; - } - - // if we find the SeTcbPrivilege then it's a LocalSystem process - if (! lstrcmpi(privName, TEXT("SeTcbPrivilege"))) { - free(pBuffer); - return 1; - } - } - - free(pBuffer); - return 0; -} - - -BOOL +static BOOL psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) { TOKEN_PRIVILEGES tp; LUID luid; TOKEN_PRIVILEGES tpPrevious; DWORD cbPrevious = sizeof(TOKEN_PRIVILEGES); - if (!LookupPrivilegeValue( NULL, Privilege, &luid )) return FALSE; + if (! LookupPrivilegeValue(NULL, Privilege, &luid)) { + PyErr_SetFromOSErrnoWithSyscall("LookupPrivilegeValue"); + return 1; + } // first pass. get current privilege setting tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = 0; - AdjustTokenPrivileges( - hToken, - FALSE, - &tp, - sizeof(TOKEN_PRIVILEGES), - &tpPrevious, - &cbPrevious - ); + if (! AdjustTokenPrivileges( + hToken, + FALSE, + &tp, + sizeof(TOKEN_PRIVILEGES), + &tpPrevious, + &cbPrevious)) + { + PyErr_SetFromOSErrnoWithSyscall("AdjustTokenPrivileges"); + return 1; + } - if (GetLastError() != ERROR_SUCCESS) return FALSE; // second pass. set privilege based on previous setting tpPrevious.PrivilegeCount = 1; tpPrevious.Privileges[0].Luid = luid; @@ -128,81 +51,72 @@ psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) { tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & tpPrevious.Privileges[0].Attributes); - AdjustTokenPrivileges( - hToken, - FALSE, - &tpPrevious, - cbPrevious, - NULL, - NULL - ); - - if (GetLastError() != ERROR_SUCCESS) return FALSE; + if (! AdjustTokenPrivileges( + hToken, + FALSE, + &tpPrevious, + cbPrevious, + NULL, + NULL)) + { + PyErr_SetFromOSErrnoWithSyscall("AdjustTokenPrivileges"); + return 1; + } - return TRUE; + return 0; } -int -psutil_set_se_debug() { - HANDLE hToken; - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - &hToken) - ) { - if (GetLastError() == ERROR_NO_TOKEN) { - if (!ImpersonateSelf(SecurityImpersonation)) { - CloseHandle(hToken); - return 0; +static HANDLE +psutil_get_thisproc_token() { + HANDLE hToken = NULL; + HANDLE me = GetCurrentProcess(); + + if (! OpenProcessToken( + me, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + { + if (GetLastError() == ERROR_NO_TOKEN) + { + if (! ImpersonateSelf(SecurityImpersonation)) { + PyErr_SetFromOSErrnoWithSyscall("ImpersonateSelf"); + return NULL; } - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - &hToken) - ) { - RevertToSelf(); - CloseHandle(hToken); - return 0; + if (! OpenProcessToken( + me, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) + { + PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken"); + return NULL; } } + else { + PyErr_SetFromOSErrnoWithSyscall("OpenProcessToken"); + return NULL; + } } - // enable SeDebugPrivilege (open any process) - if (! psutil_set_privilege(hToken, SE_DEBUG_NAME, TRUE)) { - RevertToSelf(); - CloseHandle(hToken); - return 0; - } - - RevertToSelf(); - CloseHandle(hToken); - return 1; + return hToken; } +/* + * Set this process in SE DEBUG mode so that we have more chances of + * querying processes owned by other users, including many owned by + * Administrator and Local System. + * https://docs.microsoft.com/windows-hardware/drivers/debugger/debug-privilege + */ int -psutil_unset_se_debug() { +psutil_set_se_debug() { HANDLE hToken; - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - &hToken) - ) { - if (GetLastError() == ERROR_NO_TOKEN) { - if (! ImpersonateSelf(SecurityImpersonation)) - return 0; - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, - &hToken)) - { - return 0; - } - } - } + int err = 1; - // now disable SeDebug - if (! psutil_set_privilege(hToken, SE_DEBUG_NAME, FALSE)) - return 0; + if ((hToken = psutil_get_thisproc_token()) == NULL) + return 1; + // enable SeDebugPrivilege (open any process) + if (psutil_set_privilege(hToken, SE_DEBUG_NAME, TRUE) == 0) + err = 0; + + RevertToSelf(); CloseHandle(hToken); - return 1; + return err; } - diff --git a/psutil/arch/windows/security.h b/psutil/arch/windows/security.h index aa8a22ad1a..8d4ddb00d4 100644 --- a/psutil/arch/windows/security.h +++ b/psutil/arch/windows/security.h @@ -9,9 +9,5 @@ #include -BOOL psutil_set_privilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege); -HANDLE psutil_token_from_handle(HANDLE hProcess); -int psutil_has_system_privilege(HANDLE hProcess); int psutil_set_se_debug(); -int psutil_unset_se_debug(); From c35d4b0c582180bb870cc88b113674deb0c569d2 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 24 Feb 2019 23:11:41 +0100 Subject: [PATCH 3/5] add script to print process' AD --- Makefile | 4 ++ scripts/internal/procs_access_denied.py | 80 +++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 scripts/internal/procs_access_denied.py diff --git a/Makefile b/Makefile index 25928e0aa3..744805187f 100644 --- a/Makefile +++ b/Makefile @@ -266,5 +266,9 @@ bench-oneshot-2: ## Same as above but using perf module (supposed to be more pr check-broken-links: ## Look for broken links in source files. git ls-files | xargs $(PYTHON) -Wa scripts/internal/check_broken_links.py +print-access-denied: +# ${MAKE} install + $(TEST_PREFIX) $(PYTHON) scripts/internal/procs_access_denied.py + help: ## Display callable targets. @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' diff --git a/scripts/internal/procs_access_denied.py b/scripts/internal/procs_access_denied.py new file mode 100644 index 0000000000..9f792480bc --- /dev/null +++ b/scripts/internal/procs_access_denied.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Helper script which tries to access all info of all running processes. +It prints how many AccessDenied exceptions are raised in total and +for each Process method. +""" + +from __future__ import print_function, division +from collections import defaultdict +import sys + +import psutil + + +def term_supports_colors(file=sys.stdout): + try: + import curses + assert file.isatty() + curses.setupterm() + assert curses.tigetnum("colors") > 0 + except Exception: + return False + else: + return True + + +COLORS = term_supports_colors() + + +def hilite(s, ok=True, bold=False): + """Return an highlighted version of 'string'.""" + if not COLORS: + return s + attr = [] + if ok is None: # no color + pass + elif ok: # green + attr.append('32') + else: # red + attr.append('31') + if bold: + attr.append('1') + return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), s) + + +def main(): + tot_procs = 0 + tot_ads = 0 + signaler = object() + d = defaultdict(int) + for p in psutil.process_iter(attrs=[], ad_value=signaler): + tot_procs += 1 + for methname, value in p.info.items(): + if value is signaler: + tot_ads += 1 + d[methname] += 1 + else: + d[methname] += 0 + + for methname, ads in sorted(d.items(), key=lambda x: x[1]): + perc = (ads / tot_procs) * 100 + s = "%-20s %-3s %5.1f%% " % (methname, ads, perc) + if not ads: + s += "SUCCESS" + s = hilite(s, ok=True) + else: + s += "ACCESS DENIED" + s = hilite(s, ok=False) + print(s) + print("--------------------------") + print("total: %19s (%s total processes)" % (tot_ads, tot_procs)) + + +if __name__ == '__main__': + main() From 961a5ac2363c41282f1a89b87a261a6d15a8c278 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 24 Feb 2019 14:14:12 -0800 Subject: [PATCH 4/5] add cmd for winmake --- scripts/internal/winmake.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index b1ce7b8a4d..49aae699f9 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -483,6 +483,13 @@ def bench_oneshot_2(): sh("%s -Wa scripts\\internal\\bench_oneshot_2.py" % PYTHON) +@cmd +def print_access_denied(): + """Benchmarks for oneshot() ctx manager (see #799).""" + install() + sh("%s -Wa scripts\\internal\\procs_access_denied.py" % PYTHON) + + def set_python(s): global PYTHON if os.path.isabs(s): From c7def9e57fabcb6da48d30f77e5d40aa490c7076 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Sun, 24 Feb 2019 15:22:56 -0800 Subject: [PATCH 5/5] instead of raising exception print DEBUG message (we can't crash on import) --- psutil/_psutil_windows.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index 1e2894a8ce..8167503cde 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -3724,8 +3724,12 @@ void init_psutil_windows(void) module, "ERROR_SERVICE_DOES_NOT_EXIST", ERROR_SERVICE_DOES_NOT_EXIST); // set SeDebug for the current process - if (psutil_set_se_debug() != 0) - return NULL; + if (psutil_set_se_debug() != 0) { + PyErr_Clear(); + if (PSUTIL_DEBUG) + psutil_debug("SE DEBUG process mode not set"); + } + psutil_setup(); if (psutil_load_globals() != 0) return NULL;