Subject: Re: suid helper to verify own passwd
To: None <tech-security@netbsd.org>
From: Christos Zoulas <christos@astron.com>
List: tech-security
Date: 12/20/2006 13:37:44
In article <200612200913.KAA0000122500@zel459.zel.kfa-juelich.de>,
Matthias Drochner <M.Drochner@fz-juelich.de> wrote:
>-=-=-=-=-=-
>
>
>[elad asked me to post this to tech-security. The other
>mail referred to below (about a suid helper at nss level)
>can be found in tech-userlevel.]
>
>
>[this is more or less a followup to my
>"suid helper to read own passwd entry" mail earlier this month.]
>
>Here is an implementation which plugs in as PAM module.
>The advantage is that, opposed to the nss variant, one can't
>get the encrypted password (not even as its owner) to mount
>an offline dictionary attack.
>The backside is that if one succeeds to eavesdrop the
>communication between the (unprivileged) client program
>and the SUID helper, he gets the plaintext password and
>no strong passwd encryption will help.
>One can argue (as does Joerg) that such an attacker could
>listen to X11 events carrying the passwd as well, so there
>is no additional danger.
>
>So please have a look at it and tell whether there are
>security risks besides the obvious and known ones, and
>what else can be improved.
>I'd consider that stuff for addition to pkgsrc next
>quarter, and to make the screensavers use it.
>
>best regards
>Matthias
>
>
>-=-=-=-=-=-
>
>#include <sys/types.h>
>#include <security/pam_appl.h>
>#include <security/pam_modules.h>
>
>#include <unistd.h>
>#include <string.h>
>#include <sys/wait.h>
>#include <errno.h>
>
>#define PATH_HELPER "/home/drochner/pam_passwd_suid/helper/helper"
>
>static int
>askhelper(const char *user, const char *pass)
>{
> int fd[2];
> pid_t pid, rpid;
> int res, pwlen, s;
pwlen should be size_t, res should be ssize_t.
>
> res = pipe(fd);
> if (res < 0)
> return (errno);
Don't use res;
if (pipe(fd) == -1)
return errno;
KNF here; errno, not (errno) and everywhere else.
>
> pid = fork();
This is a good candidate for vfork.
> if (pid == -1)
> return (errno);
I use a switch for *fork() to avoid the 2 ifs.
> if (pid == 0) {
> dup2(fd[0], 0);
> close(fd[0]);
> close(fd[1]);
(void) before close and dup2
> execl(PATH_HELPER, PATH_HELPER, user, NULL);
> _exit(errno);
> }
>
> close(fd[0]);
>
> pwlen = strlen(pass);
> res = write(fd[1], pass, pwlen);
> if (res != pwlen)
> return (EIO);
This hides the true errno.
if (res != pwlen)
return res == -1 ? errno : EIO;
>
> close(fd[1]);
>
> rpid = waitpid(pid, &s, 0);
> if (rpid != pid)
> return (errno);
> if (WEXITSTATUS(s))
> return (WEXITSTATUS(s));
Should return another errno here, not the exit status.
>
> return (0);
>}
>
>PAM_EXTERN int
>pam_sm_authenticate(pam_handle_t *pamh, int flags,
> int argc, const char ** argv)
KNF
>{
> const char *user, *pass;
> int res;
>
> res = pam_get_user(pamh, &user, NULL);
> if (res != PAM_SUCCESS)
> return (res);
> res = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, NULL);
> if (res != PAM_SUCCESS)
> return (res);
>
> if (askhelper(user, pass) != 0)
> return (PAM_AUTH_ERR);
>
> return (PAM_SUCCESS);
>}
>
>PAM_MODULE_ENTRY("pam_passwdhelper");
>
>-=-=-=-=-=-
>
>#include <pwd.h>
>#include <string.h>
>#include <errno.h>
>#include <unistd.h>
>
>static char pwbuf[1024];
>
>int
>main(int argc, char **argv)
>{
> struct passwd *pwent;
> int res;
res should be ssize_t
> char *bufptr;
> size_t buflen;
>
> if (argc != 2)
> return (EINVAL);
>
> bufptr = pwbuf;
> buflen = sizeof(pwbuf);
> do {
> res = read(0, bufptr, buflen);
> if (res < 0)
> return (errno);
> bufptr += res;
> buflen -= res;
> } while (res > 0 && buflen > 0);
> if (buflen == 0)
> return (ENOMEM);
>
> pwent = getpwnam(argv[1]);
> if (!pwent || (pwent->pw_uid != getuid()))
> return (EPERM);
>
> if (strcmp(crypt(pwbuf, pwent->pw_passwd), pwent->pw_passwd) == 0)
> return (0);
>
> return (EAUTH);
>}
>
>-=-=-=-=-=-
Otherwise it looks ok.
christos