From 111022eac64579cc12d20e33146ce01717562b29 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 10 Jan 2014 18:03:28 -0500 Subject: Move username lookup functions from /port to /common Per suggestion from Peter E and Alvaro --- src/common/username.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/common/username.c (limited to 'src/common/username.c') diff --git a/src/common/username.c b/src/common/username.c new file mode 100644 index 0000000000..c6812ddd9d --- /dev/null +++ b/src/common/username.c @@ -0,0 +1,85 @@ +/*------------------------------------------------------------------------- + * + * username.c + * get user name + * + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/common/username.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include +#include +#include + +#include "common/username.h" + +/* + * Returns the current user name in a static buffer, or NULL on error and + * sets errstr + */ +const char * +get_user_name(char **errstr) +{ +#ifndef WIN32 + struct passwd *pw; + uid_t user_id = geteuid(); + + *errstr = NULL; + + errno = 0; /* clear errno before call */ + pw = getpwuid(user_id); + if (!pw) + { + *errstr = psprintf(_("failed to look up effective user id %d: %s"), + (int) user_id, errno ? strerror(errno) : + _("user does not exist")); + return NULL; + } + + return pw->pw_name; +#else + /* UNLEN = 256, 'static' variable remains after function exit */ + static char username[256 + 1]; + DWORD len = sizeof(username) - 1; + + if (!GetUserName(username, &len)) + { + *errstr = psprintf(_("user name lookup failure: %s"), strerror(errno)); + return NULL; + } + + return username; +#endif +} + + +/* + * Returns the current user name in a static buffer or exits + */ +const char * +get_user_name_or_exit(const char *progname) +{ + const char *user_name; + char *errstr; + + user_name = get_user_name(&errstr); + + if (!user_name) + { + fprintf(stderr, "%s: %s\n", progname, errstr); + exit(1); + } + return user_name; +} -- cgit v1.2.3 From 571addd729a400cece396d79696adcc63387e43b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 29 Jan 2014 20:03:57 -0500 Subject: Fix unsafe references to errno within error messaging logic. Various places were supposing that errno could be expected to hold still within an ereport() nest or similar contexts. This isn't true necessarily, though in some cases it accidentally failed to fail depending on how the compiler chanced to order the subexpressions. This class of thinko explains recent reports of odd failures on clang-built versions, typically missing or inappropriate HINT fields in messages. Problem identified by Christian Kruse, who also submitted the patch this commit is based on. (I fixed a few issues in his patch and found a couple of additional places with the same disease.) Back-patch as appropriate to all supported branches. --- src/backend/commands/tablespace.c | 6 +++++- src/backend/port/sysv_sema.c | 14 ++++++++++---- src/backend/port/sysv_shmem.c | 30 ++++++++++++++++++------------ src/bin/psql/command.c | 11 ++++++----- src/common/username.c | 10 +++++----- 5 files changed, 44 insertions(+), 27 deletions(-) (limited to 'src/common/username.c') diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index d73e5e826d..60fd939175 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -782,10 +782,14 @@ remove_symlink: else { if (unlink(linkloc) < 0) - ereport(redo ? LOG : (errno == ENOENT ? WARNING : ERROR), + { + int saved_errno = errno; + + ereport(redo ? LOG : (saved_errno == ENOENT ? WARNING : ERROR), (errcode_for_file_access(), errmsg("could not remove symbolic link \"%s\": %m", linkloc))); + } } pfree(linkloc_with_version_dir); diff --git a/src/backend/port/sysv_sema.c b/src/backend/port/sysv_sema.c index b4825d20fb..d5d66edcd3 100644 --- a/src/backend/port/sysv_sema.c +++ b/src/backend/port/sysv_sema.c @@ -91,15 +91,17 @@ InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems) if (semId < 0) { + int saved_errno = errno; + /* * Fail quietly if error indicates a collision with existing set. One * would expect EEXIST, given that we said IPC_EXCL, but perhaps we * could get a permission violation instead? Also, EIDRM might occur * if an old set is slated for destruction but not gone yet. */ - if (errno == EEXIST || errno == EACCES + if (saved_errno == EEXIST || saved_errno == EACCES #ifdef EIDRM - || errno == EIDRM + || saved_errno == EIDRM #endif ) return -1; @@ -112,7 +114,7 @@ InternalIpcSemaphoreCreate(IpcSemaphoreKey semKey, int numSems) errdetail("Failed system call was semget(%lu, %d, 0%o).", (unsigned long) semKey, numSems, IPC_CREAT | IPC_EXCL | IPCProtection), - (errno == ENOSPC) ? + (saved_errno == ENOSPC) ? errhint("This error does *not* mean that you have run out of disk space. " "It occurs when either the system limit for the maximum number of " "semaphore sets (SEMMNI), or the system wide maximum number of " @@ -136,13 +138,17 @@ IpcSemaphoreInitialize(IpcSemaphoreId semId, int semNum, int value) semun.val = value; if (semctl(semId, semNum, SETVAL, semun) < 0) + { + int saved_errno = errno; + ereport(FATAL, (errmsg_internal("semctl(%d, %d, SETVAL, %d) failed: %m", semId, semNum, value), - (errno == ERANGE) ? + (saved_errno == ERANGE) ? errhint("You possibly need to raise your kernel's SEMVMX value to be at least " "%d. Look into the PostgreSQL documentation for details.", value) : 0)); + } } /* diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index ac3a9fe3b0..65ad59570c 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -73,15 +73,17 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size) if (shmid < 0) { + int shmget_errno = errno; + /* * Fail quietly if error indicates a collision with existing segment. * One would expect EEXIST, given that we said IPC_EXCL, but perhaps * we could get a permission violation instead? Also, EIDRM might * occur if an old seg is slated for destruction but not gone yet. */ - if (errno == EEXIST || errno == EACCES + if (shmget_errno == EEXIST || shmget_errno == EACCES #ifdef EIDRM - || errno == EIDRM + || shmget_errno == EIDRM #endif ) return NULL; @@ -95,10 +97,8 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size) * against SHMMIN in the preexisting-segment case, so we will not get * EINVAL a second time if there is such a segment. */ - if (errno == EINVAL) + if (shmget_errno == EINVAL) { - int save_errno = errno; - shmid = shmget(memKey, 0, IPC_CREAT | IPC_EXCL | IPCProtection); if (shmid < 0) @@ -124,8 +124,6 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size) elog(LOG, "shmctl(%d, %d, 0) failed: %m", (int) shmid, IPC_RMID); } - - errno = save_errno; } /* @@ -137,25 +135,26 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size) * it should be. SHMMNI violation is ENOSPC, per spec. Just plain * not-enough-RAM is ENOMEM. */ + errno = shmget_errno; ereport(FATAL, (errmsg("could not create shared memory segment: %m"), errdetail("Failed system call was shmget(key=%lu, size=%zu, 0%o).", (unsigned long) memKey, size, IPC_CREAT | IPC_EXCL | IPCProtection), - (errno == EINVAL) ? + (shmget_errno == EINVAL) ? errhint("This error usually means that PostgreSQL's request for a shared memory " "segment exceeded your kernel's SHMMAX parameter, or possibly that " "it is less than " "your kernel's SHMMIN parameter.\n" "The PostgreSQL documentation contains more information about shared " "memory configuration.") : 0, - (errno == ENOMEM) ? + (shmget_errno == ENOMEM) ? errhint("This error usually means that PostgreSQL's request for a shared " "memory segment exceeded your kernel's SHMALL parameter. You might need " "to reconfigure the kernel with larger SHMALL.\n" "The PostgreSQL documentation contains more information about shared " "memory configuration.") : 0, - (errno == ENOSPC) ? + (shmget_errno == ENOSPC) ? errhint("This error does *not* mean that you have run out of disk space. " "It occurs either if all available shared memory IDs have been taken, " "in which case you need to raise the SHMMNI parameter in your kernel, " @@ -331,6 +330,7 @@ CreateAnonymousSegment(Size *size) { Size allocsize = *size; void *ptr = MAP_FAILED; + int mmap_errno = 0; #ifndef MAP_HUGETLB if (huge_tlb_pages == HUGE_TLB_ON) @@ -363,6 +363,7 @@ CreateAnonymousSegment(Size *size) ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, PG_MMAP_FLAGS | MAP_HUGETLB, -1, 0); + mmap_errno = errno; if (huge_tlb_pages == HUGE_TLB_TRY && ptr == MAP_FAILED) elog(DEBUG1, "mmap with MAP_HUGETLB failed, huge pages disabled: %m"); } @@ -376,13 +377,17 @@ CreateAnonymousSegment(Size *size) * back to non-huge pages. */ allocsize = *size; - ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, PG_MMAP_FLAGS, -1, 0); + ptr = mmap(NULL, allocsize, PROT_READ | PROT_WRITE, + PG_MMAP_FLAGS, -1, 0); + mmap_errno = errno; } if (ptr == MAP_FAILED) + { + errno = mmap_errno; ereport(FATAL, (errmsg("could not map anonymous shared memory: %m"), - (errno == ENOMEM) ? + (mmap_errno == ENOMEM) ? errhint("This error usually means that PostgreSQL's request " "for a shared memory segment exceeded available memory, " "swap space or huge pages. To reduce the request size " @@ -390,6 +395,7 @@ CreateAnonymousSegment(Size *size) "memory usage, perhaps by reducing shared_buffers or " "max_connections.", *size) : 0)); + } *size = allocsize; return ptr; diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index b26e28006e..764534a3ae 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -264,14 +264,15 @@ exec_command(const char *cmd, { #ifndef WIN32 struct passwd *pw; + uid_t user_id = geteuid(); - errno = 0; /* clear errno before call */ - pw = getpwuid(geteuid()); + errno = 0; /* clear errno before call */ + pw = getpwuid(user_id); if (!pw) { - psql_error("could not get home directory for user id %d: %s\n", - (int) geteuid(), errno ? - strerror(errno) : "user does not exist"); + psql_error("could not get home directory for user id %ld: %s\n", + (long) user_id, + errno ? strerror(errno) : _("user does not exist")); exit(EXIT_FAILURE); } dir = pw->pw_dir; diff --git a/src/common/username.c b/src/common/username.c index c6812ddd9d..e946972a56 100644 --- a/src/common/username.c +++ b/src/common/username.c @@ -34,17 +34,17 @@ get_user_name(char **errstr) { #ifndef WIN32 struct passwd *pw; - uid_t user_id = geteuid(); + uid_t user_id = geteuid(); *errstr = NULL; - errno = 0; /* clear errno before call */ + errno = 0; /* clear errno before call */ pw = getpwuid(user_id); if (!pw) { - *errstr = psprintf(_("failed to look up effective user id %d: %s"), - (int) user_id, errno ? strerror(errno) : - _("user does not exist")); + *errstr = psprintf(_("failed to look up effective user id %ld: %s"), + (long) user_id, + errno ? strerror(errno) : _("user does not exist")); return NULL; } -- cgit v1.2.3 From b777be0d48a042f500cac72140ffb50392973aa2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 28 Mar 2014 10:30:37 -0400 Subject: Un-break peer authentication. Commit 613c6d26bd42dd8c2dd0664315be9551475b8864 sloppily replaced a lookup of the UID obtained from getpeereid() with a lookup of the server's own user name, thus totally destroying peer authentication. Revert. Per report from Christoph Berg. In passing, make sure get_user_name() zeroes *errstr on success on Windows as well as non-Windows. I don't think any callers actually depend on this ATM, but we should be consistent across platforms. --- src/backend/libpq/auth.c | 16 ++++++++-------- src/common/username.c | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'src/common/username.c') diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index 2a46f7b913..a2f1c96b8b 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -21,7 +21,6 @@ #include #include -#include "common/username.h" #include "libpq/auth.h" #include "libpq/crypt.h" #include "libpq/ip.h" @@ -1560,8 +1559,7 @@ auth_peer(hbaPort *port) char ident_user[IDENT_USERNAME_MAX + 1]; uid_t uid; gid_t gid; - const char *user_name; - char *errstr; + struct passwd *pass; errno = 0; if (getpeereid(port->sock, &uid, &gid) != 0) @@ -1578,15 +1576,17 @@ auth_peer(hbaPort *port) return STATUS_ERROR; } - user_name = get_user_name(&errstr); - if (!user_name) + pass = getpwuid(uid); + + if (pass == NULL) { - ereport(LOG, (errmsg_internal("%s", errstr))); - pfree(errstr); + ereport(LOG, + (errmsg("local user with ID %d does not exist", + (int) uid))); return STATUS_ERROR; } - strlcpy(ident_user, user_name, IDENT_USERNAME_MAX + 1); + strlcpy(ident_user, pass->pw_name, IDENT_USERNAME_MAX + 1); return check_usermap(port->hba->usermap, port->user_name, ident_user, false); } diff --git a/src/common/username.c b/src/common/username.c index e946972a56..24c5b47627 100644 --- a/src/common/username.c +++ b/src/common/username.c @@ -54,6 +54,8 @@ get_user_name(char **errstr) static char username[256 + 1]; DWORD len = sizeof(username) - 1; + *errstr = NULL; + if (!GetUserName(username, &len)) { *errstr = psprintf(_("user name lookup failure: %s"), strerror(errno)); -- cgit v1.2.3