diff options
Diffstat (limited to 'libpam/pam_modutil_priv.c')
-rw-r--r-- | libpam/pam_modutil_priv.c | 57 |
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, |