summaryrefslogtreecommitdiffstats
path: root/lass/3modules/usershadow.nix
blob: cb289096904aafee40ae2739d4d849b74545f590 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
{ config, lib, pkgs, ... }@args: with import <stockholm/lib>; let

  cfg = config.lass.usershadow;

  out = {
    options.lass.usershadow = api;
    config = lib.mkIf cfg.enable imp;
  };

  api = {
    enable = mkEnableOption "usershadow";
    pattern = mkOption {
      type = types.str;
      default = "/home/%/.shadow";
    };
    path = mkOption {
      type = types.str;
    };
  };

  imp = {
    environment.systemPackages = [ usershadow ];
    lass.usershadow.path = "${usershadow}";
    security.pam.services.sshd.text = ''
      account required pam_permit.so
      auth required pam_env.so envfile=${config.system.build.pamEnvironment}
      auth sufficient pam_exec.so quiet expose_authtok ${usershadow}/bin/verify_pam ${cfg.pattern}
      auth sufficient pam_unix.so likeauth try_first_pass
      session required pam_env.so envfile=${config.system.build.pamEnvironment}
      session required pam_permit.so
      session required pam_loginuid.so
    '';

    security.pam.services.dovecot2.text = ''
      auth required pam_exec.so expose_authtok ${usershadow}/bin/verify_pam ${cfg.pattern}
      auth required pam_permit.so
      account required pam_permit.so
      session required pam_permit.so
      session required pam_env.so envfile=${config.system.build.pamEnvironment}
    '';
  };

  usershadow = let {
    deps = [
      "pwstore-fast"
      "bytestring"
    ];
    body = pkgs.writeHaskellPackage "passwords" {
      executables.verify_pam = {
        extra-depends = deps;
        text = ''
          import Data.Monoid
          import System.IO
          import Data.Char (chr)
          import System.Environment (getEnv, getArgs)
          import Crypto.PasswordStore (verifyPasswordWith, pbkdf2)
          import qualified Data.ByteString.Char8 as BS8
          import System.Exit (exitFailure, exitSuccess)

          main :: IO ()
          main = do
            user <- getEnv "PAM_USER"
            shadowFilePattern <- head <$> getArgs
            let shadowFile = lhs <> user <> tail rhs
                (lhs, rhs) = span (/= '%') shadowFilePattern
            hash <- readFile shadowFile
            password <- takeWhile (/= (chr 0)) <$> hGetLine stdin
            let res = verifyPasswordWith pbkdf2 (2^) (BS8.pack password) (BS8.pack hash)
            if res then exitSuccess else exitFailure
        '';
      };
      executables.verify_arg = {
        extra-depends = deps;
        text = ''
          import Data.Monoid
          import System.Environment (getArgs)
          import Crypto.PasswordStore (verifyPasswordWith, pbkdf2)
          import qualified Data.ByteString.Char8 as BS8
          import System.Exit (exitFailure, exitSuccess)

          main :: IO ()
          main = do
            argsList <- getArgs
            let shadowFilePattern = argsList !! 0
            let user = argsList !! 1
            let password = argsList !! 2
            let shadowFile = lhs <> user <> tail rhs
                (lhs, rhs) = span (/= '%') shadowFilePattern
            hash <- readFile shadowFile
            let res = verifyPasswordWith pbkdf2 (2^) (BS8.pack password) (BS8.pack hash)
            if res then do (putStr "yes") else exitFailure
        '';
      };
      executables.passwd = {
        extra-depends = deps;
        text = ''
          import System.Environment (getEnv)
          import Crypto.PasswordStore (makePasswordWith, pbkdf2)
          import qualified Data.ByteString.Char8 as BS8
          import System.IO (stdin, stdout, hSetEcho, hFlush, putStr, putStrLn)
          import Control.Exception (bracket_)

          main :: IO ()
          main = do
            home <- getEnv "HOME"
            mb_password <- bracket_ (hSetEcho stdin False) (hSetEcho stdin True) $ do
              putStr "Enter new UNIX password: "
              hFlush stdout
              password <- BS8.hGetLine stdin
              putStrLn ""
              putStr "Retype new UNIX password: "
              hFlush stdout
              password2 <- BS8.hGetLine stdin
              return $ if password == password2
                then Just password
                else Nothing
            case mb_password of
              Just password -> do
                hash <- makePasswordWith pbkdf2 password 10
                BS8.writeFile (home ++ "/.shadow") hash
                putStrLn "passwd: all authentication tokens updated successfully."
              Nothing -> putStrLn "Sorry, passwords do not match"
        '';
      };
    };
  };

in out