summaryrefslogtreecommitdiff
path: root/libpam/pam_modutil_priv.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpam/pam_modutil_priv.c')
-rw-r--r--libpam/pam_modutil_priv.c57
1 files changed, 53 insertions, 4 deletions
diff --git a/libpam/pam_modutil_priv.c b/libpam/pam_modutil_priv.c
index e22fab1a..7df6e6b1 100644
--- a/libpam/pam_modutil_priv.c
+++ b/libpam/pam_modutil_priv.c
@@ -14,7 +14,9 @@
#include <syslog.h>
#include <pwd.h>
#include <grp.h>
+#ifdef HAVE_SYS_FSUID_H
#include <sys/fsuid.h>
+#endif /* HAVE_SYS_FSUID_H */
/*
* Two setfsuid() calls in a row are necessary to check
@@ -22,17 +24,55 @@
*/
static int change_uid(uid_t uid, uid_t *save)
{
+#ifdef HAVE_SYS_FSUID_H
uid_t tmp = setfsuid(uid);
if (save)
*save = tmp;
return (uid_t) setfsuid(uid) == uid ? 0 : -1;
+#else
+ uid_t euid = geteuid();
+ uid_t ruid = getuid();
+ if (save)
+ *save = ruid;
+ if (ruid == uid && uid != 0)
+ if (setreuid(euid, uid))
+ return -1;
+ else {
+ setreuid(0, -1);
+ if (setreuid(-1, uid)) {
+ setreuid(-1, 0);
+ setreuid(0, -1);
+ if (setreuid(-1, uid))
+ return -1;
+ }
+ }
+#endif
}
static int change_gid(gid_t gid, gid_t *save)
{
+#ifdef HAVE_SYS_FSUID_H
gid_t tmp = setfsgid(gid);
if (save)
*save = tmp;
return (gid_t) setfsgid(gid) == gid ? 0 : -1;
+#else
+ gid_t egid = getegid();
+ gid_t rgid = getgid();
+ if (save)
+ *save = rgid;
+ if (rgid == gid)
+ if (setregid(egid, gid))
+ return -1;
+ else {
+ setregid(0, -1);
+ if (setregid(-1, gid)) {
+ setregid(-1, 0);
+ setregid(0, -1);
+ if (setregid(-1, gid))
+ return -1;
+ }
+ }
+#endif
}
static int cleanup(struct pam_modutil_privs *p)
@@ -107,11 +147,20 @@ int pam_modutil_drop_priv(pam_handle_t *pamh,
* We should care to leave process credentials in consistent state.
* That is, e.g. if change_gid() succeeded but change_uid() failed,
* we should try to restore old gid.
+ *
+ * We try to add the supplementary groups on a best-effort
+ * basis. If it fails, it's not fatal: we fall back to using an
+ * empty list.
*/
- if (setgroups(0, NULL)) {
- pam_syslog(pamh, LOG_ERR,
- "pam_modutil_drop_priv: setgroups failed: %m");
- return cleanup(p);
+ if (initgroups(pw->pw_name, pw->pw_gid)) {
+ pam_syslog(pamh, LOG_WARNING,
+ "pam_modutil_drop_priv: initgroups failed: %m");
+
+ if (setgroups(0, NULL)) {
+ pam_syslog(pamh, LOG_ERR,
+ "pam_modutil_drop_priv: setgroups failed: %m");
+ return cleanup(p);
+ }
}
if (change_gid(pw->pw_gid, &p->old_gid)) {
pam_syslog(pamh, LOG_ERR,