diff mbox series

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

Message ID ZvqNDUx8-VK6eNM_@gardel-login
State New
Headers show
Series nss: look for databases in /usr/share/nss/ if they don't exist in /etc/ | expand

Commit Message

Lennart Poettering Sept. 30, 2024, 11:35 a.m. UTC
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())
---
 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              | 26 ++++++++++++++++++++++++++
 12 files changed, 53 insertions(+), 24 deletions(-)

--
2.46.0

Comments

Andreas Schwab Sept. 30, 2024, 11:49 a.m. UTC | #1
On Sep 30 2024, Lennart Poettering wrote:

> diff --git a/nss/nss_files_fopen.c b/nss/nss_files_fopen.c
> index e7c48d7bd8..7e467e89ce 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,26 @@ __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 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 || errno != ENOENT)

I think this should also check for ENOTDIR.
Florian Weimer Sept. 30, 2024, 12:20 p.m. UTC | #2
* Andreas Schwab:

> On Sep 30 2024, Lennart Poettering wrote:
>
>> diff --git a/nss/nss_files_fopen.c b/nss/nss_files_fopen.c
>> index e7c48d7bd8..7e467e89ce 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,26 @@ __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 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 || errno != ENOENT)
>
> I think this should also check for ENOTDIR.

Do you mean EISDIR?

We have this in nss/nss_database.c:

    switch (errno)
      {
      case EACCES:
      case EISDIR:
      case ELOOP:
      case ENOENT:
      case ENOTDIR:
      case EPERM:
        /* Ignore these errors.  They are persistent errors caused
           by file system contents.  */
        break;
      default:
        /* Other errors refer to resource allocation problems and
           need to be handled by the application.  */
        return false;
      }

I'm sure there are other places with similar logic.

Thanks,
Florian
Andreas Schwab Sept. 30, 2024, 12:25 p.m. UTC | #3
On Sep 30 2024, Florian Weimer wrote:

> * Andreas Schwab:
>
>> On Sep 30 2024, Lennart Poettering wrote:
>>
>>> diff --git a/nss/nss_files_fopen.c b/nss/nss_files_fopen.c
>>> index e7c48d7bd8..7e467e89ce 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,26 @@ __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 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 || errno != ENOENT)
>>
>> I think this should also check for ENOTDIR.
>
> Do you mean EISDIR?

May that too.  ENOTDIR happens if a directory component is not a directory.
diff mbox series

Patch

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..7e467e89ce 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,26 @@  __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 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 || errno != ENOENT)
+    return fp;
+
+  strcpy (stpcpy (buf, FALLBACK_PREFIX), database);
+  return __nss_files_fopen (buf);
+}
+libc_hidden_def (__nss_files_fopen_database)