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