1*e8059197Sop /* $OpenBSD: pwd_check.c,v 1.19 2024/05/24 13:32:03 op Exp $ */
25bb578b1Smillert
398cf3d57Sprovos /*
498cf3d57Sprovos * Copyright 2000 Niels Provos <provos@citi.umich.edu>
598cf3d57Sprovos * All rights reserved.
698cf3d57Sprovos *
798cf3d57Sprovos * Redistribution and use in source and binary forms, with or without
898cf3d57Sprovos * modification, are permitted provided that the following conditions
998cf3d57Sprovos * are met:
1098cf3d57Sprovos * 1. Redistributions of source code must retain the above copyright
1198cf3d57Sprovos * notice, this list of conditions and the following disclaimer.
1298cf3d57Sprovos * 2. Redistributions in binary form must reproduce the above copyright
1398cf3d57Sprovos * notice, this list of conditions and the following disclaimer in the
1498cf3d57Sprovos * documentation and/or other materials provided with the distribution.
1598cf3d57Sprovos * 3. All advertising materials mentioning features or use of this software
1698cf3d57Sprovos * must display the following acknowledgement:
1798cf3d57Sprovos * This product includes software developed by Niels Provos.
1898cf3d57Sprovos * 4. The name of the author may not be used to endorse or promote products
1998cf3d57Sprovos * derived from this software without specific prior written permission.
2098cf3d57Sprovos *
2198cf3d57Sprovos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2298cf3d57Sprovos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2398cf3d57Sprovos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2498cf3d57Sprovos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2598cf3d57Sprovos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2698cf3d57Sprovos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2798cf3d57Sprovos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2898cf3d57Sprovos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2998cf3d57Sprovos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3098cf3d57Sprovos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3198cf3d57Sprovos */
3298cf3d57Sprovos
3398cf3d57Sprovos #include <sys/types.h>
3498cf3d57Sprovos #include <sys/wait.h>
35f063c472Smillert
36f063c472Smillert #include <stdio.h>
3798cf3d57Sprovos #include <stdlib.h>
38f063c472Smillert #include <string.h>
3998cf3d57Sprovos #include <unistd.h>
4098cf3d57Sprovos #include <limits.h>
41f063c472Smillert #include <errno.h>
42b85939c3Smillert #include <err.h>
4398cf3d57Sprovos #include <regex.h>
4498cf3d57Sprovos #include <grp.h>
4598cf3d57Sprovos #include <paths.h>
46fc30494eSmillert #include <login_cap.h>
477813863fSdjm #include <signal.h>
4898cf3d57Sprovos
49a661090bSdjm int pwd_check(login_cap_t *, char *);
50a661090bSdjm int pwd_gettries(login_cap_t *);
51a661090bSdjm
5298cf3d57Sprovos struct pattern {
5398cf3d57Sprovos char *match;
5498cf3d57Sprovos int flags;
5598cf3d57Sprovos char *response;
5698cf3d57Sprovos };
5798cf3d57Sprovos
5898cf3d57Sprovos struct pattern patterns[] = {
5998cf3d57Sprovos {
6098cf3d57Sprovos "^[0-9]*$",
6198cf3d57Sprovos REG_EXTENDED|REG_NOSUB,
6298cf3d57Sprovos "Please don't use all-digit passwords."
6398cf3d57Sprovos },
6498cf3d57Sprovos {
6598cf3d57Sprovos "^[a-z]{1,9}$",
6698cf3d57Sprovos REG_EXTENDED|REG_NOSUB,
6798cf3d57Sprovos "Please don't use an all-lower case password."
6898cf3d57Sprovos },
6998cf3d57Sprovos {
7098cf3d57Sprovos "^[a-z]{1,6}[0-9]+$",
7198cf3d57Sprovos REG_EXTENDED|REG_NOSUB|REG_ICASE,
7298cf3d57Sprovos "Please use a more complicated password."
7398cf3d57Sprovos },
7498cf3d57Sprovos {
7598cf3d57Sprovos "^([a-z][0-9]){1,4}$",
7698cf3d57Sprovos REG_EXTENDED|REG_NOSUB|REG_ICASE,
7798cf3d57Sprovos "Please use a more complicated password."
7898cf3d57Sprovos },
7998cf3d57Sprovos {
8098cf3d57Sprovos "^([0-9][a-z]){1,4}$",
8198cf3d57Sprovos REG_EXTENDED|REG_NOSUB|REG_ICASE,
8298cf3d57Sprovos "Please use a more complicated password."
8398cf3d57Sprovos }
8498cf3d57Sprovos };
8598cf3d57Sprovos
8698cf3d57Sprovos int
pwd_check(login_cap_t * lc,char * password)870608549dSmillert pwd_check(login_cap_t *lc, char *password)
8898cf3d57Sprovos {
8998cf3d57Sprovos regex_t rgx;
90fc30494eSmillert int i, res, min_len;
915bb578b1Smillert char *checker;
927813863fSdjm char *argp[] = { "sh", "-c", NULL, NULL};
9398cf3d57Sprovos int pipefds[2];
946528d8bdStedu pid_t child;
957813863fSdjm uid_t uid;
967813863fSdjm gid_t gid;
9798cf3d57Sprovos
98fc30494eSmillert min_len = (int)login_getcapnum(lc, "minpasswordlen", 6, 6);
99fc30494eSmillert if (min_len > 0 && strlen(password) < min_len) {
1002dcc237cSrobert fprintf(stderr, "Please enter a longer password.\n");
10198cf3d57Sprovos return (0);
10298cf3d57Sprovos }
10398cf3d57Sprovos
1047813863fSdjm /* External password check program */
1055bb578b1Smillert checker = login_getcapstr(lc, "passwordcheck", NULL, NULL);
10698cf3d57Sprovos
1077813863fSdjm /* Pipes are only used for external checker */
1087813863fSdjm if (checker != NULL && pipe(pipefds) == -1) {
10998cf3d57Sprovos warn("pipe");
11098cf3d57Sprovos goto out;
11198cf3d57Sprovos }
11298cf3d57Sprovos
1137813863fSdjm /* Check password in low-privileged child */
1147813863fSdjm switch (child = fork()) {
1157813863fSdjm case -1:
11698cf3d57Sprovos warn("fork");
117409c6020Stobias close(pipefds[0]);
118409c6020Stobias close(pipefds[1]);
11998cf3d57Sprovos goto out;
1207813863fSdjm case 0:
1217813863fSdjm (void)signal(SIGINT, SIG_DFL);
1227813863fSdjm (void)signal(SIGQUIT, SIG_DFL);
1237813863fSdjm uid = getuid();
1247813863fSdjm gid = getgid();
1257813863fSdjm if (setresgid(gid, gid, gid) == -1) {
1267813863fSdjm warn("setresgid");
1277813863fSdjm exit(1);
1287813863fSdjm }
1297813863fSdjm if (setgroups(1, &gid) == -1) {
1307813863fSdjm warn("setgroups");
1317813863fSdjm exit(1);
1327813863fSdjm }
1337813863fSdjm if (setresuid(uid, uid, uid) == -1) {
1347813863fSdjm warn("setresuid");
1357813863fSdjm exit(1);
1367813863fSdjm }
1377813863fSdjm
1387d4d297fSajacoutot if (checker == NULL) {
139a21fe559Sderaadt if (pledge("stdio", NULL) == -1)
140a21fe559Sderaadt err(1, "pledge");
141a21fe559Sderaadt
1427813863fSdjm for (i = 0; i < sizeof(patterns) / sizeof(*patterns); i++) {
1432aeb6b04Sderaadt int ret;
1442aeb6b04Sderaadt
1457813863fSdjm if (regcomp(&rgx, patterns[i].match,
1467813863fSdjm patterns[i].flags) != 0)
1477813863fSdjm continue;
1482aeb6b04Sderaadt ret = regexec(&rgx, password, 0, NULL, 0);
1497813863fSdjm regfree(&rgx);
1502aeb6b04Sderaadt if (ret == 0) {
1512dcc237cSrobert fprintf(stderr, "%s\n", patterns[i].response);
1527813863fSdjm exit(1);
1537813863fSdjm }
1547813863fSdjm }
1557d4d297fSajacoutot /* no external checker in use, accept the password */
1567813863fSdjm exit(0);
1577d4d297fSajacoutot }
1587813863fSdjm
159a21fe559Sderaadt if (pledge("stdio exec", NULL) == -1)
160a21fe559Sderaadt err(1, "pledge");
161a21fe559Sderaadt
1627813863fSdjm /* Otherwise, pass control to checker program */
1637813863fSdjm argp[2] = checker;
1647813863fSdjm if (dup2(pipefds[0], STDIN_FILENO) == -1) {
1657813863fSdjm warn("dup2");
1667813863fSdjm exit(1);
16798cf3d57Sprovos }
16898cf3d57Sprovos close(pipefds[0]);
1697813863fSdjm close(pipefds[1]);
17098cf3d57Sprovos
1717813863fSdjm if (execv(_PATH_BSHELL, argp) == -1) {
1727813863fSdjm warn("exec");
1737813863fSdjm exit(1);
1747813863fSdjm }
1757813863fSdjm /* NOTREACHED */
1767813863fSdjm default:
1777813863fSdjm break; /* parent continues below */
1787813863fSdjm }
1797813863fSdjm
1807813863fSdjm if (checker != NULL) {
18198cf3d57Sprovos /* Send the password to STDIN of child */
1827813863fSdjm close(pipefds[0]);
18398cf3d57Sprovos write(pipefds[1], password, strlen(password) + 1);
18498cf3d57Sprovos close(pipefds[1]);
1857813863fSdjm }
18698cf3d57Sprovos
18798cf3d57Sprovos /* get the return value from the child */
1882aeb6b04Sderaadt while (waitpid(child, &res, 0) == -1) {
189409c6020Stobias if (errno != EINTR) {
190409c6020Stobias warn("waitpid");
191409c6020Stobias goto out;
192409c6020Stobias }
1932aeb6b04Sderaadt }
1942aeb6b04Sderaadt if (WIFEXITED(res) && WEXITSTATUS(res) == 0) {
1955bb578b1Smillert free(checker);
19698cf3d57Sprovos return (1);
1975bb578b1Smillert }
19898cf3d57Sprovos
19998cf3d57Sprovos out:
2005bb578b1Smillert free(checker);
2012dcc237cSrobert fprintf(stderr, "Please use a different password. Unusual capitalization,\n");
2022dcc237cSrobert fprintf(stderr, "control characters, or digits are suggested.\n");
2037813863fSdjm
20498cf3d57Sprovos return (0);
20598cf3d57Sprovos }
20698cf3d57Sprovos
207ac597990Sderaadt int
pwd_gettries(login_cap_t * lc)2080608549dSmillert pwd_gettries(login_cap_t *lc)
209fc30494eSmillert {
210fc30494eSmillert quad_t ntries;
211fc30494eSmillert
212fc30494eSmillert if ((ntries = login_getcapnum(lc, "passwordtries", -1, -1)) != -1) {
213184d11c7Smoritz if (ntries >= 0 && ntries <= INT_MAX)
2145bb578b1Smillert return((int)ntries);
215fc30494eSmillert fprintf(stderr,
216*e8059197Sop "Warning: passwordtries out of range in /etc/login.conf");
217fc30494eSmillert }
21898cf3d57Sprovos
2190608549dSmillert /*
2205bb578b1Smillert * If no amount of tries is specified, return a default of 3,
2215bb578b1Smillert * meaning that after 3 attempts where the user is foiled by the
2225bb578b1Smillert * password checks, it will no longer be checked and they can set
2235bb578b1Smillert * it to whatever they like. This is the historic BSD behavior.
22498cf3d57Sprovos */
22598cf3d57Sprovos return (3);
22698cf3d57Sprovos }
227