Index: libexec/Makefile =================================================================== --- libexec/Makefile +++ libexec/Makefile @@ -13,6 +13,7 @@ ${_mail.local} \ ${_makewhatis.local} \ ${_mknetid} \ + pam_unix-helper \ ${_phttpget} \ ${_pppoed} \ rc \ Index: libexec/pam_unix-helper/Makefile =================================================================== --- /dev/null +++ libexec/pam_unix-helper/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +PACKAGE= runtime + +PROG= pam_unix-helper +MAN= # no manpage, internally used by pam_unix.so + +LIBADD= crypt + +BINOWN= root +BINMODE=4555 + +.include Index: libexec/pam_unix-helper/pam_unix-helper.c =================================================================== --- /dev/null +++ libexec/pam_unix-helper/pam_unix-helper.c @@ -0,0 +1,117 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 Felix Palmen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include +#include + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: pam_unix-helper [-en] username\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + int ch; + uid_t ruid; + struct passwd *pwd; + const char *realpw; + char password[_PASSWORD_LEN + 2] = { '\0' }; + const char *emptypw = ""; + int eflag = 0; + int nflag = 0; + + /* sanity check: must run privileged */ + if (geteuid() != 0) return (1); + + /* + * sanity checks: must be invoked by normal user and stdin must be + * a pipe (invoked by pam_unix.so). + */ + ruid = getuid(); + if (ruid == 0 || isatty(STDIN_FILENO)) { + (void)fprintf(stderr, "This program is used by pam_unix.so, " + "don't invoke it directly!\n"); + return (1); + } + + while ((ch = getopt(argc, argv, "en")) != -1) + switch (ch) { + case 'e': + eflag = 1; + break; + case 'n': + nflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + if (argc != 1) usage(); + + /* read password database while privileged */ + if ((pwd = getpwnam(argv[0])) == NULL) return (1); + + /* drop privileges */ + if (setuid(ruid) != 0) return (1); + + /* only allow checking your own password */ + if (pwd->pw_uid != ruid) return (1); + + /* handle null and empty password cases */ + realpw = pwd->pw_passwd; + if (realpw[0] == '\0') { + if (nflag == 1) return (0); + realpw = "*"; + } + if (eflag == 1 && strcmp(crypt(emptypw, realpw), realpw) == 0) + return (0); + + /* read user password from stdin */ + if (read(STDIN_FILENO, password, sizeof(password) - 1) < 1) + return (1); + + if (strnlen(password, _PASSWORD_LEN + 1) > _PASSWORD_LEN) + realpw = "*"; + + if (strcmp(crypt(password, realpw), realpw) == 0) + return (0); + + return (1); +}