[PATCH] nss: look for databases in /usr/share/nss/ if they don't exist in /etc/

Lennart Poettering lennart@poettering.net
Mon Sep 30 12:48:05 GMT 2024


In order to improve compatibility with systems that implement a hermetic
/usr/, this changes glibc NSS code to always look for its databases
files in /usr/share/nss/ too in case they don't exist (ENOENT) in /etc/.
This allows distributions to move /etc/protocols and similar data files
into /usr/share/nss/. These days various of these files are kinda static
anyway, and hence in many cases are better placed below the /usr/
hierarchy than below the configurable /etc/.

This change should have zero effect on existing systems, as any database
file in /etc/ will be consulted first, and only on ENOENT the matching
counterpart in /usr/share/nss/ will be attempted. This also provides a clear
path how administrators can still modify the files locally if they want
to: just copy them from /usr/share/nss/ to /etc/ and edit them there. In
that case the files in /usr/share/nss/ will have no effect anymore.

Many systems that want to achieve this behaviour currently work around
glibc's behaviour via modules such as nss-altfiles, an excercise that
becomes unnecessary if glibc just directly looks at these fallback
places. (Note that nss-altfiles has different semantics though, it
merges rather then replaces the files)

Also see:

https://github.com/uapi-group/specifications/issues/76

(2nd version of the patch: moved from /usr/share/ to /usr/share/nss/,
and I am now generating these two paths via stpcpy()/strcpy().)

(3rd version of the patch: fallback on more errnos than just ENOENT.)
---
 include/nss_files.h                |  8 ++++--
 nss/Versions                       |  2 +-
 nss/nss_compat/compat-grp.c        |  2 +-
 nss/nss_compat/compat-initgroups.c |  2 +-
 nss/nss_compat/compat-pwd.c        |  2 +-
 nss/nss_compat/compat-spwd.c       |  2 +-
 nss/nss_files/files-XXX.c          |  8 +++---
 nss/nss_files/files-alias.c        |  7 +++---
 nss/nss_files/files-initgroups.c   |  2 +-
 nss/nss_files/files-netgrp.c       |  4 +--
 nss/nss_files_data.c               | 12 ++++-----
 nss/nss_files_fopen.c              | 40 ++++++++++++++++++++++++++++++
 12 files changed, 67 insertions(+), 24 deletions(-)

diff --git a/include/nss_files.h b/include/nss_files.h
index adf934f3ea..c224dabe5f 100644
--- a/include/nss_files.h
+++ b/include/nss_files.h
@@ -25,6 +25,10 @@
 #include <libc-lock.h>
 #endif

+/* Open DATABASE for reading.  */
+FILE *__nss_files_fopen_database (const char *database);
+libc_hidden_proto (__nss_files_fopen_database)
+
 /* Open PATH for reading, as a data source for nss_files.  */
 FILE *__nss_files_fopen (const char *path);
 libc_hidden_proto (__nss_files_fopen)
@@ -89,7 +93,7 @@ enum nss_files_file
    null.  */
 enum nss_status __nss_files_data_open (struct nss_files_per_file_data **pdata,
                                        enum nss_files_file file,
-                                       const char *path,
+                                       const char *database,
                                        int *errnop, int *herrnop);
 libc_hidden_proto (__nss_files_data_open)

@@ -101,7 +105,7 @@ libc_hidden_proto (__nss_files_data_put)
 /* Performs the set*ent operation for FILE.  PATH is the file to
    open.  */
 enum nss_status __nss_files_data_setent (enum nss_files_file file,
-                                           const char *path);
+                                         const char *database);
 libc_hidden_proto (__nss_files_data_setent)

 /* Performs the end*ent operation for FILE.  */
diff --git a/nss/Versions b/nss/Versions
index d765e1d3b6..9ea298cec0 100644
--- a/nss/Versions
+++ b/nss/Versions
@@ -108,7 +108,7 @@ libc {
     __nss_passwd_lookup2; __nss_group_lookup2; __nss_hosts_lookup2;
     __nss_services_lookup2; __nss_next2; __nss_lookup;
     __nss_hash; __nss_database_get;
-    __nss_files_fopen; __nss_readline; __nss_parse_line_result;
+    __nss_files_fopen; __nss_files_fopen_database; __nss_readline; __nss_parse_line_result;
     __nss_files_data_endent;
     __nss_files_data_open;
     __nss_files_data_put;
diff --git a/nss/nss_compat/compat-grp.c b/nss/nss_compat/compat-grp.c
index ede7503de7..3a9dcedb6a 100644
--- a/nss/nss_compat/compat-grp.c
+++ b/nss/nss_compat/compat-grp.c
@@ -108,7 +108,7 @@ internal_setgrent (ent_t *ent, int stayopen, int needent)

   if (ent->stream == NULL)
     {
-      ent->stream = __nss_files_fopen ("/etc/group");
+      ent->stream = __nss_files_fopen_database ("group");

       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
diff --git a/nss/nss_compat/compat-initgroups.c b/nss/nss_compat/compat-initgroups.c
index 2598cfbc9a..2acef020ef 100644
--- a/nss/nss_compat/compat-initgroups.c
+++ b/nss/nss_compat/compat-initgroups.c
@@ -122,7 +122,7 @@ internal_setgrent (ent_t *ent)
   else
     ent->blacklist.current = 0;

-  ent->stream = __nss_files_fopen ("/etc/group");
+  ent->stream = __nss_files_fopen_database ("group");

   if (ent->stream == NULL)
     status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
diff --git a/nss/nss_compat/compat-pwd.c b/nss/nss_compat/compat-pwd.c
index 2f37e0f621..a7220dcd58 100644
--- a/nss/nss_compat/compat-pwd.c
+++ b/nss/nss_compat/compat-pwd.c
@@ -223,7 +223,7 @@ internal_setpwent (ent_t *ent, int stayopen, int needent)

   if (ent->stream == NULL)
     {
-      ent->stream = __nss_files_fopen ("/etc/passwd");
+      ent->stream = __nss_files_fopen_database ("passwd");

       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
diff --git a/nss/nss_compat/compat-spwd.c b/nss/nss_compat/compat-spwd.c
index fac1e766cb..a67edb0ea8 100644
--- a/nss/nss_compat/compat-spwd.c
+++ b/nss/nss_compat/compat-spwd.c
@@ -178,7 +178,7 @@ internal_setspent (ent_t *ent, int stayopen, int needent)

   if (ent->stream == NULL)
     {
-      ent->stream = __nss_files_fopen ("/etc/shadow");
+      ent->stream = __nss_files_fopen_database ("shadow");

       if (ent->stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
diff --git a/nss/nss_files/files-XXX.c b/nss/nss_files/files-XXX.c
index 558b2c364f..8537bb4410 100644
--- a/nss/nss_files/files-XXX.c
+++ b/nss/nss_files/files-XXX.c
@@ -39,8 +39,6 @@

 #define ENTNAME_r	CONCAT(ENTNAME,_r)

-#define DATAFILE	"/etc/" DATABASE
-
 #ifdef NEED_H_ERRNO
 # include <netdb.h>
 # define H_ERRNO_PROTO	, int *herrnop
@@ -73,7 +71,7 @@ internal_setent (FILE **stream)

   if (*stream == NULL)
     {
-      *stream = __nss_files_fopen (DATAFILE);
+      *stream = __nss_files_fopen_database (DATABASE);

       if (*stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
@@ -89,7 +87,7 @@ internal_setent (FILE **stream)
 enum nss_status
 CONCAT(_nss_files_set,ENTNAME) (int stayopen)
 {
-  return __nss_files_data_setent (CONCAT (nss_file_, ENTNAME), DATAFILE);
+  return __nss_files_data_setent (CONCAT (nss_file_, ENTNAME), DATABASE);
 }
 libc_hidden_def (CONCAT (_nss_files_set,ENTNAME))

@@ -170,7 +168,7 @@ CONCAT(_nss_files_get,ENTNAME_r) (struct STRUCTURE *result, char *buffer,
   struct nss_files_per_file_data *data;
   enum nss_status status = __nss_files_data_open (&data,
 						  CONCAT (nss_file_, ENTNAME),
-						  DATAFILE,
+						  DATABASE,
 						  errnop, H_ERRNO_ARG_OR_NULL);
   if (status != NSS_STATUS_SUCCESS)
     return status;
diff --git a/nss/nss_files/files-alias.c b/nss/nss_files/files-alias.c
index 14a59b4655..961e248317 100644
--- a/nss/nss_files/files-alias.c
+++ b/nss/nss_files/files-alias.c
@@ -42,7 +42,7 @@ internal_setent (FILE **stream)

   if (*stream == NULL)
     {
-      *stream = __nss_files_fopen ("/etc/aliases");
+      *stream = __nss_files_fopen_database ("aliases");

       if (*stream == NULL)
 	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
@@ -58,7 +58,7 @@ internal_setent (FILE **stream)
 enum nss_status
 _nss_files_setaliasent (void)
 {
-  return __nss_files_data_setent (nss_file_aliasent, "/etc/aliases");
+  return __nss_files_data_setent (nss_file_aliasent, "aliases");
 }
 libc_hidden_def (_nss_files_setaliasent)

@@ -338,7 +338,8 @@ _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,

   struct nss_files_per_file_data *data;
   enum nss_status status = __nss_files_data_open (&data, nss_file_aliasent,
-						  "/etc/aliases", errnop, NULL);
+						  "aliases",
+                                                  errnop, NULL);
   if (status != NSS_STATUS_SUCCESS)
     return status;

diff --git a/nss/nss_files/files-initgroups.c b/nss/nss_files/files-initgroups.c
index 65189d3929..bec3a293c5 100644
--- a/nss/nss_files/files-initgroups.c
+++ b/nss/nss_files/files-initgroups.c
@@ -33,7 +33,7 @@ _nss_files_initgroups_dyn (const char *user, gid_t group, long int *start,
 			   long int *size, gid_t **groupsp, long int limit,
 			   int *errnop)
 {
-  FILE *stream = __nss_files_fopen ("/etc/group");
+  FILE *stream = __nss_files_fopen_database ("group");
   if (stream == NULL)
     {
       *errnop = errno;
diff --git a/nss/nss_files/files-netgrp.c b/nss/nss_files/files-netgrp.c
index 92d8062e43..8c8ad6c21f 100644
--- a/nss/nss_files/files-netgrp.c
+++ b/nss/nss_files/files-netgrp.c
@@ -27,7 +27,7 @@
 #include "netgroup.h"
 #include <nss_files.h>

-#define DATAFILE	"/etc/netgroup"
+#define DATABASE        "netgroup"

 libc_hidden_proto (_nss_files_endnetgrent)

@@ -62,7 +62,7 @@ _nss_files_setnetgrent (const char *group, struct __netgrent *result)
     return NSS_STATUS_UNAVAIL;

   /* Find the netgroups file and open it.  */
-  fp = __nss_files_fopen (DATAFILE);
+  fp = __nss_files_fopen_database (DATABASE);
   if (fp == NULL)
     status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
   else
diff --git a/nss/nss_files_data.c b/nss/nss_files_data.c
index 261a01a775..1fab3b03b5 100644
--- a/nss/nss_files_data.c
+++ b/nss/nss_files_data.c
@@ -74,13 +74,13 @@ __nss_files_data_get (struct nss_files_per_file_data **pdata,
 /* Helper function for opening the backing file at PATH.  */
 static enum nss_status
 __nss_files_data_internal_open (struct nss_files_per_file_data *data,
-                                const char *path)
+                                const char *database)
 {
   enum nss_status status = NSS_STATUS_SUCCESS;

   if (data->stream == NULL)
     {
-      data->stream = __nss_files_fopen (path);
+      data->stream = __nss_files_fopen_database (database);

       if (data->stream == NULL)
         status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
@@ -92,7 +92,7 @@ __nss_files_data_internal_open (struct nss_files_per_file_data *data,

 enum nss_status
 __nss_files_data_open (struct nss_files_per_file_data **pdata,
-                       enum nss_files_file file, const char *path,
+                       enum nss_files_file file, const char *database,
                        int *errnop, int *herrnop)
 {
   enum nss_status status = __nss_files_data_get (pdata, file, errnop, herrnop);
@@ -103,7 +103,7 @@ __nss_files_data_open (struct nss_files_per_file_data **pdata,
   if ((*pdata)->stream == NULL)
     {
       int saved_errno = errno;
-      status = __nss_files_data_internal_open (*pdata, path);
+      status = __nss_files_data_internal_open (*pdata, database);
       __set_errno (saved_errno);
       if (status != NSS_STATUS_SUCCESS)
         __nss_files_data_put (*pdata);
@@ -122,7 +122,7 @@ __nss_files_data_put (struct nss_files_per_file_data *data)
 libc_hidden_def (__nss_files_data_put)

 enum nss_status
-__nss_files_data_setent (enum nss_files_file file, const char *path)
+__nss_files_data_setent (enum nss_files_file file, const char *database)
 {
   struct nss_files_per_file_data *data;
   enum nss_status status = __nss_files_data_get (&data, file, NULL, NULL);
@@ -130,7 +130,7 @@ __nss_files_data_setent (enum nss_files_file file, const char *path)
     return status;

   if (data->stream == NULL)
-    status = __nss_files_data_internal_open (data, path);
+    status = __nss_files_data_internal_open (data, database);
   else
     rewind (data->stream);

diff --git a/nss/nss_files_fopen.c b/nss/nss_files_fopen.c
index e7c48d7bd8..307d1f61d4 100644
--- a/nss/nss_files_fopen.c
+++ b/nss/nss_files_fopen.c
@@ -21,6 +21,9 @@
 #include <errno.h>
 #include <stdio_ext.h>

+#define PRIMARY_PREFIX "/etc/"
+#define FALLBACK_PREFIX "/usr/share/nss/"
+
 FILE *
 __nss_files_fopen (const char *path)
 {
@@ -45,3 +48,40 @@ __nss_files_fopen (const char *path)
   return fp;
 }
 libc_hidden_def (__nss_files_fopen)
+
+FILE *
+__nss_files_fopen_database (const char *database)
+{
+  char buf[sizeof(FALLBACK_PREFIX) + NAME_MAX];
+
+  /* Protect against string overflow */
+  size_t n = strlen (database);
+  if (n > NAME_MAX)
+    {
+      __set_errno (EINVAL);
+      return NULL;
+    }
+
+  strcpy (stpcpy (buf, PRIMARY_PREFIX), database);
+  FILE *fp = __nss_files_fopen (buf);
+  if (fp != NULL)
+    return fp;
+  switch (errno)
+    {
+    case ENOENT:
+    case EISDIR:
+    case ENOTDIR:
+    case ELOOP:
+      /* If the path does not exist or is incorrectly set up, let's try to use the fallback database */
+      break;
+
+    default:
+      /* Propagate all other errors (in particular EACCESS) to the caller, since we shouldn't accidentally use
+       * the wrong database just because some MAC (or so) prohibited access to the primary database. */
+      return NULL;
+    }
+
+  strcpy (stpcpy (buf, FALLBACK_PREFIX), database);
+  return __nss_files_fopen (buf);
+}
+libc_hidden_def (__nss_files_fopen_database)
--
2.46.0


More information about the Libc-alpha mailing list