def test_code_injection2(self): self.setup() self.addCleanup(self.cleanup) # failure cases out_file = self.get_random_filename() exit_code = ext_utils.run_command_and_write_stdout_to_file( "echo hello; echo world", out_file) self.assertNotEqual(0, exit_code, "exit code != 0") out_file = self.get_random_filename() exit_code = ext_utils.run_command_and_write_stdout_to_file( ["echo hello; echo world"], out_file) self.assertNotEqual(0, exit_code, "exit code != 0") # success case out_file = self.get_random_filename() exit_code = ext_utils.run_command_and_write_stdout_to_file( ["echo", "hello", ";", "echo", "world"], out_file) self.assertEqual(0, exit_code, "exit code == 0") file_contents = ext_utils.get_file_contents(out_file) self.assertEqual("hello ; echo world\n", file_contents, "unexpected output") out_file = self.get_random_filename() exit_code = ext_utils.run_command_and_write_stdout_to_file( ["echo", "hello", "world"], out_file) self.assertEqual(0, exit_code, "exit code == 0") file_contents = ext_utils.get_file_contents(out_file) self.assertEqual("hello world\n", file_contents, "unexpected output")
def create_account(self, user, password, expiration, thumbprint): """ Create a user account, with 'user', 'password', 'expiration', ssh keys and sudo permissions. Returns None if successful, error string on failure. """ userentry = None try: userentry = pwd.getpwnam(user) except (EnvironmentError, KeyError): pass uidmin = None try: uidmin = int(ext_utils.get_line_starting_with("UID_MIN", "/etc/login.defs").split()[1]) except (ValueError, KeyError, AttributeError, EnvironmentError): pass if uidmin is None: uidmin = 100 if userentry is not None and userentry[2] < uidmin and userentry[2] != self.CORE_UID: logger.error( "CreateAccount: " + user + " is a system user. Will not set password.") return "Failed to set password for system user: "******" (0x06)." if userentry is None: command = ['useradd', '--create-home', '--password', '*', user] if expiration is not None: command += ['--expiredate', expiration.split('.')[0]] if ext_utils.run(command): logger.error("Failed to create user account: " + user) return "Failed to create user account: " + user + " (0x07)." else: logger.log("CreateAccount: " + user + " already exists. Will update password.") if password is not None: self.change_password(user, password) try: if password is None: ext_utils.set_file_contents("/etc/sudoers.d/waagent", user + " ALL = (ALL) NOPASSWD: ALL\n") else: ext_utils.set_file_contents("/etc/sudoers.d/waagent", user + " ALL = (ALL) ALL\n") os.chmod("/etc/sudoers.d/waagent", 0o440) except EnvironmentError: logger.error("CreateAccount: Failed to configure sudo access for user.") return "Failed to configure sudo privileges (0x08)." home = self.get_home() if thumbprint is not None: ssh_dir = home + "/" + user + "/.ssh" ext_utils.create_dir(ssh_dir, user, 0o700) pub = ssh_dir + "/id_rsa.pub" prv = ssh_dir + "/id_rsa" ext_utils.run_command_and_write_stdout_to_file(['ssh-keygen', '-y', '-f', thumbprint + '.prv'], pub) ext_utils.set_file_contents(prv, ext_utils.get_file_contents(thumbprint + ".prv")) for f in [pub, prv]: os.chmod(f, 0o600) ext_utils.change_owner(f, user) ext_utils.set_file_contents(ssh_dir + "/authorized_keys", ext_utils.get_file_contents(pub)) ext_utils.change_owner(ssh_dir + "/authorized_keys", user) logger.log("Created user account: " + user) return None
def try_parse_context(self): self._context = HandlerContext(self._short_name) handler_env = None config = None ctxt = None code = 0 # get the HandlerEnvironment.json. According to the extension handler spec, it is always in the ./ directory self.log('cwd is ' + os.path.realpath(os.path.curdir)) handler_env_file = './HandlerEnvironment.json' if not os.path.isfile(handler_env_file): self.error("Unable to locate " + handler_env_file) return None ctxt = ext_utils.get_file_contents(handler_env_file) if ctxt == None: self.error("Unable to read " + handler_env_file) try: handler_env = json.loads(ctxt) except: pass if handler_env == None: self.log("JSON error processing " + handler_env_file) return None if type(handler_env) == list: handler_env = handler_env[0] self._context._name = handler_env['name'] self._context._version = str(handler_env['version']) self._context._config_dir = handler_env['handlerEnvironment']['configFolder'] self._context._log_dir = handler_env['handlerEnvironment']['logFolder'] self._context._log_file = os.path.join(handler_env['handlerEnvironment']['logFolder'], self._logFileName) self._change_log_file() self._context._status_dir = handler_env['handlerEnvironment']['statusFolder'] self._context._heartbeat_file = handler_env['handlerEnvironment']['heartbeatFile'] self._context._seq_no = self._get_current_seq_no(self._context._config_dir) if self._context._seq_no < 0: self.error("Unable to locate a .settings file!") return None self._context._seq_no = str(self._context._seq_no) self.log('sequence number is ' + self._context._seq_no) self._context._status_file = os.path.join(self._context._status_dir, self._context._seq_no + '.status') self._context._settings_file = os.path.join(self._context._config_dir, self._context._seq_no + '.settings') self.log("setting file path is" + self._context._settings_file) ctxt = None ctxt = ext_utils.get_file_contents(self._context._settings_file) if ctxt == None: error_msg = 'Unable to read ' + self._context._settings_file + '. ' self.error(error_msg) return None self.log("JSON config: " + HandlerUtility.redact_protected_settings(ctxt)) self._context._config = self._parse_config(ctxt) return self._context
def get_my_distro(config): if 'FreeBSD' in platform.system(): return FreeBSDDistro(config) if os.path.isfile(constants.os_release): os_name = ext_utils.get_line_starting_with("NAME", constants.os_release) elif os.path.isfile(constants.system_release): os_name = ext_utils.get_file_contents(constants.system_release) else: return GenericDistro(config) if os_name is not None: if re.search("fedora", os_name, re.IGNORECASE): # Fedora return FedoraDistro(config) if re.search("red\s?hat", os_name, re.IGNORECASE): # Red Hat return RedhatDistro(config) if re.search("centos", os_name, re.IGNORECASE): # CentOS return CentOSDistro(config) if re.search("coreos", os_name, re.IGNORECASE): # CoreOs return CoreOSDistro(config) if re.search("freebsd", os_name, re.IGNORECASE): # FreeBSD return FreeBSDDistro(config) return GenericDistro(config)
def _get_most_recent_seq(self): if (os.path.isfile('mrseq')): seq = ext_utils.get_file_contents('mrseq') if (seq): return int(seq) return -1
def _get_other_sudoers(user_name): sudoers_file = '/etc/sudoers.d/waagent' if not os.path.isfile(sudoers_file): return None sudoers = ext_utils.get_file_contents(sudoers_file).split("\n") pattern = '^{0}\s'.format(user_name) sudoers = list(filter(lambda x: re.match(pattern, x) is None, sudoers)) return sudoers
def _reset_sshd_config(sshd_file_path): ssh_default_config_filename = _get_default_ssh_config_filename() ssh_default_config_file_path = os.path.join(os.getcwd(), 'resources', ssh_default_config_filename) if not (os.path.exists(ssh_default_config_file_path)): ssh_default_config_file_path = os.path.join(os.getcwd(), 'resources', 'default') # handle CoreOS differently if isinstance(MyDistro, dist_utils.CoreOSDistro): # Parse sshd port from ssh_default_config_file_path sshd_port = 22 regex = re.compile(r"^Port\s+(\d+)", re.VERBOSE) with open(ssh_default_config_file_path) as f: for line in f: match = regex.match(line) if match: sshd_port = match.group(1) break # Prepare cloud init config for coreos-cloudinit f = tempfile.NamedTemporaryFile(delete=False) f.close() cfg_tempfile = f.name cfg_content = "#cloud-config\n\n" # Overwrite /etc/ssh/sshd_config cfg_content += "write_files:\n" cfg_content += " - path: {0}\n".format(sshd_file_path) cfg_content += " permissions: 0600\n" cfg_content += " owner: root:root\n" cfg_content += " content: |\n" for line in ext_utils.get_file_contents( ssh_default_config_file_path).split('\n'): cfg_content += " {0}\n".format(line) # Change the sshd port in /etc/systemd/system/sshd.socket cfg_content += "\ncoreos:\n" cfg_content += " units:\n" cfg_content += " - name: sshd.socket\n" cfg_content += " command: restart\n" cfg_content += " content: |\n" cfg_content += " [Socket]\n" cfg_content += " ListenStream={0}\n".format(sshd_port) cfg_content += " Accept=yes\n" ext_utils.set_file_contents(cfg_tempfile, cfg_content) ext_utils.run(['coreos-cloudinit', '-from-file', cfg_tempfile], chk_err=False) os.remove(cfg_tempfile) else: shutil.copyfile(ssh_default_config_file_path, sshd_file_path) MyDistro.restart_ssh_service()
def _forcibly_reset_chap(hutil): name = "ChallengeResponseAuthentication" config = ext_utils.get_file_contents(SshdConfigPath).split("\n") for i in range(0, len(config)): if config[i].startswith(name) and "no" in config[i].lower(): ext_utils.add_extension_event(name=hutil.get_name(), op="sshd", is_success=True, message="ChallengeResponseAuthentication no") return ext_utils.add_extension_event(name=hutil.get_name(), op="sshd", is_success=True, message="ChallengeResponseAuthentication yes") _backup_sshd_config(SshdConfigPath) _set_sshd_config(config, name, "no") ext_utils.replace_file_with_contents_atomic(SshdConfigPath, "\n".join(config)) MyDistro.restart_ssh_service()
def __init__(self, wala_config_file): self.values = dict() if not os.path.isfile(wala_config_file): raise ValueError("Missing configuration in {0}".format(wala_config_file)) try: for line in ext_utils.get_file_contents(wala_config_file).split('\n'): if not line.startswith("#") and "=" in line: parts = line.split()[0].split('=') value = parts[1].strip("\" ") if value != "None": self.values[parts[0]] = value else: self.values[parts[0]] = None # when get_file_contents returns none except AttributeError: logger.error("Unable to parse {0}".format(wala_config_file)) raise return
def _get_default_ssh_config_filename(): if os.path.isfile(constants.os_release): os_name = ext_utils.get_line_starting_with("NAME", constants.os_release) elif os.path.isfile(constants.system_release): os_name = ext_utils.get_file_contents(constants.system_release) else: return "default" if os_name is not None: # the default ssh config files are present in # /var/lib/waagent/Microsoft.OSTCExtensions.VMAccessForLinux-<version>/resources/ if re.search("centos", os_name, re.IGNORECASE): return "centos_default" if re.search("debian", os_name, re.IGNORECASE): return "debian_default" if re.search("fedora", os_name, re.IGNORECASE): return "fedora_default" if re.search("red\s?hat", os_name, re.IGNORECASE): return "redhat_default" if re.search("suse", os_name, re.IGNORECASE): return "SuSE_default" if re.search("ubuntu", os_name, re.IGNORECASE): return "ubuntu_default" return "default"
def scrub_settings_file(self): content = ext_utils.get_file_contents(self._context._settings_file) redacted = HandlerUtility.redact_protected_settings(content) ext_utils.set_file_contents(self._context._settings_file, redacted)
def test_ovf_env_parse_minimalxml(self): current_dir = path.dirname(path.abspath(__file__)) ovf_xml = ext_utils.get_file_contents( path.join(current_dir, 'ovf-env-empty.xml')) ovf_env = ovf_utils.OvfEnv.parse(ovf_xml, config) self.assertIsNone(ovf_env, "ovf_env should be null")
def _allow_password_auth(): config = ext_utils.get_file_contents(SshdConfigPath).split("\n") _set_sshd_config(config, "PasswordAuthentication", "yes") ext_utils.replace_file_with_contents_atomic(SshdConfigPath, "\n".join(config))
def _set_user_account_pub_key(protect_settings, hutil): ovf_xml = None ovf_env = None try: ovf_xml = ext_utils.get_file_contents('/var/lib/waagent/ovf-env.xml') ovf_env = ovf_utils.OvfEnv.parse(ovf_xml, Configuration) except (EnvironmentError, ValueError, KeyError, AttributeError): pass if ovf_xml is None or ovf_env is None: # default ovf_env with empty data ovf_env = ovf_utils.OvfEnv() logger.log("could not load ovf-env.xml") # user name must be provided if set ssh key or password if not protect_settings or 'username' not in protect_settings: return user_name = protect_settings['username'] user_pass = protect_settings.get('password') cert_txt = protect_settings.get('ssh_key') expiration = protect_settings.get('expiration') no_convert = False if not user_pass and not cert_txt and not ovf_env.SshPublicKeys: raise Exception("No password or ssh_key is specified.") if user_pass is not None and len(user_pass) == 0: user_pass = None hutil.log("empty passwords are not allowed, ignoring password reset") # Reset user account and password, password could be empty sudoers = _get_other_sudoers(user_name) error_string = MyDistro.create_account(user_name, user_pass, expiration, None) _save_other_sudoers(sudoers) if error_string is not None: err_msg = "Failed to create the account or set the password" ext_utils.add_extension_event(name=hutil.get_name(), op=constants.WALAEventOperation.Enable, is_success=False, message="(02101)" + err_msg) raise Exception(err_msg + " with " + error_string) hutil.log("Succeeded in creating the account or setting the password.") # Allow password authentication if user_pass is provided if user_pass is not None: ext_utils.add_extension_event(name=hutil.get_name(), op="scenario", is_success=True, message="create-user-with-password") _allow_password_auth() # Reset ssh key with the new public key passed in or reuse old public key. if cert_txt: if cert_txt and cert_txt.strip().lower().startswith("ssh-rsa"): no_convert = True try: pub_path = os.path.join('/home/', user_name, '.ssh', 'authorized_keys') ovf_env.UserName = user_name if no_convert: if cert_txt: pub_path = ovf_env.prepare_dir(pub_path, MyDistro) final_cert_txt = cert_txt if not cert_txt.endswith("\n"): final_cert_txt = final_cert_txt + "\n" ext_utils.append_file_contents(pub_path, final_cert_txt) MyDistro.set_se_linux_context( pub_path, 'unconfined_u:object_r:ssh_home_t:s0') ext_utils.change_owner(pub_path, user_name) ext_utils.add_extension_event(name=hutil.get_name(), op="scenario", is_success=True, message="create-user") hutil.log("Succeeded in resetting ssh_key.") else: err_msg = "Failed to reset ssh key because the cert content is empty." ext_utils.add_extension_event( name=hutil.get_name(), op=constants.WALAEventOperation.Enable, is_success=False, message="(02100)" + err_msg) else: # do the certificate conversion # we support PKCS8 certificates besides ssh-rsa public keys _save_cert_str_as_file(cert_txt, 'temp.crt') pub_path = ovf_env.prepare_dir(pub_path, MyDistro) retcode = ext_utils.run_command_and_write_stdout_to_file([ constants.Openssl, 'x509', '-in', 'temp.crt', '-noout', '-pubkey' ], "temp.pub") if retcode > 0: raise Exception("Failed to generate public key file.") MyDistro.ssh_deploy_public_key('temp.pub', pub_path) os.remove('temp.pub') os.remove('temp.crt') ext_utils.add_extension_event(name=hutil.get_name(), op="scenario", is_success=True, message="create-user") hutil.log("Succeeded in resetting ssh_key.") except Exception as e: hutil.log(str(e)) ext_utils.add_extension_event( name=hutil.get_name(), op=constants.WALAEventOperation.Enable, is_success=False, message="(02100)Failed to reset ssh key.") raise e
def test_encode(self): contents = eu.get_file_contents('mock_sshd_config') encoded_contents = eu.encode_for_writing_to_file(contents) known_non_ascii_character = b"%c" % encoded_contents[2353] self.assertEqual(known_non_ascii_character, b'\x9d')