summary refs log tree commit diff
path: root/modules/security/setuid-wrappers.nix
blob: 027032a7c27241a52c941c0267a84ce181e15790 (plain) (blame)
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
{pkgs, config, ...}:

with pkgs.lib;

let

  inherit (config.security) wrapperDir;

  setuidWrapper = pkgs.stdenv.mkDerivation {
    name = "setuid-wrapper";
    buildCommand = ''
      ensureDir $out/bin
      gcc -Wall -O2 -DWRAPPER_DIR=\"${wrapperDir}\" \
          ${./setuid-wrapper.c} -o $out/bin/setuid-wrapper
      strip -s $out/bin/setuid-wrapper
    '';
  };

in

{

  ###### interface

  options = {

    security.setuidPrograms = mkOption {
      default = [];
      description = ''
        Only the programs from system path listed here will be made
        setuid root (through a wrapper program).
      '';
    };

    security.extraSetuidPrograms = mkOption {
      default = [];
      example = ["fusermount"];
      description = ''
        This option lists additional programs that must be made setuid
        root. Obsolete, use setuidPrograms instead.
      '';
    };

    security.setuidOwners = mkOption {
      default = [];
      example =
        [ { program = "sendmail";
            owner = "nobody";
            group = "postdrop";
            setuid = false;
            setgid = true;
          }
        ];
      description = ''
        This option allows the ownership and permissions on the setuid
        wrappers for specific programs to be overriden from the
        default (setuid root, but not setgid root).
      '';
    };

    security.wrapperDir = mkOption {
      default = "/var/setuid-wrappers";
      description = ''
        This option defines the path to the setuid wrappers.  It
        should generally not be overriden. Some packages in nixpkgs rely on
        wrapperDir == /var/setuid-wrappers
      '';
    };

  };


  ###### implementation

  config = {

    security.setuidPrograms =
      [ "fusermount" "wodim" "cdrdao" "growisofs" ];

    system.activationScripts.setuid =
      let
        setuidPrograms =
          (map (x: { program = x; owner = "root"; group = "root"; setuid = true; })
            (config.security.setuidPrograms ++
             config.security.extraSetuidPrograms))
          ++ config.security.setuidOwners;

        makeSetuidWrapper =
          { program
          , source ? ""
          , owner ? "nobody"
          , group ? "nogroup"
          , setuid ? false
          , setgid ? false
          , permissions ? "u+rx,g+x,o+x"
          }:

          ''
            source=${if source != "" then source else "$(PATH=$SETUID_PATH type -tP ${program})"}
            if test -z "$source"; then
                # If we can't find the program, fall back to the
                # system profile.
                source=/nix/var/nix/profiles/default/bin/${program}
            fi
            
            cp ${setuidWrapper}/bin/setuid-wrapper ${wrapperDir}/${program}
            echo -n "$source" > ${wrapperDir}/${program}.real
            chmod 0000 ${wrapperDir}/${program} # to prevent races
            chown ${owner}.${group} ${wrapperDir}/${program}
            chmod "u${if setuid then "+" else "-"}s,g${if setgid then "+" else "-"}s,${permissions}" ${wrapperDir}/${program}
          '';

      in stringAfter [ "users" ] 
        ''
          # Look in the system path and in the default profile for
          # programs to be wrapped.
          SETUID_PATH=${config.system.path}/bin:${config.system.path}/sbin

          if test -d ${wrapperDir}; then rm -f ${wrapperDir}/*; fi # */
          mkdir -p ${wrapperDir}

          ${concatMapStrings makeSetuidWrapper setuidPrograms}
        '';

  };
  
}