#!/usr/bin/env python
# Lint as: python3
# -*- encoding: utf-8 -*-
"""Tests for service state checks."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

from absl import app

from grr_response_core.lib.parsers import linux_file_parser
from grr_response_server.check_lib import checks_test_lib
from grr.test_lib import test_lib


class LoginPolicyConfigurationTests(checks_test_lib.HostCheckTest):

  results = None

  @classmethod
  def setUpClass(cls):
    super(LoginPolicyConfigurationTests, cls).setUpClass()

    cls.LoadCheck("unix_login.yaml")

  def setUp(self, *args, **kwargs):
    super(LoginPolicyConfigurationTests, self).setUp(*args, **kwargs)
    if not LoginPolicyConfigurationTests.results:
      LoginPolicyConfigurationTests.results = self._GenResults()

  def _GenResults(self):
    parser = linux_file_parser.LinuxSystemPasswdParser()
    if self.results is None:
      login = {
          "/etc/passwd":
              """
              nopasswd:x:1000:1000::/home/nopasswd:/bin/bash
              md5:x:1001:1001::/home/md5:/bin/bash
              undying:x:1002:1002::/home/undying:/bin/bash
              disabled:x:1003:1003::/home/disabled:/bin/bash
              +nisuser:acr.7pt3dpA5s::::::/bin/zsh""",
          "/etc/shadow":
              """
              nopasswd::16000:0:365:7:::
              md5:$1$rootrootrootrootrootro:16000:0:365:7:::
              undying:$6$saltsalt${0}:16000:0:99999:7:::
              disabled:!:16000:0:99999:7:::""".format("r" * 86),
          "/etc/group":
              """
              nopasswd:x:1000:nopasswd
              +:::
              md5:x:1001:md5
              undying:x:1002:undying
              disabled:x:1003:disabled""",
          "/etc/gshadow":
              """
              nopasswd:::nopasswd
              md5:::md5
              undying:::undying
              disabled:::disabled"""
      }
      modes = {
          "/etc/passwd": {
              "st_mode": 0o100666
          },  # Bad write perm.
          "/etc/group": {
              "st_uid": 1
          },  # Bad owner.
          "/etc/shadow": {
              "st_mode": 0o100444
          },  # Bad read perm.
          "/etc/gshadow": {
              "st_gid": 1,
              "st_mode": 0o100400
          }
      }  # Bad group.
      host_data = self.GenFileData("LoginPolicyConfiguration", login, parser,
                                   modes)
      return self.RunChecks(host_data)

  def testPasswdHash(self):
    chk_id = "CIS-LOGIN-UNIX-HASH"
    sym = "Found: Insecure password hash method."
    found = ["password for +nisuser uses DES", "password for md5 uses MD5"]
    self.assertCheckDetectedAnom(chk_id, self.results, sym, found)

  def testEmptyPasswordCheck(self):
    chk_id = "CIS-LOGIN-UNIX-EMPTY"
    sym = "Found: Empty password string."
    found = ["password for nopasswd in SHADOW is empty."]
    self.assertCheckDetectedAnom(chk_id, self.results, sym, found)

  def testPasswdMaxageCheck(self):
    chk_id = "CIS-LOGIN-UNIX-SHADOW-MAXAGE"
    sym = "Found: Weak password aging settings in /etc/shadow."
    found = ["Weak password aging for undying in SHADOW."]
    self.assertCheckDetectedAnom(chk_id, self.results, sym, found)

  def testNisPasswordCheck(self):
    chk_id = "CIS-LOGIN-UNIX-NIS-MARKER"
    sym = "Found: NIS entries present."
    found = [
        "Group entry + is a NIS account marker.",
        "User account +nisuser is a NIS account marker."
    ]
    self.assertCheckDetectedAnom(chk_id, self.results, sym, found)

  def testDetectWeakPermissions(self):
    chk_id = "CIS-LOGIN-UNIX-WRITABLE"
    sym = "Found: System account files can be modified by non-privileged users."
    found = [
        "/etc/group: user: 1, group: 0, mode: -rw-r--r--",
        "/etc/passwd: user: 0, group: 0, mode: -rw-rw-rw-"
    ]
    self.assertCheckDetectedAnom(chk_id, self.results, sym, found)

  def testDetectShadowReadable(self):
    chk_id = "CIS-LOGIN-UNIX-SHADOW-PERMS"
    sym = "Found: Incorrect shadow file permissions."
    found = [
        "/etc/gshadow: user: 0, group: 1, mode: -r--------",
        "/etc/shadow: user: 0, group: 0, mode: -r--r--r--"
    ]
    self.assertCheckDetectedAnom(chk_id, self.results, sym, found)

  def testReportDetectedAnomalies(self):
    chk_id = "CIS-LOGIN-UNIX-INCONSISTENCIES"
    sym = "Found: System account entries are anomalous."
    found = [
        "Mismatched group and gshadow files.",
        "Mismatched passwd and shadow files."
    ]
    self.assertCheckDetectedAnom(chk_id, self.results, sym, found)


def main(argv):
  test_lib.main(argv)


if __name__ == "__main__":
  app.run(main)
