nixos/users-groups: check format of passwd entries

Things will get quite broken if an /etc/passwd entry contains a
colon (which terminates a field), or a newline (which terminates a
record).  I know because I just accidentally made a user whose home
directory path contained a newline!

So let's make sure that can't happen.
This commit is contained in:
Alyssa Ross 2021-04-20 11:53:24 +00:00
parent d0a6d3b834
commit 9e400a8b93

View file

@ -6,6 +6,12 @@ let
ids = config.ids; ids = config.ids;
cfg = config.users; cfg = config.users;
isPasswdCompatible = str: !(hasInfix ":" str || hasInfix "\n" str);
passwdEntry = type: lib.types.addCheck type isPasswdCompatible // {
name = "passwdEntry ${type.name}";
description = "${type.description}, not containing newlines or colons";
};
# Check whether a password hash will allow login. # Check whether a password hash will allow login.
allowsLogin = hash: allowsLogin = hash:
hash == "" # login without password hash == "" # login without password
@ -54,7 +60,7 @@ let
options = { options = {
name = mkOption { name = mkOption {
type = types.str; type = passwdEntry types.str;
apply = x: assert (builtins.stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x; apply = x: assert (builtins.stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x;
description = '' description = ''
The name of the user account. If undefined, the name of the The name of the user account. If undefined, the name of the
@ -63,7 +69,7 @@ let
}; };
description = mkOption { description = mkOption {
type = types.str; type = passwdEntry types.str;
default = ""; default = "";
example = "Alice Q. User"; example = "Alice Q. User";
description = '' description = ''
@ -128,7 +134,7 @@ let
}; };
home = mkOption { home = mkOption {
type = types.path; type = passwdEntry types.path;
default = "/var/empty"; default = "/var/empty";
description = "The user's home directory."; description = "The user's home directory.";
}; };
@ -157,7 +163,7 @@ let
}; };
shell = mkOption { shell = mkOption {
type = types.nullOr (types.either types.shellPackage types.path); type = types.nullOr (types.either types.shellPackage (passwdEntry types.path));
default = pkgs.shadow; default = pkgs.shadow;
defaultText = "pkgs.shadow"; defaultText = "pkgs.shadow";
example = literalExample "pkgs.bashInteractive"; example = literalExample "pkgs.bashInteractive";
@ -217,7 +223,7 @@ let
}; };
hashedPassword = mkOption { hashedPassword = mkOption {
type = with types; nullOr str; type = with types; nullOr (passwdEntry str);
default = null; default = null;
description = '' description = ''
Specifies the hashed password for the user. Specifies the hashed password for the user.
@ -251,7 +257,7 @@ let
}; };
initialHashedPassword = mkOption { initialHashedPassword = mkOption {
type = with types; nullOr str; type = with types; nullOr (passwdEntry str);
default = null; default = null;
description = '' description = ''
Specifies the initial hashed password for the user, i.e. the Specifies the initial hashed password for the user, i.e. the
@ -323,7 +329,7 @@ let
options = { options = {
name = mkOption { name = mkOption {
type = types.str; type = passwdEntry types.str;
description = '' description = ''
The name of the group. If undefined, the name of the attribute set The name of the group. If undefined, the name of the attribute set
will be used. will be used.
@ -340,7 +346,7 @@ let
}; };
members = mkOption { members = mkOption {
type = with types; listOf str; type = with types; listOf (passwdEntry str);
default = []; default = [];
description = '' description = ''
The user names of the group members, added to the The user names of the group members, added to the