From 95f60010219e142a436fae18e1695cbc45407afe Mon Sep 17 00:00:00 2001 From: Saiyang Gou Date: Tue, 4 Feb 2020 16:15:00 -0800 Subject: [PATCH] bpo-39184: Add audit events to command execution functions in os and pty modules (GH-17824) --- Doc/library/os.rst | 10 ++++ Doc/library/pty.rst | 1 + Lib/pty.py | 2 + .../2020-01-07-00-42-08.bpo-39184.fe7NgK.rst | 1 + Modules/posixmodule.c | 49 ++++++++++++++++--- 5 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Security/2020-01-07-00-42-08.bpo-39184.fe7NgK.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index f59423c6f2d..bfc03227e4e 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3314,6 +3314,8 @@ to be ignored. you can check whether or not it is available using :data:`os.supports_fd`. If it is unavailable, using it will raise a :exc:`NotImplementedError`. + .. audit-event:: os.exec path,args,env os.execl + .. availability:: Unix, Windows. .. versionadded:: 3.3 @@ -3670,6 +3672,8 @@ written in Python, such as a mail server's external command delivery program. :c:data:`POSIX_SPAWN_SETSCHEDPARAM` and :c:data:`POSIX_SPAWN_SETSCHEDULER` flags. + .. audit-event:: os.posix_spawn path,argv,env os.posix_spawn + .. versionadded:: 3.8 .. availability:: Unix. @@ -3684,6 +3688,8 @@ written in Python, such as a mail server's external command delivery program. for the *executable* file in the list of directories specified by the :envvar:`PATH` environment variable (in the same way as for ``execvp(3)``). + .. audit-event:: os.posix_spawn path,argv,env os.posix_spawnp + .. versionadded:: 3.8 .. availability:: See :func:`posix_spawn` documentation. @@ -3784,6 +3790,8 @@ written in Python, such as a mail server's external command delivery program. L = ['cp', 'index.html', '/dev/null'] os.spawnvpe(os.P_WAIT, 'cp', L, os.environ) + .. audit-event:: os.spawn mode,path,args,env os.spawnl + .. availability:: Unix, Windows. :func:`spawnlp`, :func:`spawnlpe`, :func:`spawnvp` and :func:`spawnvpe` are not available on Windows. :func:`spawnle` and :func:`spawnve` are not thread-safe on Windows; we advise you to use the @@ -3853,6 +3861,8 @@ written in Python, such as a mail server's external command delivery program. function is not resolved until this function is first called. If the function cannot be resolved, :exc:`NotImplementedError` will be raised. + .. audit-event:: os.startfile path,operation os.startfile + .. availability:: Windows. diff --git a/Doc/library/pty.rst b/Doc/library/pty.rst index 12268437d07..e85d2e239fd 100644 --- a/Doc/library/pty.rst +++ b/Doc/library/pty.rst @@ -69,6 +69,7 @@ The :mod:`pty` module defines the following functions: *select* throws an error on your platform when passed three empty lists. This is a bug, documented in `issue 26228 `_. + .. audit-event:: pty.spawn argv pty.spawn .. versionchanged:: 3.4 :func:`spawn` now returns the status value from :func:`os.waitpid` diff --git a/Lib/pty.py b/Lib/pty.py index e841f12f3ed..a32432041fa 100644 --- a/Lib/pty.py +++ b/Lib/pty.py @@ -8,6 +8,7 @@ from select import select import os +import sys import tty __all__ = ["openpty","fork","spawn"] @@ -151,6 +152,7 @@ def spawn(argv, master_read=_read, stdin_read=_read): """Create a spawned process.""" if type(argv) == type(''): argv = (argv,) + sys.audit('pty.spawn', argv) pid, master_fd = fork() if pid == CHILD: os.execlp(argv[0], *argv) diff --git a/Misc/NEWS.d/next/Security/2020-01-07-00-42-08.bpo-39184.fe7NgK.rst b/Misc/NEWS.d/next/Security/2020-01-07-00-42-08.bpo-39184.fe7NgK.rst new file mode 100644 index 00000000000..1ab5d4d70ee --- /dev/null +++ b/Misc/NEWS.d/next/Security/2020-01-07-00-42-08.bpo-39184.fe7NgK.rst @@ -0,0 +1 @@ +Add audit events to command execution functions in os and pty modules. \ No newline at end of file diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index b71eddf90b7..ec3da4fb2fc 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -5234,6 +5234,12 @@ os_execv_impl(PyObject *module, path_t *path, PyObject *argv) return NULL; } + if (PySys_Audit("os.exec", "OOO", path->object ? path->object : Py_None, + argv, Py_None) < 0) { + free_string_array(argvlist, argc); + return NULL; + } + _Py_BEGIN_SUPPRESS_IPH #ifdef HAVE_WEXECV _wexecv(path->wide, argvlist); @@ -5277,7 +5283,7 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env) if (!PyList_Check(argv) && !PyTuple_Check(argv)) { PyErr_SetString(PyExc_TypeError, "execve: argv must be a tuple or list"); - goto fail; + goto fail_0; } argc = PySequence_Size(argv); if (argc < 1) { @@ -5288,22 +5294,27 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env) if (!PyMapping_Check(env)) { PyErr_SetString(PyExc_TypeError, "execve: environment must be a mapping object"); - goto fail; + goto fail_0; } argvlist = parse_arglist(argv, &argc); if (argvlist == NULL) { - goto fail; + goto fail_0; } if (!argvlist[0][0]) { PyErr_SetString(PyExc_ValueError, "execve: argv first element cannot be empty"); - goto fail; + goto fail_0; } envlist = parse_envlist(env, &envc); if (envlist == NULL) - goto fail; + goto fail_0; + + if (PySys_Audit("os.exec", "OOO", path->object ? path->object : Py_None, + argv, env) < 0) { + goto fail_1; + } _Py_BEGIN_SUPPRESS_IPH #ifdef HAVE_FEXECVE @@ -5321,9 +5332,9 @@ os_execve_impl(PyObject *module, path_t *path, PyObject *argv, PyObject *env) /* If we get here it's definitely an error */ posix_path_error(path); - + fail_1: free_string_array(envlist, envc); - fail: + fail_0: if (argvlist) free_string_array(argvlist, argc); return NULL; @@ -5654,6 +5665,11 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a } attrp = &attr; + if (PySys_Audit("os.posix_spawn", "OOO", + path->object ? path->object : Py_None, argv, env) < 0) { + goto exit; + } + _Py_BEGIN_SUPPRESS_IPH #ifdef HAVE_POSIX_SPAWNP if (use_posix_spawnp) { @@ -5894,6 +5910,13 @@ os_spawnv_impl(PyObject *module, int mode, path_t *path, PyObject *argv) mode = _P_OVERLAY; #endif + if (PySys_Audit("os.spawn", "iOOO", mode, + path->object ? path->object : Py_None, argv, + Py_None) < 0) { + free_string_array(argvlist, argc); + return NULL; + } + Py_BEGIN_ALLOW_THREADS _Py_BEGIN_SUPPRESS_IPH #ifdef HAVE_WSPAWNV @@ -6003,6 +6026,11 @@ os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv, mode = _P_OVERLAY; #endif + if (PySys_Audit("os.spawn", "iOOO", mode, + path->object ? path->object : Py_None, argv, env) < 0) { + goto fail_2; + } + Py_BEGIN_ALLOW_THREADS _Py_BEGIN_SUPPRESS_IPH #ifdef HAVE_WSPAWNV @@ -6021,6 +6049,7 @@ os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv, else res = Py_BuildValue(_Py_PARSE_INTPTR, spawnval); + fail_2: while (--envc >= 0) PyMem_DEL(envlist[envc]); PyMem_DEL(envlist); @@ -11701,6 +11730,12 @@ os_startfile_impl(PyObject *module, path_t *filepath, "startfile not available on this platform"); } + if (PySys_Audit("os.startfile", "Ou", + filepath->object ? filepath->object : Py_None, + operation) < 0) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS rc = Py_ShellExecuteW((HWND)0, operation, filepath->wide, NULL, NULL, SW_SHOWNORMAL);