git.iko.soy/typeable/nixpkgs
on master help sign in
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.

commit9e400a8b93cda6280be403c936e77cc006a28468 authorAlyssa Ross <hi@alyssa.is> dateTue 20 Apr 2021, 11:53 (6 years ago) parentd0a6d3b
1 file changed+14−8
nixos/modules/config/users-groups.nix+14−8
nixos/modules/config/users-groups.nix+14−8
@@ -6,6 +6,12 @@ let
66 ids = config.ids;
77 cfg = config.users;
88
9+ isPasswdCompatible = str: !(hasInfix ":" str || hasInfix "\n" str);
10+ passwdEntry = type: lib.types.addCheck type isPasswdCompatible // {
11+ name = "passwdEntry ${type.name}";
12+ description = "${type.description}, not containing newlines or colons";
13+ };
14+
915 # Check whether a password hash will allow login.
1016 allowsLogin = hash:
1117 hash == "" # login without password
@@ -54,7 +60,7 @@ let
5460 options = {
5561
5662 name = mkOption {
57− type = types.str;
63+ type = passwdEntry types.str;
5864 apply = x: assert (builtins.stringLength x < 32 || abort "Username '${x}' is longer than 31 characters which is not allowed!"); x;
5965 description = ''
6066 The name of the user account. If undefined, the name of the
@@ -63,7 +69,7 @@ let
6369 };
6470
6571 description = mkOption {
66− type = types.str;
72+ type = passwdEntry types.str;
6773 default = "";
6874 example = "Alice Q. User";
6975 description = ''
@@ -128,7 +134,7 @@ let
128134 };
129135
130136 home = mkOption {
131− type = types.path;
137+ type = passwdEntry types.path;
132138 default = "/var/empty";
133139 description = "The user's home directory.";
134140 };
@@ -157,7 +163,7 @@ let
157163 };
158164
159165 shell = mkOption {
160− type = types.nullOr (types.either types.shellPackage types.path);
166+ type = types.nullOr (types.either types.shellPackage (passwdEntry types.path));
161167 default = pkgs.shadow;
162168 defaultText = "pkgs.shadow";
163169 example = literalExample "pkgs.bashInteractive";
@@ -217,7 +223,7 @@ let
217223 };
218224
219225 hashedPassword = mkOption {
220− type = with types; nullOr str;
226+ type = with types; nullOr (passwdEntry str);
221227 default = null;
222228 description = ''
223229 Specifies the hashed password for the user.
@@ -251,7 +257,7 @@ let
251257 };
252258
253259 initialHashedPassword = mkOption {
254− type = with types; nullOr str;
260+ type = with types; nullOr (passwdEntry str);
255261 default = null;
256262 description = ''
257263 Specifies the initial hashed password for the user, i.e. the
@@ -323,7 +329,7 @@ let
323329 options = {
324330
325331 name = mkOption {
326− type = types.str;
332+ type = passwdEntry types.str;
327333 description = ''
328334 The name of the group. If undefined, the name of the attribute set
329335 will be used.
@@ -340,7 +346,7 @@ let
340346 };
341347
342348 members = mkOption {
343− type = with types; listOf str;
349+ type = with types; listOf (passwdEntry str);
344350 default = [];
345351 description = ''
346352 The user names of the group members, added to the