about summary refs log tree commit diff
path: root/nixos/modules/security
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/security')
-rw-r--r--nixos/modules/security/apparmor-suid.nix46
-rw-r--r--nixos/modules/security/apparmor.nix68
-rw-r--r--nixos/modules/security/ca.nix26
-rw-r--r--nixos/modules/security/pam.nix286
-rw-r--r--nixos/modules/security/pam_usb.nix41
-rw-r--r--nixos/modules/security/polkit.nix121
-rw-r--r--nixos/modules/security/rngd.nix37
-rw-r--r--nixos/modules/security/rtkit.nix39
-rw-r--r--nixos/modules/security/setuid-wrapper.c81
-rw-r--r--nixos/modules/security/setuid-wrappers.nix121
-rw-r--r--nixos/modules/security/sudo.nix90
11 files changed, 956 insertions, 0 deletions
diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
new file mode 100644
index 0000000000000..bc661164fdc29
--- /dev/null
+++ b/nixos/modules/security/apparmor-suid.nix
@@ -0,0 +1,46 @@
+{pkgs, config, ...}:
+let
+  cfg = config.security.apparmor;
+in
+with pkgs.lib;
+{
+
+  options.security.apparmor.confineSUIDApplications = mkOption {
+    default = true;
+    description = ''
+      Install AppArmor profiles for commonly-used SUID application
+      to mitigate potential privilege escalation attacks due to bugs
+      in such applications.
+
+      Currently available profiles: ping
+    '';
+  };
+
+  config = mkIf (cfg.confineSUIDApplications) {
+    security.apparmor.profiles = [ (pkgs.writeText "ping" ''
+      #include <tunables/global>
+      /var/setuid-wrappers/ping {
+        #include <abstractions/base>
+        #include <abstractions/consoles>
+        #include <abstractions/nameservice>
+
+        capability net_raw,
+        capability setuid,
+        network inet raw,
+
+        ${pkgs.glibc}/lib/*.so mr,
+        ${pkgs.libcap}/lib/libcap.so* mr,
+        ${pkgs.attr}/lib/libattr.so* mr,
+
+        ${pkgs.iputils}/bin/ping mixr,
+        /var/setuid-wrappers/ping.real r,
+
+        #/etc/modules.conf r,
+
+        ## Site-specific additions and overrides. See local/README for details.
+        ##include <local/bin.ping>
+      }
+    '') ];
+  };
+
+}
diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
new file mode 100644
index 0000000000000..d4aa0598dd3de
--- /dev/null
+++ b/nixos/modules/security/apparmor.nix
@@ -0,0 +1,68 @@
+{pkgs, config, ...}:
+
+let
+  cfg = config.security.apparmor;
+in
+
+with pkgs.lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    security.apparmor = {
+
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable AppArmor application security system. Enable only if
+          you want to further improve AppArmor.
+        '';
+      };
+
+      profiles = mkOption {
+        default = [];
+        merge = mergeListOption;
+        description = ''
+          List of file names of AppArmor profiles.
+        '';
+      };
+
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf (cfg.enable) {
+
+    assertions = [ { assertion = config.boot.kernelPackages.kernel.features ? apparmor
+                               && config.boot.kernelPackages.kernel.features.apparmor;
+                     message = "AppArmor is enabled, but the kernel doesn't have AppArmor support"; }
+                 ];
+
+    environment.systemPackages = [ pkgs.apparmor ];
+
+    systemd.services.apparmor = {
+      #wantedBy = [ "basic.target" ];
+      wantedBy = [ "local-fs.target" ];
+      path = [ pkgs.apparmor ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = "yes";
+        ExecStart = concatMapStrings (profile:
+          ''${pkgs.apparmor}/sbin/apparmor_parser -rKv -I ${pkgs.apparmor}/etc/apparmor.d/ "${profile}" ; ''
+        ) cfg.profiles;
+        ExecStop = concatMapStrings (profile:
+          ''${pkgs.apparmor}/sbin/apparmor_parser -Rv -I ${pkgs.apparmor}/etc/apparmor.d/ "${profile}" ; ''
+        ) cfg.profiles;
+      };
+
+    };
+
+  };
+
+}
diff --git a/nixos/modules/security/ca.nix b/nixos/modules/security/ca.nix
new file mode 100644
index 0000000000000..2e93fb36b4505
--- /dev/null
+++ b/nixos/modules/security/ca.nix
@@ -0,0 +1,26 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  config = {
+
+    environment.etc =
+      [ { source = "${pkgs.cacert}/etc/ca-bundle.crt";
+          target = "ssl/certs/ca-bundle.crt";
+        }
+
+        # Backward compatibility; may remove at some point.
+        { source = "${pkgs.cacert}/etc/ca-bundle.crt";
+          target = "ca-bundle.crt";
+        }
+      ];
+
+    environment.variables.OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt";
+    environment.variables.CURL_CA_BUNDLE = "/etc/ssl/certs/ca-bundle.crt";
+    environment.variables.GIT_SSL_CAINFO = "/etc/ssl/certs/ca-bundle.crt";
+
+  };
+
+}
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
new file mode 100644
index 0000000000000..3ef01ea2c1791
--- /dev/null
+++ b/nixos/modules/security/pam.nix
@@ -0,0 +1,286 @@
+# This module provides configuration for the PAM (Pluggable
+# Authentication Modules) system.
+
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) pam_krb5 pam_ccreds;
+
+  pam_ldap = if config.users.ldap.daemon.enable then pkgs.nss_pam_ldapd else pkgs.pam_ldap;
+
+  otherService = pkgs.writeText "other.pam"
+    ''
+      auth     required pam_warn.so
+      auth     required pam_deny.so
+      account  required pam_warn.so
+      account  required pam_deny.so
+      password required pam_warn.so
+      password required pam_deny.so
+      session  required pam_warn.so
+      session  required pam_deny.so
+    '';
+
+  # Create a limits.conf(5) file.
+  makeLimitsConf = limits:
+    pkgs.writeText "limits.conf"
+      (concatStringsSep "\n"
+           (map ({ domain, type, item, value }:
+                 concatStringsSep " " [ domain type item value ])
+                limits));
+
+  motd = pkgs.writeText "motd" config.users.motd;
+
+  makePAMService =
+    { name
+    , # If set, root doesn't need to authenticate (e.g. for the "chsh"
+      # service).
+      rootOK ? false
+    , # If set, user listed in /etc/pamusb.conf are able to log in with
+      # the associated usb key.
+      usbAuth ? config.security.pam.usb.enable
+    , # If set, OTPW system will be used (if ~/.otpw exists)
+      otpwAuth ? config.security.pam.enableOTPW
+    , # If set, the calling user's SSH agent is used to authenticate
+      # against the keys in the calling user's ~/.ssh/authorized_keys.
+      # This is useful for "sudo" on password-less remote systems.
+      sshAgentAuth ? false
+    , # If set, the service will register a new session with systemd's
+      # login manager.  If the service is running locally, this will
+      # give the user ownership of audio devices etc.
+      startSession ? false
+    , # Set the login uid of the process (/proc/self/loginuid) for
+      # auditing purposes.  The login uid is only set by "entry
+      # points" like login and sshd, not by commands like sudo.
+      setLoginUid ? startSession
+    , # Whether to forward XAuth keys between users.  Mostly useful
+      # for "su".
+      forwardXAuth ? false
+    , # Whether to allow logging into accounts that have no password
+      # set (i.e., have an empty password field in /etc/passwd or
+      # /etc/group).  This does not enable logging into disabled
+      # accounts (i.e., that have the password field set to `!').
+      # Note that regardless of what the pam_unix documentation says,
+      # accounts with hashed empty passwords are always allowed to log
+      # in.
+      allowNullPassword ? false
+    , # The limits, as per limits.conf(5).
+      limits ? config.security.pam.loginLimits
+    , # Whether to show the message of the day.
+      showMotd ? false
+    , # Whether to update /var/log/wtmp.
+      updateWtmp ? false
+    }:
+
+    { source = pkgs.writeText "${name}.pam"
+        # !!! TODO: move the LDAP stuff to the LDAP module, and the
+        # Samba stuff to the Samba module.  This requires that the PAM
+        # module provides the right hooks.
+        ''
+          # Account management.
+          account sufficient pam_unix.so
+          ${optionalString config.users.ldap.enable
+              "account sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.krb5.enable
+              "account sufficient ${pam_krb5}/lib/security/pam_krb5.so"}
+
+          # Authentication management.
+          ${optionalString rootOK
+              "auth sufficient pam_rootok.so"}
+          ${optionalString (config.security.pam.enableSSHAgentAuth && sshAgentAuth)
+              "auth sufficient ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so file=~/.ssh/authorized_keys:~/.ssh/authorized_keys2:/etc/ssh/authorized_keys.d/%u"}
+          ${optionalString usbAuth
+              "auth sufficient ${pkgs.pam_usb}/lib/security/pam_usb.so"}
+          auth sufficient pam_unix.so ${optionalString allowNullPassword "nullok"} likeauth
+          ${optionalString otpwAuth
+              "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
+          ${optionalString config.users.ldap.enable
+              "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
+          ${optionalString config.krb5.enable ''
+            auth [default=ignore success=1 service_err=reset] ${pam_krb5}/lib/security/pam_krb5.so use_first_pass
+            auth [default=die success=done] ${pam_ccreds}/lib/security/pam_ccreds.so action=validate use_first_pass
+            auth sufficient ${pam_ccreds}/lib/security/pam_ccreds.so action=store use_first_pass
+          ''}
+          auth required   pam_deny.so
+
+          # Password management.
+          password requisite pam_unix.so nullok sha512
+          ${optionalString config.users.ldap.enable
+              "password sufficient ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.krb5.enable
+              "password sufficient ${pam_krb5}/lib/security/pam_krb5.so use_first_pass"}
+          ${optionalString config.services.samba.syncPasswordsByPam
+              "password optional ${pkgs.samba}/lib/security/pam_smbpass.so nullok use_authtok try_first_pass"}
+
+          # Session management.
+          session required pam_unix.so
+          ${optionalString updateWtmp
+              "session required ${pkgs.pam}/lib/security/pam_lastlog.so silent"}
+          ${optionalString config.users.ldap.enable
+              "session optional ${pam_ldap}/lib/security/pam_ldap.so"}
+          ${optionalString config.krb5.enable
+              "session optional ${pam_krb5}/lib/security/pam_krb5.so"}
+          ${optionalString otpwAuth
+              "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"}
+          ${optionalString startSession
+              "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
+          ${optionalString setLoginUid
+              "session required pam_loginuid.so"}
+          ${optionalString forwardXAuth
+              "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"}
+          ${optionalString (limits != [])
+              "session required ${pkgs.pam}/lib/security/pam_limits.so conf=${makeLimitsConf limits}"}
+          ${optionalString (showMotd && config.users.motd != null)
+              "session optional ${pkgs.pam}/lib/security/pam_motd.so motd=${motd}"}
+        '';
+      target = "pam.d/${name}";
+    };
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    security.pam.loginLimits = mkOption {
+      default = [];
+      example =
+        [ { domain = "ftp";
+            type   = "hard";
+            item   = "nproc";
+            value  = "0";
+          }
+          { domain = "@student";
+            type   = "-";
+            item   = "maxlogins";
+            value  = "4";
+          }
+       ];
+
+     description =
+       '' Define resource limits that should apply to users or groups.
+          Each item in the list should be an attribute set with a
+          <varname>domain</varname>, <varname>type</varname>,
+          <varname>item</varname>, and <varname>value</varname>
+          attribute.  The syntax and semantics of these attributes
+          must be that described in the limits.conf(5) man page.
+       '';
+    };
+
+    security.pam.services = mkOption {
+      default = [];
+      example = [
+        { name = "chsh"; rootOK = true; }
+        { name = "login"; startSession = true; allowNullPassword = true;
+          limits = [
+            { domain = "ftp";
+              type   = "hard";
+              item   = "nproc";
+              value  = "0";
+            }
+          ];
+        }
+      ];
+
+      description =
+        ''
+          This option defines the PAM services.  A service typically
+          corresponds to a program that uses PAM,
+          e.g. <command>login</command> or <command>passwd</command>.
+          Each element of this list is an attribute set describing a
+          service.  The attribute <varname>name</varname> specifies
+          the name of the service.  The attribute
+          <varname>rootOK</varname> specifies whether the root user is
+          allowed to use this service without authentication.  The
+          attribute <varname>startSession</varname> specifies whether
+          systemd's PAM connector module should be used to start a new
+          session; for local sessions, this will give the user
+          ownership of devices such as audio and CD-ROM drives.  The
+          attribute <varname>forwardXAuth</varname> specifies whether
+          X authentication keys should be passed from the calling user
+          to the target user (e.g. for <command>su</command>).
+
+          The attribute <varname>limits</varname> defines resource limits
+          that should apply to users or groups for the service.  Each item in
+          the list should be an attribute set with a
+          <varname>domain</varname>, <varname>type</varname>,
+          <varname>item</varname>, and <varname>value</varname> attribute.
+          The syntax and semantics of these attributes must be that described
+          in the limits.conf(5) man page.
+        '';
+    };
+
+    security.pam.enableSSHAgentAuth = mkOption {
+      default = false;
+      description =
+        ''
+          Enable sudo logins if the user's SSH agent provides a key
+          present in <filename>~/.ssh/authorized_keys</filename>.
+          This allows machines to exclusively use SSH keys instead of
+          passwords.
+        '';
+    };
+
+    security.pam.enableOTPW = mkOption {
+      default = false;
+      description = ''
+        Enable the OTPW (one-time password) PAM module
+      '';
+    };
+
+    users.motd = mkOption {
+      default = null;
+      example = "Today is Sweetmorn, the 4th day of The Aftermath in the YOLD 3178.";
+      type = types.nullOr types.string;
+      description = "Message of the day shown to users when they log in.";
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = {
+
+    environment.systemPackages =
+      # Include the PAM modules in the system path mostly for the manpages.
+      [ pkgs.pam ]
+      ++ optional config.users.ldap.enable pam_ldap
+      ++ optionals config.krb5.enable [pam_krb5 pam_ccreds]
+      ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ];
+
+    environment.etc =
+      map makePAMService config.security.pam.services
+      ++ singleton
+        { source = otherService;
+          target = "pam.d/other";
+        };
+
+    security.setuidOwners = [ {
+      program = "unix_chkpwd";
+      source = "${pkgs.pam}/sbin/unix_chkpwd.orig";
+      owner = "root";
+      setuid = true;
+    } ];
+
+    security.pam.services =
+      # Most of these should be moved to specific modules.
+      [ { name = "cups"; }
+        { name = "ejabberd"; }
+        { name = "ftp"; }
+        { name = "i3lock"; }
+        { name = "lshd"; }
+        { name = "samba"; }
+        { name = "screen"; }
+        { name = "vlock"; }
+        { name = "xlock"; }
+        { name = "xscreensaver"; }
+      ];
+
+  };
+
+}
diff --git a/nixos/modules/security/pam_usb.nix b/nixos/modules/security/pam_usb.nix
new file mode 100644
index 0000000000000..1c2a6a05f2612
--- /dev/null
+++ b/nixos/modules/security/pam_usb.nix
@@ -0,0 +1,41 @@
+{config, pkgs, ...}:
+
+with pkgs.lib;
+
+let
+
+  inherit (pkgs) pam_usb;
+
+  cfg = config.security.pam.usb;
+
+  anyUsbAuth = any (attrByPath ["usbAuth"] false) config.security.pam.services;
+
+in
+
+{
+  options = {
+
+    security.pam.usb = {
+      enable = mkOption {
+        default = false;
+        description = ''
+          Enable USB login for all login system unless the service disabled
+          it.  For more information, visit <link
+          xlink:href="http://pamusb.org/doc/quickstart#setting_up" />.
+        '';
+      };
+
+    };
+
+  };
+
+  config = mkIf (cfg.enable || anyUsbAuth) {
+
+    # pmount need to have a set-uid bit to make pam_usb works in user
+    # environment. (like su, sudo)
+
+    security.setuidPrograms = [ "pmount" "pumount" ];
+    environment.systemPackages = [ pkgs.pmount ];
+
+  };
+}
diff --git a/nixos/modules/security/polkit.nix b/nixos/modules/security/polkit.nix
new file mode 100644
index 0000000000000..b9b32496a3687
--- /dev/null
+++ b/nixos/modules/security/polkit.nix
@@ -0,0 +1,121 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.security.polkit;
+
+in
+
+{
+
+  options = {
+
+    security.polkit.enable = mkOption {
+      default = true;
+      description = "Whether to enable PolKit.";
+    };
+
+    security.polkit.permissions = mkOption {
+      default = "";
+      example =
+        ''
+          [Disallow Users To Suspend]
+          Identity=unix-group:users
+          Action=org.freedesktop.upower.*
+          ResultAny=no
+          ResultInactive=no
+          ResultActive=no
+
+          [Allow Anybody To Eject Disks]
+          Identity=unix-user:*
+          Action=org.freedesktop.udisks.drive-eject
+          ResultAny=yes
+          ResultInactive=yes
+          ResultActive=yes
+
+          [Allow Alice To Mount Filesystems After Admin Authentication]
+          Identity=unix-user:alice
+          Action=org.freedesktop.udisks.filesystem-mount
+          ResultAny=auth_admin
+          ResultInactive=auth_admin
+          ResultActive=auth_admin
+        '';
+      description =
+        ''
+          Allows the default permissions of privileged actions to be overridden.
+        '';
+    };
+
+    security.polkit.adminIdentities = mkOption {
+      default = "unix-user:0;unix-group:wheel";
+      example = "";
+      description =
+        ''
+          Specifies which users are considered “administrators”, for those
+          actions that require the user to authenticate as an
+          administrator (i.e. have an <literal>auth_admin</literal>
+          value).  By default, this is the <literal>root</literal>
+          user and all users in the <literal>wheel</literal> group.
+        '';
+    };
+
+  };
+
+
+  config = mkIf cfg.enable {
+
+    environment.systemPackages = [ pkgs.polkit ];
+
+    # The polkit daemon reads action files
+    environment.pathsToLink = [ "/share/polkit-1/actions" ];
+
+    environment.etc =
+      [ # No idea what the "null backend" is, but it seems to need this.
+        { source = "${pkgs.polkit}/etc/polkit-1/nullbackend.conf.d";
+          target = "polkit-1/nullbackend.conf.d";
+        }
+
+        # This file determines what users are considered
+        # "administrators".
+        { source = pkgs.writeText "10-nixos.conf"
+            ''
+              [Configuration]
+              AdminIdentities=${cfg.adminIdentities}
+            '';
+          target = "polkit-1/localauthority.conf.d/10-nixos.conf";
+        }
+
+        { source = pkgs.writeText "org.nixos.pkla" cfg.permissions;
+          target = "polkit-1/localauthority/10-vendor.d/org.nixos.pkla";
+        }
+      ];
+
+    services.dbus.packages = [ pkgs.polkit ];
+
+    security.pam.services = [ { name = "polkit-1"; } ];
+
+    security.setuidPrograms = [ "pkexec" ];
+
+    security.setuidOwners = singleton
+      { program = "polkit-agent-helper-1";
+        owner = "root";
+        group = "root";
+        setuid = true;
+        source = "${pkgs.polkit}/libexec/polkit-1/polkit-agent-helper-1";
+      };
+
+    system.activationScripts.polkit =
+      ''
+        mkdir -p /var/lib/polkit-1/localauthority
+        chmod 700 /var/lib/polkit-1{/localauthority,}
+
+        # Force polkitd to be restarted so that it reloads its
+        # configuration.
+        ${pkgs.procps}/bin/pkill -INT -u root -x polkitd
+      '';
+
+  };
+
+}
diff --git a/nixos/modules/security/rngd.nix b/nixos/modules/security/rngd.nix
new file mode 100644
index 0000000000000..dd251fe69d310
--- /dev/null
+++ b/nixos/modules/security/rngd.nix
@@ -0,0 +1,37 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+  options = {
+    security.rngd.enable = mkOption {
+      default = true;
+      description = ''
+        Whether to enable the rng daemon, which adds entropy from
+        hardware sources of randomness to the kernel entropy pool when
+        available.
+      '';
+    };
+  };
+
+  config = mkIf config.security.rngd.enable {
+    services.udev.extraRules = ''
+      KERNEL=="random", TAG+="systemd"
+      SUBSYSTEM=="cpu", ENV{MODALIAS}=="x86cpu:*feature:*009E*", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
+      KERNEL=="hw_random", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
+      KERNEL=="tmp0", TAG+="systemd", ENV{SYSTEMD_WANTS}+="rngd.service"
+    '';
+
+    systemd.services.rngd = {
+      bindsTo = [ "dev-random.device" ];
+
+      after = [ "dev-random.device" ];
+
+      description = "Hardware RNG Entropy Gatherer Daemon";
+
+      serviceConfig.ExecStart = "${pkgs.rng_tools}/sbin/rngd -f";
+
+      restartTriggers = [ pkgs.rng_tools ];
+    };
+  };
+}
diff --git a/nixos/modules/security/rtkit.nix b/nixos/modules/security/rtkit.nix
new file mode 100644
index 0000000000000..e47e7baa2b84d
--- /dev/null
+++ b/nixos/modules/security/rtkit.nix
@@ -0,0 +1,39 @@
+# A module for ‘rtkit’, a DBus system service that hands out realtime
+# scheduling priority to processes that ask for it.
+
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  options = {
+
+    security.rtkit.enable = mkOption {
+      default = false;
+      description = ''
+        Whether to enable the RealtimeKit system service, which hands
+        out realtime scheduling priority to user processes on
+        demand. For example, the PulseAudio server uses this to
+        acquire realtime priority.
+      '';
+    };
+
+  };
+
+
+  config = mkIf config.security.rtkit.enable {
+
+    environment.systemPackages = [ pkgs.rtkit ];
+
+    services.dbus.packages = [ pkgs.rtkit ];
+
+    users.extraUsers = singleton
+      { name = "rtkit";
+        uid = config.ids.uids.rtkit;
+        description = "RealtimeKit daemon";
+      };
+
+  };
+
+}
diff --git a/nixos/modules/security/setuid-wrapper.c b/nixos/modules/security/setuid-wrapper.c
new file mode 100644
index 0000000000000..007ffbc34fe9d
--- /dev/null
+++ b/nixos/modules/security/setuid-wrapper.c
@@ -0,0 +1,81 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+/* Make sure assertions are not compiled out.  */
+#undef NDEBUG
+
+extern char **environ;
+
+static char * wrapperDir = WRAPPER_DIR;
+
+int main(int argc, char * * argv)
+{
+    char self[PATH_MAX];
+
+    int len = readlink("/proc/self/exe", self, sizeof(self) - 1);
+    assert (len > 0);
+    self[len] = 0;
+
+    /* Make sure that we are being executed from the right location,
+       i.e., `wrapperDir'.  This is to prevent someone from
+       creating hard link `X' from some other location, along with a
+       false `X.real' file, to allow arbitrary programs from being
+       executed setuid.  */
+    assert ((strncmp(self, wrapperDir, sizeof(wrapperDir)) == 0) &&
+	    (self[strlen(wrapperDir)] == '/'));
+
+    /* Make *really* *really* sure that we were executed as `self',
+       and not, say, as some other setuid program.  That is, our
+       effective uid/gid should match the uid/gid of `self'. */
+    //printf("%d %d\n", geteuid(), getegid());
+
+    struct stat st;
+    assert (lstat(self, &st) != -1);
+
+    //printf("%d %d\n", st.st_uid, st.st_gid);
+    
+    assert ((st.st_mode & S_ISUID) == 0 ||
+	    (st.st_uid == geteuid()));
+
+    assert ((st.st_mode & S_ISGID) == 0 ||
+	    st.st_gid == getegid());
+
+    /* And, of course, we shouldn't be writable. */
+    assert (!(st.st_mode & (S_IWGRP | S_IWOTH)));
+
+
+    /* Read the path of the real (wrapped) program from <self>.real. */
+    char realFN[PATH_MAX + 10];
+    int realFNSize = snprintf (realFN, sizeof(realFN), "%s.real", self);
+    assert (realFNSize < sizeof(realFN));
+
+    int fdSelf = open(realFN, O_RDONLY);
+    assert (fdSelf != -1);
+
+    char real[PATH_MAX];
+    len = read(fdSelf, real, PATH_MAX);
+    assert (len != -1);
+    assert (len < sizeof (real));
+    assert (len > 0);
+    real[len] = 0;
+
+    close(fdSelf);
+    
+    //printf("real = %s, len = %d\n", real, len);
+
+    execve(real, argv, environ);
+
+    fprintf(stderr, "%s: cannot run `%s': %s\n",
+        argv[0], real, strerror(errno));
+    
+    exit(1);
+}
diff --git a/nixos/modules/security/setuid-wrappers.nix b/nixos/modules/security/setuid-wrappers.nix
new file mode 100644
index 0000000000000..e75679e5ff69d
--- /dev/null
+++ b/nixos/modules/security/setuid-wrappers.nix
@@ -0,0 +1,121 @@
+{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 = ''
+        The Nix store cannot contain setuid/setgid programs directly.
+        For this reason, NixOS can automatically generate wrapper
+        programs that have the necessary privileges.  This option
+        lists the names of programs in the system environment for
+        which setuid root wrappers should be created.
+      '';
+    };
+
+    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 overridden 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
+        expect that <option>wrapperDir</option> is
+        <filename>/var/setuid-wrappers</filename>.
+      '';
+    };
+
+  };
+
+
+  ###### 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.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}
+        '';
+
+  };
+
+}
diff --git a/nixos/modules/security/sudo.nix b/nixos/modules/security/sudo.nix
new file mode 100644
index 0000000000000..cd548f4a4fe0c
--- /dev/null
+++ b/nixos/modules/security/sudo.nix
@@ -0,0 +1,90 @@
+{pkgs, config, ...}:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.security.sudo;
+
+  inherit (pkgs) sudo;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    security.sudo.enable = mkOption {
+      default = true;
+      description =
+        ''
+          Whether to enable the <command>sudo</command> command, which
+          allows non-root users to execute commands as root.
+        '';
+    };
+
+    security.sudo.wheelNeedsPassword = mkOption {
+      default = true;
+      description =
+        ''
+          Whether users of the <code>wheel</code> group can execute
+          commands as super user without entering a password.
+        '';
+      };
+
+    security.sudo.configFile = mkOption {
+      # Note: if syntax errors are detected in this file, the NixOS
+      # configuration will fail to build.
+      description =
+        ''
+          This string contains the contents of the
+          <filename>sudoers</filename> file.
+        '';
+    };
+  };
+
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+
+    security.sudo.configFile =
+      ''
+        # Don't edit this file. Set the NixOS option ‘security.sudo.configFile’ instead.
+
+        # Environment variables to keep for root and %wheel.
+        Defaults:root,%wheel env_keep+=LOCALE_ARCHIVE
+        Defaults:root,%wheel env_keep+=NIX_CONF_DIR
+        Defaults:root,%wheel env_keep+=NIX_PATH
+        Defaults:root,%wheel env_keep+=TERMINFO_DIRS
+
+        # Keep SSH_AUTH_SOCK so that pam_ssh_agent_auth.so can do its magic.
+        Defaults env_keep+=SSH_AUTH_SOCK
+
+        # "root" is allowed to do anything.
+        root        ALL=(ALL) SETENV: ALL
+
+        # Users in the "wheel" group can do anything.
+        %wheel      ALL=(ALL) ${if cfg.wheelNeedsPassword then "" else "NOPASSWD: ALL, "}SETENV: ALL
+      '';
+
+    security.setuidPrograms = [ "sudo" "sudoedit" ];
+
+    environment.systemPackages = [ sudo ];
+
+    security.pam.services = [ { name = "sudo"; sshAgentAuth = true; } ];
+
+    environment.etc = singleton
+      { source = pkgs.writeText "sudoers-in" cfg.configFile;
+          # Make sure that the sudoers file is syntactically valid.
+          # (currently disabled - NIXOS-66)
+          #"${pkgs.sudo}/sbin/visudo -f $src -c && cp $src $out";
+        target = "sudoers";
+        mode = "0440";
+      };
+
+  };
+
+}