1/* $OpenBSD: pwd_check.c,v 1.19 2024/05/24 13:32:03 op Exp $ */ 2 3/* 4 * Copyright 2000 Niels Provos <provos@citi.umich.edu> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Niels Provos. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33#include <sys/types.h> 34#include <sys/wait.h> 35 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <unistd.h> 40#include <limits.h> 41#include <errno.h> 42#include <err.h> 43#include <regex.h> 44#include <grp.h> 45#include <paths.h> 46#include <login_cap.h> 47#include <signal.h> 48 49int pwd_check(login_cap_t *, char *); 50int pwd_gettries(login_cap_t *); 51 52struct pattern { 53 char *match; 54 int flags; 55 char *response; 56}; 57 58struct pattern patterns[] = { 59 { 60 "^[0-9]*$", 61 REG_EXTENDED|REG_NOSUB, 62 "Please don't use all-digit passwords." 63 }, 64 { 65 "^[a-z]{1,9}$", 66 REG_EXTENDED|REG_NOSUB, 67 "Please don't use an all-lower case password." 68 }, 69 { 70 "^[a-z]{1,6}[0-9]+$", 71 REG_EXTENDED|REG_NOSUB|REG_ICASE, 72 "Please use a more complicated password." 73 }, 74 { 75 "^([a-z][0-9]){1,4}$", 76 REG_EXTENDED|REG_NOSUB|REG_ICASE, 77 "Please use a more complicated password." 78 }, 79 { 80 "^([0-9][a-z]){1,4}$", 81 REG_EXTENDED|REG_NOSUB|REG_ICASE, 82 "Please use a more complicated password." 83 } 84}; 85 86int 87pwd_check(login_cap_t *lc, char *password) 88{ 89 regex_t rgx; 90 int i, res, min_len; 91 char *checker; 92 char *argp[] = { "sh", "-c", NULL, NULL}; 93 int pipefds[2]; 94 pid_t child; 95 uid_t uid; 96 gid_t gid; 97 98 min_len = (int)login_getcapnum(lc, "minpasswordlen", 6, 6); 99 if (min_len > 0 && strlen(password) < min_len) { 100 fprintf(stderr, "Please enter a longer password.\n"); 101 return (0); 102 } 103 104 /* External password check program */ 105 checker = login_getcapstr(lc, "passwordcheck", NULL, NULL); 106 107 /* Pipes are only used for external checker */ 108 if (checker != NULL && pipe(pipefds) == -1) { 109 warn("pipe"); 110 goto out; 111 } 112 113 /* Check password in low-privileged child */ 114 switch (child = fork()) { 115 case -1: 116 warn("fork"); 117 close(pipefds[0]); 118 close(pipefds[1]); 119 goto out; 120 case 0: 121 (void)signal(SIGINT, SIG_DFL); 122 (void)signal(SIGQUIT, SIG_DFL); 123 uid = getuid(); 124 gid = getgid(); 125 if (setresgid(gid, gid, gid) == -1) { 126 warn("setresgid"); 127 exit(1); 128 } 129 if (setgroups(1, &gid) == -1) { 130 warn("setgroups"); 131 exit(1); 132 } 133 if (setresuid(uid, uid, uid) == -1) { 134 warn("setresuid"); 135 exit(1); 136 } 137 138 if (checker == NULL) { 139 if (pledge("stdio", NULL) == -1) 140 err(1, "pledge"); 141 142 for (i = 0; i < sizeof(patterns) / sizeof(*patterns); i++) { 143 int ret; 144 145 if (regcomp(&rgx, patterns[i].match, 146 patterns[i].flags) != 0) 147 continue; 148 ret = regexec(&rgx, password, 0, NULL, 0); 149 regfree(&rgx); 150 if (ret == 0) { 151 fprintf(stderr, "%s\n", patterns[i].response); 152 exit(1); 153 } 154 } 155 /* no external checker in use, accept the password */ 156 exit(0); 157 } 158 159 if (pledge("stdio exec", NULL) == -1) 160 err(1, "pledge"); 161 162 /* Otherwise, pass control to checker program */ 163 argp[2] = checker; 164 if (dup2(pipefds[0], STDIN_FILENO) == -1) { 165 warn("dup2"); 166 exit(1); 167 } 168 close(pipefds[0]); 169 close(pipefds[1]); 170 171 if (execv(_PATH_BSHELL, argp) == -1) { 172 warn("exec"); 173 exit(1); 174 } 175 /* NOTREACHED */ 176 default: 177 break; /* parent continues below */ 178 } 179 180 if (checker != NULL) { 181 /* Send the password to STDIN of child */ 182 close(pipefds[0]); 183 write(pipefds[1], password, strlen(password) + 1); 184 close(pipefds[1]); 185 } 186 187 /* get the return value from the child */ 188 while (waitpid(child, &res, 0) == -1) { 189 if (errno != EINTR) { 190 warn("waitpid"); 191 goto out; 192 } 193 } 194 if (WIFEXITED(res) && WEXITSTATUS(res) == 0) { 195 free(checker); 196 return (1); 197 } 198 199 out: 200 free(checker); 201 fprintf(stderr, "Please use a different password. Unusual capitalization,\n"); 202 fprintf(stderr, "control characters, or digits are suggested.\n"); 203 204 return (0); 205} 206 207int 208pwd_gettries(login_cap_t *lc) 209{ 210 quad_t ntries; 211 212 if ((ntries = login_getcapnum(lc, "passwordtries", -1, -1)) != -1) { 213 if (ntries >= 0 && ntries <= INT_MAX) 214 return((int)ntries); 215 fprintf(stderr, 216 "Warning: passwordtries out of range in /etc/login.conf"); 217 } 218 219 /* 220 * If no amount of tries is specified, return a default of 3, 221 * meaning that after 3 attempts where the user is foiled by the 222 * password checks, it will no longer be checked and they can set 223 * it to whatever they like. This is the historic BSD behavior. 224 */ 225 return (3); 226} 227