summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorGraham Christensen <graham@grahamc.com>2017-02-13 09:36:35 -0500
committerGitHub <noreply@github.com>2017-02-13 09:36:35 -0500
commit1d2548772ebfd52cd5ce3d37644dd90c88c57bff (patch)
treeb7c454907e150d3f90c906660074dde0972f2cff /nixos
parent909a1dd569b214660769f75a79827d9267786fd5 (diff)
parent96d767de621242a5df6de5db82a9b088d24ef606 (diff)
downloadnixlib-1d2548772ebfd52cd5ce3d37644dd90c88c57bff.tar
nixlib-1d2548772ebfd52cd5ce3d37644dd90c88c57bff.tar.gz
nixlib-1d2548772ebfd52cd5ce3d37644dd90c88c57bff.tar.bz2
nixlib-1d2548772ebfd52cd5ce3d37644dd90c88c57bff.tar.lz
nixlib-1d2548772ebfd52cd5ce3d37644dd90c88c57bff.tar.xz
nixlib-1d2548772ebfd52cd5ce3d37644dd90c88c57bff.tar.zst
nixlib-1d2548772ebfd52cd5ce3d37644dd90c88c57bff.zip
Merge pull request #22724 from grahamc/pam-oath-fixup
pam_oath: require OATH and pam_unix credentials to be valid
Diffstat (limited to 'nixos')
-rw-r--r--nixos/lib/test-driver/Machine.pm2
-rw-r--r--nixos/modules/security/pam.nix4
-rw-r--r--nixos/release.nix1
-rw-r--r--nixos/tests/pam-oath-login.nix126
4 files changed, 130 insertions, 3 deletions
diff --git a/nixos/lib/test-driver/Machine.pm b/nixos/lib/test-driver/Machine.pm
index 14c39e859bc1..85bc376f67fa 100644
--- a/nixos/lib/test-driver/Machine.pm
+++ b/nixos/lib/test-driver/Machine.pm
@@ -508,7 +508,7 @@ sub screenshot {
 sub getTTYText {
     my ($self, $tty) = @_;
 
-    my ($status, $out) = $self->execute("fold -w 80 /dev/vcs${tty}");
+    my ($status, $out) = $self->execute("fold -w\$(stty -F /dev/tty${tty} size | awk '{print \$2}') /dev/vcs${tty}");
     return $out;
 }
 
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 96e7c45d4963..67652fbd1e70 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -253,6 +253,8 @@ let
               "auth sufficient ${pkgs.pam_u2f}/lib/security/pam_u2f.so"}
           ${optionalString cfg.usbAuth
               "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
+          ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
+              "auth requisite ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
         '' +
           # Modules in this block require having the password set in PAM_AUTHTOK.
           # pam_unix is marked as 'sufficient' on NixOS which means nothing will run
@@ -271,8 +273,6 @@ let
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
           ${optionalString cfg.otpwAuth
               "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
-          ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
-              "auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
           ${optionalString use_ldap
               "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
           ${optionalString config.services.sssd.enable
diff --git a/nixos/release.nix b/nixos/release.nix
index c061b9801a0c..0f93deddf263 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -283,6 +283,7 @@ in rec {
   tests.leaps = callTest tests/leaps.nix { };
   tests.nsd = callTest tests/nsd.nix {};
   tests.openssh = callTest tests/openssh.nix {};
+  tests.pam-oath-login = callTest tests/pam-oath-login.nix {};
   #tests.panamax = hydraJob (import tests/panamax.nix { system = "x86_64-linux"; });
   tests.peerflix = callTest tests/peerflix.nix {};
   tests.postgresql = callTest tests/postgresql.nix {};
diff --git a/nixos/tests/pam-oath-login.nix b/nixos/tests/pam-oath-login.nix
new file mode 100644
index 000000000000..4364d6e354a6
--- /dev/null
+++ b/nixos/tests/pam-oath-login.nix
@@ -0,0 +1,126 @@
+import ./make-test.nix ({ pkgs, latestKernel ? false, ... }:
+
+let
+  oathSnakeoilSecret = "cdd4083ef8ff1fa9178c6d46bfb1a3";
+
+  # With HOTP mode the password is calculated based on a counter of
+  # how many passwords have been made. In this env, we'll always be on
+  # the 0th counter, so the password is static.
+  #
+  # Generated in nix-shell -p oathToolkit
+  # via: oathtool -v -d6 -w10 cdd4083ef8ff1fa9178c6d46bfb1a3
+  # and picking a the first 4:
+  oathSnakeOilPassword1 = "143349";
+  oathSnakeOilPassword2 = "801753";
+  oathSnakeOilPassword3 = "019933";
+  oathSnakeOilPassword4 = "403895";
+
+  alicePassword = "foobar";
+  # Generated via: mkpasswd -m sha-512 and passing in "foobar"
+  hashedAlicePassword = "$6$MsMrE1q.1HrCgTS$Vq2e/uILzYjSN836TobAyN9xh9oi7EmCmucnZID25qgPoibkw8qTCugiAPnn4eCGvn1A.7oEBFJaaGUaJsQQY.";
+
+in
+{
+  name = "pam-oath-login";
+
+  machine =
+    { config, pkgs, lib, ... }:
+    {
+      security.pam.oath = {
+        enable = true;
+      };
+
+      users.extraUsers.alice = {
+        isNormalUser = true;
+        name = "alice";
+        uid = 1000;
+        hashedPassword = hashedAlicePassword;
+        extraGroups = [ "wheel" ];
+        createHome = true;
+        home = "/home/alice";
+      };
+
+
+      systemd.services.setupOathSnakeoilFile = {
+        wantedBy = [ "default.target" ];
+        before = [ "default.target" ];
+        unitConfig = {
+          type = "oneshot";
+          RemainAfterExit = true;
+        };
+        script = ''
+          touch /etc/users.oath
+          chmod 600 /etc/users.oath
+          chown root /etc/users.oath
+          echo "HOTP/E/6 alice - ${oathSnakeoilSecret}" > /etc/users.oath
+        '';
+      };
+    };
+
+  testScript =
+    ''
+      $machine->waitForUnit('multi-user.target');
+      $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty1'");
+      $machine->screenshot("postboot");
+
+
+      subtest "Invalid password", sub {
+        $machine->fail("pgrep -f 'agetty.*tty2'");
+        $machine->sendKeys("alt-f2");
+        $machine->waitUntilSucceeds("[ \$(fgconsole) = 2 ]");
+        $machine->waitForUnit('getty@tty2.service');
+        $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty2'");
+
+        $machine->waitUntilTTYMatches(2, "login: ");
+        $machine->sendChars("alice\n");
+        $machine->waitUntilTTYMatches(2, "login: alice");
+        $machine->waitUntilSucceeds("pgrep login");
+
+        $machine->waitUntilTTYMatches(2, "One-time password");
+        $machine->sendChars("${oathSnakeOilPassword1}\n");
+        $machine->waitUntilTTYMatches(2, "Password: ");
+        $machine->sendChars("blorg\n");
+        $machine->waitUntilTTYMatches(2, "Login incorrect");
+      };
+
+      subtest "Invalid oath token", sub {
+        $machine->fail("pgrep -f 'agetty.*tty3'");
+        $machine->sendKeys("alt-f3");
+        $machine->waitUntilSucceeds("[ \$(fgconsole) = 3 ]");
+        $machine->waitForUnit('getty@tty3.service');
+        $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty3'");
+
+        $machine->waitUntilTTYMatches(3, "login: ");
+        $machine->sendChars("alice\n");
+        $machine->waitUntilTTYMatches(3, "login: alice");
+        $machine->waitUntilSucceeds("pgrep login");
+        $machine->waitUntilTTYMatches(3, "One-time password");
+        $machine->sendChars("000000\n");
+        $machine->waitUntilTTYMatches(3, "Login incorrect");
+        $machine->waitUntilTTYMatches(3, "login:");
+      };
+
+      subtest "Happy path (both passwords are mandatory to get us in)", sub {
+        $machine->fail("pgrep -f 'agetty.*tty4'");
+        $machine->sendKeys("alt-f4");
+        $machine->waitUntilSucceeds("[ \$(fgconsole) = 4 ]");
+        $machine->waitForUnit('getty@tty4.service');
+        $machine->waitUntilSucceeds("pgrep -f 'agetty.*tty4'");
+
+        $machine->waitUntilTTYMatches(4, "login: ");
+        $machine->sendChars("alice\n");
+        $machine->waitUntilTTYMatches(4, "login: alice");
+        $machine->waitUntilSucceeds("pgrep login");
+        $machine->waitUntilTTYMatches(4, "One-time password");
+        $machine->sendChars("${oathSnakeOilPassword2}\n");
+        $machine->waitUntilTTYMatches(4, "Password: ");
+        $machine->sendChars("${alicePassword}\n");
+
+        $machine->waitUntilSucceeds("pgrep -u alice bash");
+        $machine->sendChars("touch  done4\n");
+        $machine->waitForFile("/home/alice/done4");
+      };
+
+    '';
+
+})