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
49 int pwd_check(login_cap_t *, char *);
50 int pwd_gettries(login_cap_t *);
51
52 struct pattern {
53 char *match;
54 int flags;
55 char *response;
56 };
57
58 struct 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
86 int
pwd_check(login_cap_t * lc,char * password)87 pwd_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
207 int
pwd_gettries(login_cap_t * lc)208 pwd_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