Subject: Re: suid helper to verify own passwd
To: None <tech-security@netbsd.org>
From: Christian Biere <christianbiere@gmx.de>
List: tech-security
Date: 12/22/2006 00:47:12
Matthias Drochner wrote:
> static int
> askhelper(const char *user, const char *pass)
> {
> 	int fd[2];
> 	pid_t pid, rpid;
> 	int res, pwlen, s;
> 
> 	res = pipe(fd);
> 	if (res < 0)
> 		return (errno);

You could use socketpair() with AF_LOCAL instead which would allow checking
credentials.

> 	pid = fork();
> 	if (pid == -1)
> 		return (errno);
> 	if (pid == 0) {
> 		dup2(fd[0], 0);
> 		close(fd[0]);
> 		close(fd[1]);
> 		execl(PATH_HELPER, PATH_HELPER, user, NULL);
> 		_exit(errno);
> 	}
> 
> 	close(fd[0]);
> 
> 	pwlen = strlen(pass);
> 	res = write(fd[1], pass, pwlen);

Wouldn't it be better to include the terminating NUL character, so
that the receiver can be absolutely sure about the length?

> 	if (res != pwlen)
> 		return (EIO);
> 
> 	close(fd[1]);
> 
> 	rpid = waitpid(pid, &s, 0);
> 	if (rpid != pid)
> 		return (errno);
> 	if (WEXITSTATUS(s))
> 		return (WEXITSTATUS(s));
> 
> 	return (0);
> }
> 
> PAM_EXTERN int
> pam_sm_authenticate(pam_handle_t *pamh, int flags,
> 		    int argc, const char ** argv)
> {
> 	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");

> Content-Description: helper.c
> #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;
> 	char *bufptr;
> 	size_t buflen;
> 
> 	if (argc != 2)
> 		return (EINVAL);
> 
> 	bufptr = pwbuf;
> 	buflen = sizeof(pwbuf);
> 	do {
> 		res = read(0, bufptr, buflen);

STDIN_FILENO is more readable.

> 		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);

Since crypt(3) says NULL is returned on failure, it wouldn't hurt to check
for NULL.

pwbuf is/must be NUL-terminated?
 
> 	return (EAUTH);
> }

-- 
Christian