1  /* Check if effective user id can access file
       2     Copyright (C) 1990-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* Written by David MacKenzie and Torbjorn Granlund.
      20     Adapted for GNU C library by Roland McGrath.  */
      21  
      22  #ifdef HAVE_CONFIG_H
      23  # include <config.h>
      24  #endif
      25  
      26  #include <sys/types.h>
      27  #include <sys/stat.h>
      28  
      29  #ifdef S_IEXEC
      30  # ifndef S_IXUSR
      31  #  define S_IXUSR S_IEXEC
      32  # endif
      33  # ifndef S_IXGRP
      34  #  define S_IXGRP (S_IEXEC >> 3)
      35  # endif
      36  # ifndef S_IXOTH
      37  #  define S_IXOTH (S_IEXEC >> 6)
      38  # endif
      39  #endif /* S_IEXEC */
      40  
      41  #if defined HAVE_UNISTD_H || defined _LIBC
      42  # include <unistd.h>
      43  #endif
      44  
      45  #ifndef _POSIX_VERSION
      46  uid_t getuid ();
      47  gid_t getgid ();
      48  uid_t geteuid ();
      49  gid_t getegid ();
      50  #endif /* not POSIX_VERSION */
      51  
      52  #include <errno.h>
      53  #ifndef errno
      54  extern int errno;
      55  #endif
      56  #ifndef __set_errno
      57  # define __set_errno(val) errno = (val)
      58  #endif
      59  
      60  #if defined EACCES && !defined EACCESS
      61  # define EACCESS EACCES
      62  #endif
      63  
      64  #ifndef F_OK
      65  # define F_OK 0
      66  # define X_OK 1
      67  # define W_OK 2
      68  # define R_OK 4
      69  #endif
      70  
      71  #if !defined S_IROTH && defined R_OK
      72  # define S_IROTH R_OK
      73  #endif
      74  #if !defined S_IWOTH && defined W_OK
      75  # define S_IWOTH W_OK
      76  #endif
      77  #if !defined S_IXOTH && defined X_OK
      78  # define S_IXOTH X_OK
      79  #endif
      80  
      81  
      82  #ifdef _LIBC
      83  
      84  # define group_member __group_member
      85  # define euidaccess __euidaccess
      86  
      87  #else
      88  
      89  /* The user's real user id. */
      90  static uid_t uid;
      91  
      92  /* The user's real group id. */
      93  static gid_t gid;
      94  
      95  /* The user's effective user id. */
      96  static uid_t euid;
      97  
      98  /* The user's effective group id. */
      99  static gid_t egid;
     100  
     101  /* Nonzero if UID, GID, EUID, and EGID have valid values. */
     102  static int have_ids;
     103  
     104  # ifdef HAVE_GETGROUPS
     105  int group_member ();
     106  # else
     107  #  define group_member(gid)	0
     108  # endif
     109  
     110  #endif
     111  
     112  
     113  /* Return 0 if the user has permission of type MODE on file PATH;
     114     otherwise, return -1 and set `errno' to EACCESS.
     115     Like access, except that it uses the effective user and group
     116     id's instead of the real ones, and it does not check for read-only
     117     filesystem, text busy, etc. */
     118  
     119  int
     120  euidaccess (const char *path, int mode)
     121  {
     122    struct __stat64_t64 stats;
     123    int granted;
     124  
     125  #ifdef	_LIBC
     126    uid_t euid;
     127    gid_t egid;
     128  #else
     129    if (have_ids == 0)
     130      {
     131        have_ids = 1;
     132        uid = getuid ();
     133        gid = getgid ();
     134        euid = geteuid ();
     135        egid = getegid ();
     136      }
     137  
     138    if (uid == euid && gid == egid)
     139      /* If we are not set-uid or set-gid, access does the same.  */
     140      return access (path, mode);
     141  #endif
     142  
     143    if (__stat64_time64 (path, &stats))
     144      return -1;
     145  
     146    mode &= (X_OK | W_OK | R_OK);	/* Clear any bogus bits. */
     147  #if R_OK != S_IROTH || W_OK != S_IWOTH || X_OK != S_IXOTH
     148    ?error Oops, portability assumptions incorrect.
     149  #endif
     150  
     151    if (mode == F_OK)
     152      return 0;			/* The file exists. */
     153  
     154  #ifdef	_LIBC
     155    /* Now we need the IDs.  */
     156    euid = __geteuid ();
     157    egid = __getegid ();
     158  
     159    if (__getuid () == euid && __getgid () == egid)
     160      /* If we are not set-uid or set-gid, access does the same.  */
     161      return __access (path, mode);
     162  #endif
     163  
     164    /* The super-user can read and write any file, and execute any file
     165       that anyone can execute. */
     166    if (euid == 0 && ((mode & X_OK) == 0
     167  		    || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
     168      return 0;
     169  
     170    if (euid == stats.st_uid)
     171      granted = (unsigned int) (stats.st_mode & (mode << 6)) >> 6;
     172    else if (egid == stats.st_gid || group_member (stats.st_gid))
     173      granted = (unsigned int) (stats.st_mode & (mode << 3)) >> 3;
     174    else
     175      granted = (stats.st_mode & mode);
     176    /* XXX Add support for ACLs.  */
     177    if (granted == mode)
     178      return 0;
     179    __set_errno (EACCESS);
     180    return -1;
     181  }
     182  #undef euidaccess
     183  #undef eaccess
     184  #ifdef weak_alias
     185  weak_alias (__euidaccess, euidaccess)
     186  weak_alias (__euidaccess, eaccess)
     187  #endif
     188  
     189  #ifdef TEST
     190  # include <stdio.h>
     191  # include <errno.h>
     192  # include "error.h"
     193  
     194  char *program_name;
     195  
     196  int
     197  main (int argc, char **argv)
     198  {
     199    char *file;
     200    int mode;
     201    int err;
     202  
     203    program_name = argv[0];
     204    if (argc < 3)
     205      abort ();
     206    file = argv[1];
     207    mode = atoi (argv[2]);
     208  
     209    err = euidaccess (file, mode);
     210    printf ("%d\n", err);
     211    if (err != 0)
     212      error (0, errno, "%s", file);
     213    exit (0);
     214  }
     215  #endif