1""" 2A few checks to make sure the environment is sane 3""" 4 5import errno 6import logging 7import os 8import re 9import socket 10import stat 11import sys 12 13import salt.defaults.exitcodes 14import salt.utils.files 15import salt.utils.path 16import salt.utils.platform 17import salt.utils.user 18from salt.exceptions import CommandExecutionError, SaltClientError, SaltSystemExit 19from salt.log import is_console_configured 20from salt.log.setup import LOG_LEVELS 21 22# Original Author: Jeff Schroeder <jeffschroeder@computer.org> 23 24 25try: 26 import win32file 27 import salt.utils.win_reg 28except ImportError: 29 import resource 30 31log = logging.getLogger(__name__) 32 33ROOT_DIR = "c:\\salt" if salt.utils.platform.is_windows() else "/" 34DEFAULT_SCHEMES = ["tcp://", "udp://", "file://"] 35 36 37def zmq_version(): 38 """ 39 ZeroMQ python bindings >= 2.1.9 are required 40 """ 41 try: 42 import zmq 43 except Exception: # pylint: disable=broad-except 44 # Return True for local mode 45 return True 46 ver = zmq.__version__ 47 # The last matched group can be None if the version 48 # is something like 3.1 and that will work properly 49 match = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?", ver) 50 51 # Fallthrough and hope for the best 52 if not match: 53 msg = "Using untested zmq python bindings version: '{}'".format(ver) 54 if is_console_configured(): 55 log.warning(msg) 56 else: 57 sys.stderr.write("WARNING {}\n".format(msg)) 58 return True 59 60 major, minor, point = match.groups() 61 62 if major.isdigit(): 63 major = int(major) 64 if minor.isdigit(): 65 minor = int(minor) 66 67 # point very well could be None 68 if point and point.isdigit(): 69 point = int(point) 70 71 if major == 2 and minor == 1: 72 # zmq 2.1dev could be built against a newer libzmq 73 if "dev" in ver and not point: 74 msg = "Using dev zmq module, please report unexpected results" 75 if is_console_configured(): 76 log.warning(msg) 77 else: 78 sys.stderr.write("WARNING: {}\n".format(msg)) 79 return True 80 elif point and point >= 9: 81 return True 82 elif major > 2 or (major == 2 and minor > 1): 83 return True 84 85 # If all else fails, gracefully croak and warn the user 86 log.critical("ZeroMQ python bindings >= 2.1.9 are required") 87 if "salt-master" in sys.argv[0]: 88 msg = ( 89 "The Salt Master is unstable using a ZeroMQ version " 90 "lower than 2.1.11 and requires this fix: http://lists.zeromq." 91 "org/pipermail/zeromq-dev/2011-June/012094.html" 92 ) 93 if is_console_configured(): 94 log.critical(msg) 95 else: 96 sys.stderr.write("CRITICAL {}\n".format(msg)) 97 return False 98 99 100def lookup_family(hostname): 101 """ 102 Lookup a hostname and determine its address family. The first address returned 103 will be AF_INET6 if the system is IPv6-enabled, and AF_INET otherwise. 104 """ 105 # If lookups fail, fall back to AF_INET sockets (and v4 addresses). 106 fallback = socket.AF_INET 107 try: 108 hostnames = socket.getaddrinfo( 109 hostname or None, None, socket.AF_UNSPEC, socket.SOCK_STREAM 110 ) 111 if not hostnames: 112 return fallback 113 h = hostnames[0] 114 return h[0] 115 except socket.gaierror: 116 return fallback 117 118 119def verify_socket(interface, pub_port, ret_port): 120 """ 121 Attempt to bind to the sockets to verify that they are available 122 """ 123 124 addr_family = lookup_family(interface) 125 for port in pub_port, ret_port: 126 sock = socket.socket(addr_family, socket.SOCK_STREAM) 127 try: 128 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 129 sock.bind((interface, int(port))) 130 except Exception as exc: # pylint: disable=broad-except 131 msg = "Unable to bind socket {}:{}".format(interface, port) 132 if exc.args: 133 msg = "{}, error: {}".format(msg, str(exc)) 134 else: 135 msg = "{}, this might not be a problem.".format(msg) 136 msg += "; Is there another salt-master running?" 137 if is_console_configured(): 138 log.warning(msg) 139 else: 140 sys.stderr.write("WARNING: {}\n".format(msg)) 141 return False 142 finally: 143 sock.close() 144 145 return True 146 147 148def verify_logs_filter(files): 149 to_verify = [] 150 for filename in files: 151 verify_file = True 152 for scheme in DEFAULT_SCHEMES: 153 if filename.startswith(scheme): 154 verify_file = False 155 break 156 if verify_file: 157 to_verify.append(filename) 158 return to_verify 159 160 161def verify_log_files(files, user): 162 """ 163 Verify the log files exist and are owned by the named user. Filenames that 164 begin with tcp:// and udp:// will be filtered out. Filenames that begin 165 with file:// are handled correctly 166 """ 167 return verify_files(verify_logs_filter(files), user) 168 169 170def _get_pwnam(user): 171 """ 172 Get the user from passwords database 173 """ 174 if salt.utils.platform.is_windows(): 175 return True 176 import pwd # after confirming not running Windows 177 178 try: 179 return pwd.getpwnam(user) 180 except KeyError: 181 msg = ( 182 "Failed to prepare the Salt environment for user {}. The user is not" 183 " available.".format(user) 184 ) 185 if is_console_configured(): 186 log.critical(msg) 187 else: 188 print(msg, file=sys.stderr, flush=True) 189 sys.exit(salt.defaults.exitcodes.EX_NOUSER) 190 191 192def verify_files(files, user): 193 """ 194 Verify that the named files exist and are owned by the named user 195 """ 196 if salt.utils.platform.is_windows(): 197 return True 198 199 # after confirming not running Windows 200 pwnam = _get_pwnam(user) 201 uid = pwnam[2] 202 203 for fn_ in files: 204 dirname = os.path.dirname(fn_) 205 try: 206 if dirname: 207 try: 208 os.makedirs(dirname) 209 except OSError as err: 210 if err.errno != errno.EEXIST: 211 raise 212 if not os.path.isfile(fn_): 213 with salt.utils.files.fopen(fn_, "w"): 214 pass 215 216 except OSError as err: 217 if os.path.isfile(dirname): 218 msg = "Failed to create path {}, is {} a file?".format(fn_, dirname) 219 raise SaltSystemExit(msg=msg) 220 if err.errno != errno.EACCES: 221 raise 222 msg = 'No permissions to access "{}", are you running as the correct user?'.format( 223 fn_ 224 ) 225 raise SaltSystemExit(msg=msg) 226 227 except OSError as err: # pylint: disable=duplicate-except 228 msg = 'Failed to create path "{}" - {}'.format(fn_, err) 229 raise SaltSystemExit(msg=msg) 230 231 stats = os.stat(fn_) 232 if uid != stats.st_uid: 233 try: 234 os.chown(fn_, uid, -1) 235 except OSError: 236 pass 237 return True 238 239 240def verify_env( 241 dirs, user, permissive=False, pki_dir="", skip_extra=False, root_dir=ROOT_DIR 242): 243 """ 244 Verify that the named directories are in place and that the environment 245 can shake the salt 246 """ 247 if salt.utils.platform.is_windows(): 248 return win_verify_env( 249 root_dir, dirs, permissive=permissive, skip_extra=skip_extra 250 ) 251 252 # after confirming not running Windows 253 pwnam = _get_pwnam(user) 254 uid = pwnam[2] 255 gid = pwnam[3] 256 groups = salt.utils.user.get_gid_list(user, include_default=False) 257 258 for dir_ in dirs: 259 if not dir_: 260 continue 261 if not os.path.isdir(dir_): 262 try: 263 with salt.utils.files.set_umask(0o022): 264 os.makedirs(dir_) 265 # If starting the process as root, chown the new dirs 266 if os.getuid() == 0: 267 os.chown(dir_, uid, gid) 268 except OSError as err: 269 msg = 'Failed to create directory path "{0}" - {1}\n' 270 sys.stderr.write(msg.format(dir_, err)) 271 sys.exit(err.errno) 272 273 mode = os.stat(dir_) 274 # If starting the process as root, chown the new dirs 275 if os.getuid() == 0: 276 fmode = os.stat(dir_) 277 if fmode.st_uid != uid or fmode.st_gid != gid: 278 if permissive and fmode.st_gid in groups: 279 # Allow the directory to be owned by any group root 280 # belongs to if we say it's ok to be permissive 281 pass 282 else: 283 # chown the file for the new user 284 os.chown(dir_, uid, gid) 285 for subdir in [a for a in os.listdir(dir_) if "jobs" not in a]: 286 fsubdir = os.path.join(dir_, subdir) 287 if "{}jobs".format(os.path.sep) in fsubdir: 288 continue 289 for root, dirs, files in salt.utils.path.os_walk(fsubdir): 290 for name in files: 291 if name.startswith("."): 292 continue 293 path = os.path.join(root, name) 294 try: 295 fmode = os.stat(path) 296 except OSError: 297 pass 298 if fmode.st_uid != uid or fmode.st_gid != gid: 299 if permissive and fmode.st_gid in groups: 300 pass 301 else: 302 # chown the file for the new user 303 os.chown(path, uid, gid) 304 for name in dirs: 305 path = os.path.join(root, name) 306 fmode = os.stat(path) 307 if fmode.st_uid != uid or fmode.st_gid != gid: 308 if permissive and fmode.st_gid in groups: 309 pass 310 else: 311 # chown the file for the new user 312 os.chown(path, uid, gid) 313 # Allow the pki dir to be 700 or 750, but nothing else. 314 # This prevents other users from writing out keys, while 315 # allowing the use-case of 3rd-party software (like django) 316 # to read in what it needs to integrate. 317 # 318 # If the permissions aren't correct, default to the more secure 700. 319 # If acls are enabled, the pki_dir needs to remain readable, this 320 # is still secure because the private keys are still only readable 321 # by the user running the master 322 if dir_ == pki_dir: 323 smode = stat.S_IMODE(mode.st_mode) 324 if smode != 448 and smode != 488: 325 if os.access(dir_, os.W_OK): 326 os.chmod(dir_, 448) 327 else: 328 msg = 'Unable to securely set the permissions of "{0}".' 329 msg = msg.format(dir_) 330 if is_console_configured(): 331 log.critical(msg) 332 else: 333 sys.stderr.write("CRITICAL: {}\n".format(msg)) 334 335 if skip_extra is False: 336 # Run the extra verification checks 337 zmq_version() 338 339 340def check_user(user): 341 """ 342 Check user and assign process uid/gid. 343 """ 344 if salt.utils.platform.is_windows(): 345 return True 346 if user == salt.utils.user.get_user(): 347 return True 348 349 # after confirming not running Windows 350 pwuser = _get_pwnam(user) 351 352 try: 353 if hasattr(os, "initgroups"): 354 os.initgroups(user, pwuser.pw_gid) # pylint: disable=minimum-python-version 355 else: 356 os.setgroups(salt.utils.user.get_gid_list(user, include_default=False)) 357 os.setgid(pwuser.pw_gid) 358 os.setuid(pwuser.pw_uid) 359 360 # We could just reset the whole environment but let's just override 361 # the variables we can get from pwuser 362 if "HOME" in os.environ: 363 os.environ["HOME"] = pwuser.pw_dir 364 365 if "SHELL" in os.environ: 366 os.environ["SHELL"] = pwuser.pw_shell 367 368 for envvar in ("USER", "LOGNAME"): 369 if envvar in os.environ: 370 os.environ[envvar] = pwuser.pw_name 371 372 except OSError: 373 msg = 'Salt configured to run as user "{}" but unable to switch.'.format(user) 374 if is_console_configured(): 375 log.critical(msg) 376 else: 377 sys.stderr.write("CRITICAL: {}\n".format(msg)) 378 return False 379 return True 380 381 382def list_path_traversal(path): 383 """ 384 Returns a full list of directories leading up to, and including, a path. 385 386 So list_path_traversal('/path/to/salt') would return: 387 ['/', '/path', '/path/to', '/path/to/salt'] 388 in that order. 389 390 This routine has been tested on Windows systems as well. 391 list_path_traversal('c:\\path\\to\\salt') on Windows would return: 392 ['c:\\', 'c:\\path', 'c:\\path\\to', 'c:\\path\\to\\salt'] 393 """ 394 out = [path] 395 (head, tail) = os.path.split(path) 396 if tail == "": 397 # paths with trailing separators will return an empty string 398 out = [head] 399 (head, tail) = os.path.split(head) 400 while head != out[0]: 401 # loop until head is the same two consecutive times 402 out.insert(0, head) 403 (head, tail) = os.path.split(head) 404 return out 405 406 407def check_path_traversal(path, user="root", skip_perm_errors=False): 408 """ 409 Walk from the root up to a directory and verify that the current 410 user has access to read each directory. This is used for making 411 sure a user can read all parent directories of the minion's key 412 before trying to go and generate a new key and raising an IOError 413 """ 414 for tpath in list_path_traversal(path): 415 if not os.access(tpath, os.R_OK): 416 msg = "Could not access {}.".format(tpath) 417 if not os.path.exists(tpath): 418 msg += " Path does not exist." 419 else: 420 current_user = salt.utils.user.get_user() 421 # Make the error message more intelligent based on how 422 # the user invokes salt-call or whatever other script. 423 if user != current_user: 424 msg += " Try running as user {}.".format(user) 425 else: 426 msg += " Please give {} read permissions.".format(user) 427 428 # We don't need to bail on config file permission errors 429 # if the CLI 430 # process is run with the -a flag 431 if skip_perm_errors: 432 return 433 # Propagate this exception up so there isn't a sys.exit() 434 # in the middle of code that could be imported elsewhere. 435 raise SaltClientError(msg) 436 437 438def check_max_open_files(opts): 439 """ 440 Check the number of max allowed open files and adjust if needed 441 """ 442 mof_c = opts.get("max_open_files", 100000) 443 if sys.platform.startswith("win"): 444 # Check the Windows API for more detail on this 445 # http://msdn.microsoft.com/en-us/library/xt874334(v=vs.71).aspx 446 # and the python binding http://timgolden.me.uk/pywin32-docs/win32file.html 447 mof_s = mof_h = win32file._getmaxstdio() 448 else: 449 mof_s, mof_h = resource.getrlimit(resource.RLIMIT_NOFILE) 450 451 accepted_keys_dir = os.path.join(opts.get("pki_dir"), "minions") 452 accepted_count = len(os.listdir(accepted_keys_dir)) 453 454 log.debug("This salt-master instance has accepted %s minion keys.", accepted_count) 455 456 level = logging.INFO 457 458 if (accepted_count * 4) <= mof_s: 459 # We check for the soft value of max open files here because that's the 460 # value the user chose to raise to. 461 # 462 # The number of accepted keys multiplied by four(4) is lower than the 463 # soft value, everything should be OK 464 return 465 466 msg = ( 467 "The number of accepted minion keys({}) should be lower than 1/4 " 468 "of the max open files soft setting({}). ".format(accepted_count, mof_s) 469 ) 470 471 if accepted_count >= mof_s: 472 # This should never occur, it might have already crashed 473 msg += "salt-master will crash pretty soon! " 474 level = logging.CRITICAL 475 elif (accepted_count * 2) >= mof_s: 476 # This is way too low, CRITICAL 477 level = logging.CRITICAL 478 elif (accepted_count * 3) >= mof_s: 479 level = logging.WARNING 480 # The accepted count is more than 3 time, WARN 481 elif (accepted_count * 4) >= mof_s: 482 level = logging.INFO 483 484 if mof_c < mof_h: 485 msg += ( 486 "According to the system's hard limit, there's still a " 487 "margin of {} to raise the salt's max_open_files " 488 "setting. ".format(mof_h - mof_c) 489 ) 490 491 msg += "Please consider raising this value." 492 log.log(level=level, msg=msg) 493 494 495def _realpath_darwin(path): 496 base = "" 497 for part in path.split(os.path.sep)[1:]: 498 if base != "": 499 if os.path.islink(os.path.sep.join([base, part])): 500 base = os.readlink(os.path.sep.join([base, part])) 501 else: 502 base = os.path.abspath(os.path.sep.join([base, part])) 503 else: 504 base = os.path.abspath(os.path.sep.join([base, part])) 505 return base 506 507 508def _realpath_windows(path): 509 base = "" 510 for part in path.split(os.path.sep): 511 if base != "": 512 try: 513 # Need to use salt.utils.path.readlink as it handles junctions 514 part = salt.utils.path.readlink(os.path.sep.join([base, part])) 515 base = os.path.abspath(part) 516 except OSError: 517 base = os.path.abspath(os.path.sep.join([base, part])) 518 else: 519 base = part 520 return base 521 522 523def _realpath(path): 524 """ 525 Cross platform realpath method. On Windows when python 3, this method 526 uses the os.readlink method to resolve any filesystem links. 527 All other platforms and version use ``os.path.realpath``. 528 """ 529 if salt.utils.platform.is_darwin(): 530 return _realpath_darwin(path) 531 elif salt.utils.platform.is_windows(): 532 return _realpath_windows(path) 533 return os.path.realpath(path) 534 535 536def clean_path(root, path, subdir=False): 537 """ 538 Accepts the root the path needs to be under and verifies that the path is 539 under said root. Pass in subdir=True if the path can result in a 540 subdirectory of the root instead of having to reside directly in the root 541 """ 542 real_root = _realpath(root) 543 if not os.path.isabs(real_root): 544 return "" 545 if not os.path.isabs(path): 546 path = os.path.join(root, path) 547 path = os.path.normpath(path) 548 real_path = _realpath(path) 549 if subdir: 550 if real_path.startswith(real_root): 551 return real_path 552 else: 553 if os.path.dirname(real_path) == os.path.normpath(real_root): 554 return real_path 555 return "" 556 557 558def valid_id(opts, id_): 559 """ 560 Returns if the passed id is valid 561 """ 562 try: 563 if any(x in id_ for x in ("/", "\\", "\0")): 564 return False 565 return bool(clean_path(opts["pki_dir"], id_)) 566 except (AttributeError, KeyError, TypeError, UnicodeDecodeError): 567 return False 568 569 570def safe_py_code(code): 571 """ 572 Check a string to see if it has any potentially unsafe routines which 573 could be executed via python, this routine is used to improve the 574 safety of modules suct as virtualenv 575 """ 576 bads = ("import", ";", "subprocess", "eval", "open", "file", "exec", "input") 577 for bad in bads: 578 if code.count(bad): 579 return False 580 return True 581 582 583def verify_log(opts): 584 """ 585 If an insecre logging configuration is found, show a warning 586 """ 587 level = LOG_LEVELS.get(str(opts.get("log_level")).lower(), logging.NOTSET) 588 589 if level < logging.INFO: 590 log.warning( 591 "Insecure logging configuration detected! Sensitive data may be logged." 592 ) 593 594 595def win_verify_env(path, dirs, permissive=False, pki_dir="", skip_extra=False): 596 """ 597 Verify that the named directories are in place and that the environment 598 can shake the salt 599 """ 600 import salt.utils.win_functions 601 import salt.utils.win_dacl 602 import salt.utils.path 603 604 # Make sure the file_roots is not set to something unsafe since permissions 605 # on that directory are reset 606 607 # `salt.utils.path.safe_path` will consider anything inside `C:\Windows` to 608 # be unsafe. In some instances the test suite uses 609 # `C:\Windows\Temp\salt-tests-tmpdir\rootdir` as the file_roots. So, we need 610 # to consider anything in `C:\Windows\Temp` to be safe 611 system_root = os.environ.get("SystemRoot", r"C:\Windows") 612 allow_path = "\\".join([system_root, "TEMP"]) 613 if not salt.utils.path.safe_path(path=path, allow_path=allow_path): 614 raise CommandExecutionError( 615 "`file_roots` set to a possibly unsafe location: {}".format(path) 616 ) 617 618 # Create the root path directory if missing 619 if not os.path.isdir(path): 620 os.makedirs(path) 621 622 current_user = salt.utils.win_functions.get_current_user() 623 # Set permissions to the registry key 624 if salt.utils.win_functions.is_admin(current_user): 625 reg_path = "HKLM\\SOFTWARE\\Salt Project\\salt" 626 if not salt.utils.win_reg.key_exists( 627 hive="HKLM", key="SOFTWARE\\Salt Project\\salt" 628 ): 629 salt.utils.win_reg.set_value( 630 hive="HKLM", key="SOFTWARE\\Salt Project\\salt" 631 ) 632 try: 633 # Make the Administrators group owner 634 # Use the SID to be locale agnostic 635 salt.utils.win_dacl.set_owner( 636 obj_name=reg_path, principal="S-1-5-32-544", obj_type="registry" 637 ) 638 except CommandExecutionError: 639 msg = 'Unable to securely set the owner of "{}".'.format(reg_path) 640 if is_console_configured(): 641 log.critical(msg) 642 else: 643 sys.stderr.write("CRITICAL: {}\n".format(msg)) 644 645 try: 646 # Get a clean dacl by not passing an obj_name 647 dacl = salt.utils.win_dacl.dacl(obj_type="registry") 648 649 # Add aces to the dacl, use the GUID (locale non-specific) 650 # Administrators Group 651 dacl.add_ace( 652 principal="S-1-5-32-544", 653 access_mode="grant", 654 permissions="full_control", 655 applies_to="this_key_subkeys", 656 ) 657 # System 658 dacl.add_ace( 659 principal="S-1-5-18", 660 access_mode="grant", 661 permissions="full_control", 662 applies_to="this_key_subkeys", 663 ) 664 # Owner 665 dacl.add_ace( 666 principal="S-1-3-4", 667 access_mode="grant", 668 permissions="full_control", 669 applies_to="this_key_subkeys", 670 ) 671 672 # Save the dacl to the object 673 dacl.save(obj_name=reg_path, protected=True) 674 675 except CommandExecutionError: 676 msg = 'Unable to securely set the permissions of "{}"'.format(reg_path) 677 if is_console_configured(): 678 log.critical(msg) 679 else: 680 sys.stderr.write("CRITICAL: {}\n".format(msg)) 681 682 # Set permissions to the root path directory 683 if salt.utils.win_functions.is_admin(current_user): 684 try: 685 # Make the Administrators group owner 686 # Use the SID to be locale agnostic 687 salt.utils.win_dacl.set_owner(obj_name=path, principal="S-1-5-32-544") 688 689 except CommandExecutionError: 690 msg = "Unable to securely set the owner of {}".format(path) 691 if is_console_configured(): 692 log.critical(msg) 693 else: 694 sys.stderr.write("CRITICAL: {}\n".format(msg)) 695 696 if not permissive: 697 try: 698 # Get a clean dacl by not passing an obj_name 699 dacl = salt.utils.win_dacl.dacl() 700 701 # Add aces to the dacl, use the GUID (locale non-specific) 702 # Administrators Group 703 dacl.add_ace( 704 principal="S-1-5-32-544", 705 access_mode="grant", 706 permissions="full_control", 707 applies_to="this_folder_subfolders_files", 708 ) 709 # System 710 dacl.add_ace( 711 principal="S-1-5-18", 712 access_mode="grant", 713 permissions="full_control", 714 applies_to="this_folder_subfolders_files", 715 ) 716 # Owner 717 dacl.add_ace( 718 principal="S-1-3-4", 719 access_mode="grant", 720 permissions="full_control", 721 applies_to="this_folder_subfolders_files", 722 ) 723 724 # Save the dacl to the object 725 dacl.save(obj_name=path, protected=True) 726 727 except CommandExecutionError: 728 msg = 'Unable to securely set the permissions of "{}".'.format(path) 729 if is_console_configured(): 730 log.critical(msg) 731 else: 732 sys.stderr.write("CRITICAL: {}\n".format(msg)) 733 734 # Create the directories 735 for dir_ in dirs: 736 if not dir_: 737 continue 738 if not os.path.isdir(dir_): 739 try: 740 os.makedirs(dir_) 741 except OSError as err: 742 msg = 'Failed to create directory path "{0}" - {1}\n' 743 sys.stderr.write(msg.format(dir_, err)) 744 sys.exit(err.errno) 745 746 # The PKI dir gets its own permissions 747 if dir_ == pki_dir: 748 try: 749 # Make Administrators group the owner 750 salt.utils.win_dacl.set_owner(obj_name=path, principal="S-1-5-32-544") 751 752 # Give Admins, System and Owner permissions 753 # Get a clean dacl by not passing an obj_name 754 dacl = salt.utils.win_dacl.dacl() 755 756 # Add aces to the dacl, use the GUID (locale non-specific) 757 # Administrators Group 758 dacl.add_ace( 759 principal="S-1-5-32-544", 760 access_mode="grant", 761 permissions="full_control", 762 applies_to="this_folder_subfolders_files", 763 ) 764 # System 765 dacl.add_ace( 766 principal="S-1-5-18", 767 access_mode="grant", 768 permissions="full_control", 769 applies_to="this_folder_subfolders_files", 770 ) 771 # Owner 772 dacl.add_ace( 773 principal="S-1-3-4", 774 access_mode="grant", 775 permissions="full_control", 776 applies_to="this_folder_subfolders_files", 777 ) 778 779 # Save the dacl to the object 780 dacl.save(obj_name=dir_, protected=True) 781 782 except CommandExecutionError: 783 msg = 'Unable to securely set the permissions of "{0}".' 784 msg = msg.format(dir_) 785 if is_console_configured(): 786 log.critical(msg) 787 else: 788 sys.stderr.write("CRITICAL: {}\n".format(msg)) 789 790 if skip_extra is False: 791 # Run the extra verification checks 792 zmq_version() 793