On AIX, access(X_OK) may succeed when run as root even if the execution isn't possible. This behavior is allowed by POSIX which says: ... for a process with appropriate privileges, an implementation may indicate success for X_OK even if execute permission is not granted to any user. It can lead hook programs to have their execution refused: git commit -m content fatal: cannot exec '.git/hooks/pre-commit': Permission denied Add NEED_ACCESS_ROOT_HANDLER in order to use an access helper function. It checks with stat if any executable flags is set when the current user is root. Signed-off-by: Clément Chigot <clement.chigot@xxxxxxxx> Message-ID: <AM6PR02MB4950ED66291B16F35BE61A26EA3C0@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx> --- Makefile | 8 ++++++++ compat/access.c | 31 +++++++++++++++++++++++++++++++ compat/fileno.c | 2 +- config.mak.uname | 1 + git-compat-util.h | 12 +++++++++++- 5 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 compat/access.c diff --git a/Makefile b/Makefile index 9f1b6e8926..6ac7218106 100644 --- a/Makefile +++ b/Makefile @@ -439,6 +439,9 @@ all:: # # Define FILENO_IS_A_MACRO if fileno() is a macro, not a real function. # +# Define NEED_ACCESS_ROOT_HANDLER if access() under root may success for X_OK +# even if execution permission isn't granted for any user. +# # Define PAGER_ENV to a SP separated VAR=VAL pairs to define # default environment variables to be passed when a pager is spawned, e.g. # @@ -1833,6 +1836,11 @@ ifdef FILENO_IS_A_MACRO COMPAT_OBJS += compat/fileno.o endif +ifdef NEED_ACCESS_ROOT_HANDLER + COMPAT_CFLAGS += -DNEED_ACCESS_ROOT_HANDLER + COMPAT_OBJS += compat/access.o +endif + ifeq ($(TCLTK_PATH),) NO_TCLTK = NoThanks endif diff --git a/compat/access.c b/compat/access.c new file mode 100644 index 0000000000..19fda3e877 --- /dev/null +++ b/compat/access.c @@ -0,0 +1,31 @@ +#define COMPAT_CODE_ACCESS +#include "../git-compat-util.h" + +/* Do the same thing access(2) does, but use the effective uid, + * and don't make the mistake of telling root that any file is + * executable. This version uses stat(2). + */ +int git_access(const char *path, int mode) +{ + struct stat st; + + /* do not interfere a normal user */ + if (geteuid()) + return access(path, mode); + + if (stat(path, &st) < 0) + return -1; + + /* Root can read or write any file. */ + if (!(mode & X_OK)) + return 0; + + /* Root can execute any file that has any one of the execute + * bits set. + */ + if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) + return 0; + + errno = EACCES; + return -1; +} diff --git a/compat/fileno.c b/compat/fileno.c index 7b105f4cd7..8e80ef335d 100644 --- a/compat/fileno.c +++ b/compat/fileno.c @@ -1,4 +1,4 @@ -#define COMPAT_CODE +#define COMPAT_CODE_FILENO #include "../git-compat-util.h" int git_fileno(FILE *stream) diff --git a/config.mak.uname b/config.mak.uname index 86cbe47627..ce13ab8295 100644 --- a/config.mak.uname +++ b/config.mak.uname @@ -270,6 +270,7 @@ ifeq ($(uname_S),AIX) NEEDS_LIBICONV = YesPlease BASIC_CFLAGS += -D_LARGE_FILES FILENO_IS_A_MACRO = UnfortunatelyYes + NEED_ACCESS_ROOT_HANDLER = UnfortunatelyYes ifeq ($(shell expr "$(uname_V)" : '[1234]'),1) NO_PTHREADS = YesPlease else diff --git a/git-compat-util.h b/git-compat-util.h index 31b47932bd..bdca50e64d 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -1236,12 +1236,22 @@ struct tm *git_gmtime_r(const time_t *, struct tm *); #ifdef FILENO_IS_A_MACRO int git_fileno(FILE *stream); -# ifndef COMPAT_CODE +# ifndef COMPAT_CODE_FILENO # undef fileno # define fileno(p) git_fileno(p) # endif #endif +#ifdef NEED_ACCESS_ROOT_HANDLER +int git_access(const char *path, int mode); +# ifndef COMPAT_CODE_ACCESS +# ifdef access +# undef access +# endif +# define access(path, mode) git_access(path, mode) +# endif +#endif + /* * Our code often opens a path to an optional file, to work on its * contents when we can successfully open it. We can ignore a failure -- 2.17.1 Clément Chigot ATOS Bull SAS 1 rue de Provence - 38432 Échirolles - France