Skip to content

Conversation

alejandro-colomar
Copy link
Collaborator

@alejandro-colomar alejandro-colomar commented Jun 6, 2025

@hallyn

This addresses several off-by-one issues found in <src/newusers.c>, while discussing #1155 (comment) during development/review of #1155.


Revisions:

v2
  • Fix bug reported by CodeQL.
$ git rd 
1:  36e1d6da = 1:  36e1d6da src/newusers.c: Fix off-by-one benign bug in array declaration
2:  c2509ac5 = 2:  c2509ac5 src/newusers.c: Move break to loop condition
-:  -------- > 3:  0b02de7f src/newusers.c: Do not read memory that we have not written to
3:  6e671be2 = 4:  25e619ed src/newusers.c: Check for 6 fields without needing an array of 7
4:  825e4a0b = 5:  d8d180fc src/newusers.c: Remove bogus comment
v3
  • Change commit message: I'm not so sure that the bug is so safe. If that byte is nonzero, we copy the pointer, and the we do an unbound read eventually, as if it were a string. That can be quite bad.
$ git rd 
1:  36e1d6da = 1:  36e1d6da src/newusers.c: Fix off-by-one benign bug in array declaration
2:  c2509ac5 = 2:  c2509ac5 src/newusers.c: Move break to loop condition
3:  0b02de7f ! 3:  b0b1f0c4 src/newusers.c: Do not read memory that we have not written to
    @@ Metadata
      ## Commit message ##
         src/newusers.c: Do not read memory that we have not written to
     
    -    This issue could be bad.  The good thing is that the read is limited to
    -    one byte.
    -
         Link: <https://github.com/shadow-maint/shadow/pull/1265>
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
4:  25e619ed = 4:  44b18c53 src/newusers.c: Check for 6 fields without needing an array of 7
5:  d8d180fc = 5:  8d09dfb2 src/newusers.c: Remove bogus comment
v3b
  • Add Fixes tags.
$ git rd 
1:  36e1d6da ! 1:  11aefe7d src/newusers.c: Fix off-by-one benign bug in array declaration
    @@ Commit message
                         216:    fields[0]
                         225:    fields[0]
     
    +    Fixes: 45c6603cc86c (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")
         Link: <https://github.com/shadow-maint/shadow/pull/1155#discussion_r2132261260>
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
2:  c2509ac5 = 2:  628b8c1a src/newusers.c: Move break to loop condition
3:  b0b1f0c4 ! 3:  a6d51c30 src/newusers.c: Do not read memory that we have not written to
    @@ Metadata
      ## Commit message ##
         src/newusers.c: Do not read memory that we have not written to
     
    +    Fixes: 45c6603cc86c (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")
         Link: <https://github.com/shadow-maint/shadow/pull/1265>
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
4:  44b18c53 = 4:  16ef3158 src/newusers.c: Check for 6 fields without needing an array of 7
5:  8d09dfb2 ! 5:  1dc42b09 src/newusers.c: Remove bogus comment
    @@ Commit message
         must be 7 fields.  Something is clearly bogus here.  Let's assume the
         code is correct, and the comment is wrong.
     
    +    Fixes: 45c6603cc86c (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
      ## src/newusers.c ##

@alejandro-colomar alejandro-colomar requested a review from hallyn June 6, 2025 14:24
@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Jun 6, 2025

Regarding the check for 6 fields, I don't know why the code checks for 6 fields.

The manual page says the fields are:

pw_name:pw_passwd:pw_uid:pw_gid:pw_gecos:pw_dir:pw_shell

@alejandro-colomar
Copy link
Collaborator Author

git-blame(1) says the source code is checking for 6 fields since

$ git ref2 45c6603cc
45c6603cc86c (2007-10-07, 2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")

@alejandro-colomar alejandro-colomar marked this pull request as ready for review June 6, 2025 14:32
@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Jun 6, 2025

Ughhhh, CodeQL has caught an important bug here.

shadow/src/newusers.c

Lines 1235 to 1237 in b9323a0

if (!streq(fields[6], "")) {
newpw.pw_shell = fields[6];
}

This is reading a buffer that we have never initialized. :(

alejandro-colomar added a commit to alejandro-colomar/shadow that referenced this pull request Jun 6, 2025
This issue could be bad.  The good thing is that the read is limited to
one byte.

Link: <shadow-maint#1265>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
@alejandro-colomar alejandro-colomar force-pushed the fields678 branch 2 times, most recently from d8d180f to 8d09dfb Compare June 6, 2025 14:43
alejandro-colomar added a commit to alejandro-colomar/shadow that referenced this pull request Jun 6, 2025
Link: <shadow-maint#1265>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
@alejandro-colomar
Copy link
Collaborator Author

Cc: @thesamesam , @ikerexxe , @jubalh

This array is only ever used as an array of 7.

alx@devuan:/srv/alx/src/shadow/shadow/master$ sed_rm_ccomments()
{
        perl -p -e 's%/\*.*?\*/%%g' \
        |sed -E '\%/\*%, \%\*/% {\%(\*/|/\*)%!d}' \
        |sed -E '\%/\*% {s%/\*.*%%; n; s%.*\*/%%;}' \
        |sed -E '\%/\*% {s%/\*.*%%; n; s%.*\*/%%;}' \
        |sed 's%//.*%%';
}
alx@devuan:/srv/alx/src/shadow/shadow/master$ cat src/newusers.c | grepc main | sed_rm_ccomments | grep -nT 'n\?fields\(\[.*]\)\?'
                  4:		char *fields[8];
                  5:		int nfields;
                 59:			for (cp = buf, nfields = 0; nfields < 7; nfields++) {
                 60:				fields[nfields] = strsep(&cp, ":");
                 64:			if (nfields != 6) {
                 72:			pw = pw_locate (fields[0]);
                 74:			if (NULL == pw && getpwnam(fields[0]) != NULL) {
                 77:					 Prog, fields[0]);
                 81:			if (NULL == pw && get_user_id(fields[2], &uid) != 0) {
                 91:			    && (add_group (fields[0], fields[3], &gid, uid) != 0)) {
                101:			    && (add_user (fields[0], uid, gid) != 0)) {
                110:			pw = pw_locate (fields[0]);
                114:				         Prog, line, fields[0], pw_dbname ());
                132:			usernames[nusers-1] = xstrdup(fields[0]);
                133:			passwords[nusers-1] = xstrdup(fields[1]);
                135:			if (add_passwd (&newpw, fields[1]) != 0) {
                141:			if (!streq(fields[4], "")) {
                142:				newpw.pw_gecos = fields[4];
                145:			if (!streq(fields[5], "")) {
                146:				newpw.pw_dir = fields[5];
                149:			if (!streq(fields[6], "")) {
                150:				newpw.pw_shell = fields[6];
                153:			if (   !streq(fields[5], "")
                195:			if (is_sub_uid && want_subuids() && !local_sub_uid_assigned(fields[0])) {
                205:				if (sub_uid_add(fields[0], sub_uid_start, sub_uid_count) == 0)
                216:			if (is_sub_gid && want_subgids() && !local_sub_gid_assigned(fields[0])) {
                225:				if (sub_gid_add(fields[0], sub_gid_start, sub_gid_count) == 0) {
alx@devuan:/srv/alx/src/shadow/shadow/master$ cat src/newusers.c | grepc main | sed_rm_ccomments | grep -nTo 'n\?fields\(\[.*]\)\?'
                  4:	fields[8]
                  5:	nfields
                 59:	nfields
                 59:	nfields
                 59:	nfields
                 60:	fields[nfields]
                 64:	nfields
                 72:	fields[0]
                 74:	fields[0]
                 77:	fields[0]
                 81:	fields[2]
                 91:	fields[0], fields[3]
                101:	fields[0]
                110:	fields[0]
                114:	fields[0]
                132:	fields[0]
                133:	fields[1]
                135:	fields[1]
                141:	fields[4]
                142:	fields[4]
                145:	fields[5]
                146:	fields[5]
                149:	fields[6]
                150:	fields[6]
                153:	fields[5]
                195:	fields[0]
                205:	fields[0]
                216:	fields[0]
                225:	fields[0]

Fixes: 45c6603 (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")
Link: <shadow-maint#1155 (comment)>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Fixes: 45c6603 (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")
Link: <shadow-maint#1265>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
The code is checking for 6 fields, while the comment is saying there
must be 7 fields.  Something is clearly bogus here.  Let's assume the
code is correct, and the comment is wrong.

Fixes: 45c6603 (2007-10-07; "[svn-upgrade] Integrating new upstream version, shadow (19990709)")
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Copy link
Member

@hallyn hallyn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, no, it may be wrong now, but we do inte d to read seven fields, the seventh (fields[6]) being the shell to use.

@hallyn
Copy link
Member

hallyn commented Jun 7, 2025

Yeah i think it is correct at the moment. Nfields ends as 6 bc cp is null at bottom of loop after the seventh field is read.

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Jun 7, 2025

@hallyn

I don't understand your comments.

This is the situation as I see it:

  • The array is unnecessarily declared as an array of 8. I think we agree on the first commit that it should be an array of 7.

  • The documentation says there are 7 fields, and the comments in the source code agree with the documentation. This was probably true on other Unix systems.

  • However, our code has never worked for 7 fields. It has always checked for 6 fields, and reported an error for 7 (or 5,4,3,2,1,0).

  • We can fix this by adding support for the 7th field, or we can just assume that there has never been a 7th field, and continue working for 6 fields. If we add support for the 7th field, we should consider accepting 6 too, since every existing script that uses our tools will be passing 6 currently.

  • Regardless of not having supported the 7th field, we tried to read it, but it was never there, or we would have reported an error earlier. This means we've had been reading as a string the pw_shell field (7th), which contained garbage. A dangerous bug. This is true regardless of our opinion on supporting the 7th field.

@alejandro-colomar
Copy link
Collaborator Author

Maybe I should split the PR.

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Jun 7, 2025

Nah, see #1267 and #1266

@alejandro-colomar alejandro-colomar deleted the fields678 branch June 7, 2025 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants