[v2] inet: add boundary check to prevent bof and inet/tst-ruserpass [BZ #33881]

Message ID 20260320135802.13850-1-c.colonna@itdoctor.it (mailing list archive)
State New
Headers
Series [v2] inet: add boundary check to prevent bof and inet/tst-ruserpass [BZ #33881] |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed

Commit Message

Christian Colonna March 20, 2026, 1:58 p.m. UTC
  Add boundary checks in the token function parsing .netrc config file.
If token was too long, the buffer storing token could overflow.

Add test creating .netrc file with a long token and verify that ruserpass doesn't cause SEGFAULT.
Add additional test to verify that when permission of .netrc are not 0600 ruserpass returns -1.

Signed-off-by: Christian Colonna <c.colonna@itdoctor.it>
---
 inet/Makefile        |  1 +
 inet/ruserpass.c     | 10 ++++--
 inet/tst-ruserpass.c | 86 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 94 insertions(+), 3 deletions(-)
 create mode 100644 inet/tst-ruserpass.c
  

Patch

diff --git a/inet/Makefile b/inet/Makefile
index 613f61d290..d8a57498ef 100644
--- a/inet/Makefile
+++ b/inet/Makefile
@@ -97,6 +97,7 @@  tests := \
   tst-inet6_rth \
   tst-network \
   tst-ntoa \
+  tst-ruserpass \
   tst-sockaddr \
   # tests
 
diff --git a/inet/ruserpass.c b/inet/ruserpass.c
index be4e024203..ab7b23334c 100644
--- a/inet/ruserpass.c
+++ b/inet/ruserpass.c
@@ -54,7 +54,9 @@  static	FILE *cfile;
 #define	ID	10
 #define	MACHINE	11
 
-static char tokval[100];
+#define TOKVAL_SIZE 100
+static char tokval[TOKVAL_SIZE];
+
 
 static const char tokstr[] =
 {
@@ -229,7 +231,8 @@  token (void)
 		while ((c = getc_unlocked(cfile)) != EOF && c != '"') {
 			if (c == '\\')
 				c = getc_unlocked(cfile);
-			*cp++ = c;
+			if (cp-tokval < TOKVAL_SIZE - 1)
+				*cp++ = c;
 		}
 	} else {
 		*cp++ = c;
@@ -237,7 +240,8 @@  token (void)
 		    && c != '\n' && c != '\t' && c != ' ' && c != ',') {
 			if (c == '\\')
 				c = getc_unlocked(cfile);
-			*cp++ = c;
+			if (cp-tokval < TOKVAL_SIZE - 1)
+				*cp++ = c;
 		}
 	}
 	*cp = 0;
diff --git a/inet/tst-ruserpass.c b/inet/tst-ruserpass.c
new file mode 100644
index 0000000000..462e4b90bb
--- /dev/null
+++ b/inet/tst-ruserpass.c
@@ -0,0 +1,86 @@ 
+/* Test for ruserpass.
+   Copyright (C) 2026-2026 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+
+
+static char *temp_home_dir;
+static char *temp_netrc;
+
+extern int ruserpass (const char *host, const char **aname, const char **apass);
+
+static void
+generate_string_a (char * str, size_t len)
+{
+  memset (str, 'a', len);
+  str[len] = '\0';
+}
+
+static void
+do_prepare (int argc, char **argv)
+{
+  char temp_password[200];
+
+  // creating a .netrc file for testing. In ruserpass the file is accessed relative to $HOME env, we will tweak $HOME to use our test file
+  temp_home_dir = support_create_temp_directory ("tst-ruserpass-");
+  temp_netrc = xasprintf ("%s/.netrc", temp_home_dir);
+  add_temp_file (temp_netrc);
+
+  generate_string_a(temp_password, sizeof(temp_password) - 1);
+
+  char * netrc_content = xasprintf ("machine foo.gnu login foo password %s\n", temp_password);
+
+  support_write_file_string (temp_netrc, netrc_content);
+
+  free (netrc_content);
+}
+
+#define PREPARE do_prepare
+
+static int
+do_test (void)
+{
+  const char *orig_name = NULL;
+  const char *orig_pass = NULL;
+
+  if (access (temp_netrc, R_OK) != 0)
+    FAIL_EXIT1 ("File .netrc is not readable");
+  setenv ("HOME", temp_home_dir, 1);
+
+  // function should returns -1 if .netrc file permission is readable by others
+  TEST_COMPARE (ruserpass ("foo.gnu", &orig_name, &orig_pass), -1);
+
+  if (chmod (temp_netrc, S_IRUSR | S_IWUSR) != 0)
+    FAIL_EXIT1 ("Impossible to set .netrc permissions. We need it to be 0600 else ruserpass will exit -1.");
+
+  // ruserpass should not segfault if password is longer than password tokval buffer
+  TEST_COMPARE (ruserpass ("foo.gnu", &orig_name, &orig_pass), EXIT_SUCCESS);
+
+  free (temp_home_dir);
+  free (temp_netrc);
+
+  return EXIT_SUCCESS;
+}
+
+#include <support/test-driver.c>
\ No newline at end of file