1r"""
2A salt util for modifying the audit policies on the machine. This util is used
3by the ``win_auditpol`` and ``win_lgpo`` modules.
4
5Though this utility does not set group policy for auditing, it displays how all
6auditing configuration is applied on the machine, either set directly or via
7local or domain group policy.
8
9.. versionadded:: 2018.3.4
10.. versionadded:: 2019.2.1
11
12This util allows you to view and modify the audit settings as they are applied
13on the machine. The audit settings are broken down into nine categories:
14
15- Account Logon
16- Account Management
17- Detailed Tracking
18- DS Access
19- Logon/Logoff
20- Object Access
21- Policy Change
22- Privilege Use
23- System
24
25The ``get_settings`` function will return the subcategories for all nine of
26the above categories in one dictionary along with their auditing status.
27
28To modify a setting you only need to specify the subcategory name and the value
29you wish to set. Valid settings are:
30
31- No Auditing
32- Success
33- Failure
34- Success and Failure
35
36Usage:
37
38.. code-block:: python
39
40    import salt.utils.win_lgpo_auditpol
41
42    # Get current state of all audit settings
43    salt.utils.win_lgpo_auditpol.get_settings()
44
45    # Get the current state of all audit settings in the "Account Logon"
46    # category
47    salt.utils.win_lgpo_auditpol.get_settings(category="Account Logon")
48
49    # Get current state of the "Credential Validation" setting
50    salt.utils.win_lgpo_auditpol.get_setting(name='Credential Validation')
51
52    # Set the state of the "Credential Validation" setting to Success and
53    # Failure
54    salt.utils.win_lgpo_auditpol.set_setting(name='Credential Validation',
55                                             value='Success and Failure')
56
57    # Set the state of the "Credential Validation" setting to No Auditing
58    salt.utils.win_lgpo_auditpol.set_setting(name='Credential Validation',
59                                             value='No Auditing')
60"""
61
62import logging
63import re
64import tempfile
65
66import salt.modules.cmdmod
67import salt.utils.files
68import salt.utils.platform
69from salt.exceptions import CommandExecutionError
70
71log = logging.getLogger(__name__)
72__virtualname__ = "auditpol"
73
74categories = [
75    "Account Logon",
76    "Account Management",
77    "Detailed Tracking",
78    "DS Access",
79    "Logon/Logoff",
80    "Object Access",
81    "Policy Change",
82    "Privilege Use",
83    "System",
84]
85
86settings = {
87    "No Auditing": "/success:disable /failure:disable",
88    "Success": "/success:enable /failure:disable",
89    "Failure": "/success:disable /failure:enable",
90    "Success and Failure": "/success:enable /failure:enable",
91}
92
93
94# Although utils are often directly imported, it is also possible to use the
95# loader.
96def __virtual__():
97    """
98    Only load if on a Windows system
99    """
100    if not salt.utils.platform.is_windows():
101        return False, "This utility only available on Windows"
102
103    return __virtualname__
104
105
106def _auditpol_cmd(cmd):
107    """
108    Helper function for running the auditpol command
109
110    Args:
111        cmd (str): the auditpol command to run
112
113    Returns:
114        list: A list containing each line of the return (splitlines)
115
116    Raises:
117        CommandExecutionError: If the command encounters an error
118    """
119    ret = salt.modules.cmdmod.run_all(cmd="auditpol {}".format(cmd), python_shell=True)
120    if ret["retcode"] == 0:
121        return ret["stdout"].splitlines()
122
123    msg = "Error executing auditpol command: {}\n".format(cmd)
124    msg += "\n".join(ret["stdout"])
125    raise CommandExecutionError(msg)
126
127
128def get_settings(category="All"):
129    """
130    Get the current configuration for all audit settings specified in the
131    category
132
133    Args:
134        category (str):
135            One of the nine categories to return. Can also be ``All`` to return
136            the settings for all categories. Valid options are:
137
138            - Account Logon
139            - Account Management
140            - Detailed Tracking
141            - DS Access
142            - Logon/Logoff
143            - Object Access
144            - Policy Change
145            - Privilege Use
146            - System
147            - All
148
149            Default value is ``All``
150
151    Returns:
152        dict: A dictionary containing all subcategories for the specified
153            category along with their current configuration
154
155    Raises:
156        KeyError: On invalid category
157        CommandExecutionError: If an error is encountered retrieving the settings
158
159    Usage:
160
161    .. code-block:: python
162
163        import salt.utils.win_lgpo_auditpol
164
165        # Get current state of all audit settings
166        salt.utils.win_lgpo_auditpol.get_settings()
167
168        # Get the current state of all audit settings in the "Account Logon"
169        # category
170        salt.utils.win_lgpo_auditpol.get_settings(category="Account Logon")
171    """
172    # Parameter validation
173    if category.lower() in ["all", "*"]:
174        category = "*"
175    elif category.lower() not in [x.lower() for x in categories]:
176        raise KeyError('Invalid category: "{}"'.format(category))
177
178    cmd = '/get /category:"{}"'.format(category)
179    results = _auditpol_cmd(cmd)
180
181    ret = {}
182    # Skip the first 2 lines
183    for line in results[3:]:
184        if "  " in line.strip():
185            ret.update(dict(list(zip(*[iter(re.split(r"\s{2,}", line.strip()))] * 2))))
186    return ret
187
188
189def get_setting(name):
190    """
191    Get the current configuration for the named audit setting
192
193    Args:
194        name (str): The name of the setting to retrieve
195
196    Returns:
197        str: The current configuration for the named setting
198
199    Raises:
200        KeyError: On invalid setting name
201        CommandExecutionError: If an error is encountered retrieving the settings
202
203    Usage:
204
205    .. code-block:: python
206
207        import salt.utils.win_lgpo_auditpol
208
209        # Get current state of the "Credential Validation" setting
210        salt.utils.win_lgpo_auditpol.get_setting(name='Credential Validation')
211    """
212    current_settings = get_settings(category="All")
213    for setting in current_settings:
214        if name.lower() == setting.lower():
215            return current_settings[setting]
216    raise KeyError("Invalid name: {}".format(name))
217
218
219def _get_valid_names():
220    if "auditpol.valid_names" not in __context__:
221        settings = get_settings(category="All")
222        __context__["auditpol.valid_names"] = [k.lower() for k in settings]
223    return __context__["auditpol.valid_names"]
224
225
226def set_setting(name, value):
227    """
228    Set the configuration for the named audit setting
229
230    Args:
231
232        name (str):
233            The name of the setting to configure
234
235        value (str):
236            The configuration for the named value. Valid options are:
237
238            - No Auditing
239            - Success
240            - Failure
241            - Success and Failure
242
243    Returns:
244        bool: True if successful
245
246    Raises:
247        KeyError: On invalid ``name`` or ``value``
248        CommandExecutionError: If an error is encountered modifying the setting
249
250    Usage:
251
252    .. code-block:: python
253
254        import salt.utils.win_lgpo_auditpol
255
256        # Set the state of the "Credential Validation" setting to Success and
257        # Failure
258        salt.utils.win_lgpo_auditpol.set_setting(name='Credential Validation',
259                                                 value='Success and Failure')
260
261        # Set the state of the "Credential Validation" setting to No Auditing
262        salt.utils.win_lgpo_auditpol.set_setting(name='Credential Validation',
263                                                 value='No Auditing')
264    """
265    # Input validation
266    if name.lower() not in _get_valid_names():
267        raise KeyError("Invalid name: {}".format(name))
268    for setting in settings:
269        if value.lower() == setting.lower():
270            cmd = '/set /subcategory:"{}" {}'.format(name, settings[setting])
271            break
272    else:
273        raise KeyError("Invalid setting value: {}".format(value))
274
275    _auditpol_cmd(cmd)
276
277    return True
278
279
280def get_auditpol_dump():
281    """
282    Gets the contents of an auditpol /backup. Used by the LGPO module to get
283    fieldnames and GUIDs for Advanced Audit policies.
284
285    Returns:
286        list: A list of lines form the backup file
287
288    Usage:
289
290    .. code-block:: python
291
292        import salt.utils.win_lgpo_auditpol
293
294        dump = salt.utils.win_lgpo_auditpol.get_auditpol_dump()
295    """
296    # Just get a temporary file name
297    # NamedTemporaryFile will delete the file it creates by default on Windows
298    with tempfile.NamedTemporaryFile(suffix=".csv") as tmp_file:
299        csv_file = tmp_file.name
300
301    cmd = "/backup /file:{}".format(csv_file)
302    _auditpol_cmd(cmd)
303
304    with salt.utils.files.fopen(csv_file) as fp:
305        return fp.readlines()
306