aboutsummaryrefslogtreecommitdiff
path: root/paludis/util
diff options
context:
space:
mode:
authorAvatar Saleem Abdulrasool <compnerd@compnerd.org> 2016-01-17 12:21:45 -0800
committerAvatar Saleem Abdulrasool <compnerd@compnerd.org> 2016-01-21 13:09:11 -0800
commit6cc7eb47142dcad1cf277ccf31c38fc53746e45e (patch)
tree6c029356fe6b2a580de4875c1e4201e4881deea9 /paludis/util
parent1e359553c2e5da698318714bd77884da0288f4d8 (diff)
downloadpaludis-6cc7eb47142dcad1cf277ccf31c38fc53746e45e.tar.gz
paludis-6cc7eb47142dcad1cf277ccf31c38fc53746e45e.tar.xz
paludis: POSIX_ME_HARDER accounts repository
POSIX permits an indefinite required buffer size for querying GECOS fields. This is currently used in three locations. Previously, paludis would assume that it could perform a sysconf call to retrieve the requisite buffer size for ensuring that the subsequent GECOS field query would not receive a -ERANGE error. However, as it turns out, this is actually a problem even with GLIBC where the NSS may end up querying a service which has a larger field value for the `struct pwd` (glibc sets the return value to the NSS buffer length, aka 1k, but may end up querying a service which provides a larger response). Use local wrappers which perform the size adjustment to avoid an undersized buffer. Localise the functions which query the various GECOS fields which comprise the persona into util.
Diffstat (limited to 'paludis/util')
-rw-r--r--paludis/util/files.m41
-rw-r--r--paludis/util/persona.cc127
-rw-r--r--paludis/util/persona.hh43
-rw-r--r--paludis/util/process.cc14
4 files changed, 177 insertions, 8 deletions
diff --git a/paludis/util/files.m4 b/paludis/util/files.m4
index e2f304c52..dadcfcab3 100644
--- a/paludis/util/files.m4
+++ b/paludis/util/files.m4
@@ -55,6 +55,7 @@ add(`named_value', `hh', `cc', `fwd')
add(`no_type', `hh')
add(`operators', `hh')
add(`options', `hh', `fwd', `cc', `gtest')
+add(`persona', `hh', `cc')
add(`pimp', `hh', `impl')
add(`pipe', `hh', `cc')
add(`pool', `hh', `cc', `impl', `gtest', `fwd')
diff --git a/paludis/util/persona.cc b/paludis/util/persona.cc
new file mode 100644
index 000000000..817ea960b
--- /dev/null
+++ b/paludis/util/persona.cc
@@ -0,0 +1,127 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2016 Saleem Abdulrasool
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <paludis/util/persona.hh>
+
+#include <paludis/util/log.hh>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+
+namespace
+{
+ template <unsigned Name>
+ struct SystemConfigurationParameter;
+
+ template <>
+ struct SystemConfigurationParameter<_SC_GETPW_R_SIZE_MAX> {
+ static constexpr const char * const Spelling = "_SC_GETPW_R_SIZE_MAX";
+ };
+
+ template <>
+ struct SystemConfigurationParameter<_SC_GETGR_R_SIZE_MAX> {
+ static constexpr const char * const Spelling = "_SC_GETGR_R_SIZE_MAX";
+ };
+
+ template <unsigned Name, size_t DefaultSize = 1024, long DodgyLimit = 1024 * 128>
+ size_t initial_buffer_size(const char *context)
+ {
+ using namespace paludis;
+
+ long size;
+
+ errno = 0;
+ size = ::sysconf(Name);
+ if (size == -1 && errno == 0)
+ return DefaultSize;
+
+ if (size > DodgyLimit)
+ Log::get_instance()->message(context, ll_warning, lc_context)
+ << "Got dodgy value " << size << " from "
+ << "sysconf(" << SystemConfigurationParameter<Name>::Spelling << ")";
+
+ return std::min(size, DodgyLimit);
+ }
+}
+
+namespace paludis
+{
+ int getpwnam_r_s(const char *name, std::vector<char> & buffer,
+ struct passwd & pwd, struct passwd * & result)
+ {
+ size_t length;
+ int rv;
+
+ buffer.clear();
+
+ length =
+ initial_buffer_size<_SC_GETPW_R_SIZE_MAX>("accounts.getpw_r_size");
+
+ for (buffer.reserve(length);
+ (rv = ::getpwnam_r(name, &pwd, buffer.data(), buffer.capacity(),
+ &result)) == ERANGE;
+ buffer.reserve(length))
+ length = length * 2;
+
+ return rv;
+ }
+
+ int getgrgid_r_s(gid_t gid, std::vector<char> & buffer,
+ struct group & grp, struct group * & result)
+ {
+ size_t length;
+ int rv;
+
+ buffer.clear();
+
+ length =
+ initial_buffer_size<_SC_GETGR_R_SIZE_MAX>("accounts.getgr_r_size");
+
+ for (buffer.reserve(length);
+ (rv = ::getgrgid_r(gid, &grp, buffer.data(), buffer.capacity(),
+ &result)) == ERANGE;
+ buffer.reserve(length))
+ length = length * 2;
+
+ return rv;
+ }
+
+ int getpwuid_r_s(uid_t uid, std::vector<char> & buffer,
+ struct passwd & pwd, struct passwd * & result)
+ {
+ size_t length;
+ int rv;
+
+ buffer.clear();
+
+ length =
+ initial_buffer_size<_SC_GETPW_R_SIZE_MAX>("accounts.getpw_r_size");
+
+ for (buffer.reserve(length);
+ (rv = ::getpwuid_r(uid, &pwd, buffer.data(), buffer.capacity(),
+ &result)) == ERANGE;
+ buffer.reserve(length))
+ length = length * 2;
+
+ return rv;
+ }
+}
+
diff --git a/paludis/util/persona.hh b/paludis/util/persona.hh
new file mode 100644
index 000000000..de20216cd
--- /dev/null
+++ b/paludis/util/persona.hh
@@ -0,0 +1,43 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2016 Saleem Abdulrasool
+ *
+ * This file is part of the Paludis package manager. Paludis is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU General
+ * Public License version 2, as published by the Free Software Foundation.
+ *
+ * Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_PERSONA_H
+#define PALUDIS_GUARD_PALUDIS_UTIL_PERSONA_H
+
+#include <paludis/util/attributes.hh>
+
+#include <vector>
+
+#include <grp.h>
+#include <pwd.h>
+
+namespace paludis
+{
+ int getpwnam_r_s(const char * name, std::vector<char> & buffer,
+ struct passwd & pwd,
+ struct passwd * & result) PALUDIS_VISIBLE;
+
+ int getgrgid_r_s(gid_t gid, std::vector<char> & buffer, struct group & grp,
+ struct group * & result) PALUDIS_VISIBLE;
+
+ int getpwuid_r_s(uid_t uid, std::vector<char> & buffer, struct passwd & pwd,
+ struct passwd * & result) PALUDIS_VISIBLE;
+}
+
+#endif
diff --git a/paludis/util/process.cc b/paludis/util/process.cc
index b4b553db1..380d671d6 100644
--- a/paludis/util/process.cc
+++ b/paludis/util/process.cc
@@ -17,6 +17,7 @@
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <paludis/util/persona.hh>
#include <paludis/util/process.hh>
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/pipe.hh>
@@ -806,17 +807,14 @@ Process::run()
if (0 != ::setgid(_imp->setgid))
throw ProcessError("setgid() failed");
- int buflen(::sysconf(_SC_GETPW_R_SIZE_MAX));
- if (-1 == buflen)
- buflen = 1 << 16;
+ struct passwd pwd;
+ struct passwd *result;
+ std::vector<char> buffer;
- std::unique_ptr<char []> buf(new char[buflen]);
- struct passwd pw;
- struct passwd * pw_result(nullptr);
- if (0 != getpwuid_r(_imp->setuid, &pw, buf.get(), buflen, &pw_result) || nullptr == pw_result)
+ if (0 != getpwuid_r_s(_imp->setuid, buffer, pwd, result) || result == nullptr)
throw ProcessError("getpwuid_r() failed");
- if (0 != ::initgroups(pw_result->pw_name, getgid()))
+ if (0 != ::initgroups(pwd.pw_name, getgid()))
throw ProcessError("initgroups() failed");
if (0 != ::setuid(_imp->setuid))