Annotation of src/usr.bin/passwd/pwd_check.c, Revision 1.7
1.7 ! tedu 1: /* $OpenBSD: pwd_check.c,v 1.6 2002/06/28 22:28:17 deraadt Exp $ */
1.1 provos 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>
1.2 millert 34:
35: #include <stdio.h>
1.1 provos 36: #include <stdlib.h>
1.2 millert 37: #include <string.h>
1.1 provos 38: #include <unistd.h>
39: #include <limits.h>
1.2 millert 40: #include <errno.h>
1.3 millert 41: #include <err.h>
1.1 provos 42: #include <regex.h>
43: #include <grp.h>
44: #include <paths.h>
45: #include <pwd.h>
1.3 millert 46: #include <util.h>
1.4 millert 47: #include <login_cap.h>
1.1 provos 48:
49: struct pattern {
50: char *match;
51: int flags;
52: char *response;
53: };
54:
55: struct pattern patterns[] = {
56: {
1.6 deraadt 57: "^[0-9]*$",
1.1 provos 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
1.4 millert 84: pwd_check(struct passwd *pwd, login_cap_t *lc, char *password)
1.1 provos 85: {
86: regex_t rgx;
1.4 millert 87: int i, res, min_len;
88: char *cp, option[LINE_MAX];
1.1 provos 89: int pipefds[2];
1.7 ! tedu 90: pid_t child;
1.6 deraadt 91:
1.4 millert 92: min_len = (int) login_getcapnum(lc, "minpasswordlen", 6, 6);
93: if (min_len > 0 && strlen(password) < min_len) {
1.1 provos 94: printf("Please enter a longer password.\n");
95: return (0);
96: }
97:
98: for (i = 0; i < sizeof(patterns)/sizeof(struct pattern); i++) {
99: if (regcomp(&rgx, patterns[i].match, patterns[i].flags) != 0)
100: continue;
101: res = regexec(&rgx, password, 0, NULL, 0);
102: regfree(&rgx);
103: if (!res) {
104: printf("%s\nUnusual capitalization, control characters or digits are suggested.\n", patterns[i].response);
105: return (0);
106: }
107: }
108:
109: /* Okay, now pass control to an external program */
110:
1.4 millert 111: /*
112: * Check login.conf, falling back onto the deprecated passwd.conf
113: */
114: if ((cp = login_getcapstr(lc, "passwordcheck", NULL, NULL)) != NULL) {
115: strlcpy(option, cp, sizeof(option));
116: free(cp);
117: } else {
118: pw_getconf(option, LINE_MAX, pwd->pw_name, "pwdcheck");
119:
120: /* Try to find an entry for the group */
121: if (*option == 0) {
122: struct group *grp;
123: char grpkey[LINE_MAX];
124:
125: grp = getgrgid(pwd->pw_gid);
126: if (grp != NULL) {
1.6 deraadt 127: snprintf(grpkey, LINE_MAX, ":%s",
1.5 itojun 128: grp->gr_name);
129: pw_getconf(option, LINE_MAX, grpkey,
130: "pwdcheck");
131: }
132: if (grp != NULL && *option == 0 &&
133: strchr(pwd->pw_name, '.') == NULL) {
1.6 deraadt 134: snprintf(grpkey, LINE_MAX, ".%s",
1.4 millert 135: grp->gr_name);
136: pw_getconf(option, LINE_MAX, grpkey,
137: "pwdcheck");
138: }
139: if (*option == 0)
140: pw_getconf(option, LINE_MAX, "default",
141: "pwdcheck");
1.1 provos 142: }
143: }
1.6 deraadt 144:
1.1 provos 145: /* If no checker is specified, we accept the password */
146: if (*option == 0)
147: return (1);
148:
149: if (pipe(pipefds) == -1) {
150: warn("pipe");
151: goto out;
152: }
153:
1.7 ! tedu 154: child = fork();
! 155: if (child == 0) {
1.1 provos 156: char *argp[] = { "sh", "-c", NULL, NULL};
1.6 deraadt 157:
1.1 provos 158: /* Drop privileges */
159: seteuid(getuid());
160: setuid(getuid());
161:
162: if (dup2(pipefds[0], STDIN_FILENO) == -1)
163: exit(1);
164:
165: close(pipefds[0]);
166: close(pipefds[1]);
167:
168: argp[2] = option;
169: if (execv(_PATH_BSHELL, argp) == -1)
170: exit(1);
171: /* NOT REACHED */
1.7 ! tedu 172: } else if (child == -1) {
1.1 provos 173: warn("fork");
174: goto out;
175: }
176: close(pipefds[0]);
177:
178: /* Send the password to STDIN of child */
179: write(pipefds[1], password, strlen(password) + 1);
180: close(pipefds[1]);
181:
182: /* get the return value from the child */
1.7 ! tedu 183: wait(&child);
! 184: if (WIFEXITED(child) && WEXITSTATUS(child) == 0)
1.1 provos 185: return (1);
186:
187: out:
1.6 deraadt 188: printf("Please use a different password. Unusual capitalization,\n");
189: printf("control characters, or digits are suggested.\n");
1.1 provos 190: return (0);
191: }
192:
1.6 deraadt 193: int
1.7 ! tedu 194: pwd_gettries(struct passwd *pwd, login_cap_t *lc)
1.4 millert 195: {
1.1 provos 196: char option[LINE_MAX];
1.6 deraadt 197: char *ep = option;
1.4 millert 198: quad_t ntries;
1.6 deraadt 199: long lval;
1.4 millert 200:
201: /*
202: * Check login.conf, falling back onto the deprecated passwd.conf
203: */
204: if ((ntries = login_getcapnum(lc, "passwordtries", -1, -1)) != -1) {
205: if (ntries > INT_MAX || ntries < 0) {
206: fprintf(stderr,
1.6 deraadt 207: "Warning: pwdtries out of range in /etc/login.conf");
1.4 millert 208: goto out;
209: }
210: return((int)ntries);
211: }
1.1 provos 212:
213: pw_getconf(option, LINE_MAX, pwd->pw_name, "pwdtries");
214:
215: /* Try to find an entry for the group */
216: if (*option == 0) {
1.4 millert 217: struct group *grp;
218: char grpkey[LINE_MAX];
1.1 provos 219:
1.4 millert 220: grp = getgrgid(pwd->pw_gid);
221: if (grp != NULL) {
1.6 deraadt 222: snprintf(grpkey, LINE_MAX, ":%s", grp->gr_name);
1.5 itojun 223: pw_getconf(option, LINE_MAX, grpkey, "pwdtries");
224: }
225: if (grp != NULL && *option == 0 &&
226: strchr(pwd->pw_name, '.') == NULL) {
1.6 deraadt 227: snprintf(grpkey, LINE_MAX, ".%s", grp->gr_name);
1.1 provos 228: pw_getconf(option, LINE_MAX, grpkey, "pwdtries");
229: }
230: if (*option == 0)
1.4 millert 231: pw_getconf(option, LINE_MAX, "default", "pwdtries");
1.1 provos 232: }
1.6 deraadt 233:
1.1 provos 234: if (*option == 0)
235: goto out;
1.6 deraadt 236:
237: errno = 0;
238: lval = strtol(option, &ep, 10);
239: if (option[0] == '\0' || *ep != '\0') {
240: fprintf(stderr,
241: "Warning: Bad pwdtries line in /etc/passwd.conf");
242: goto out;
243: }
244: if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) ||
245: (lval > INT_MAX || lval < 0)) {
246: fprintf(stderr,
247: "Warning: pwdtries out of range in /etc/passwd.conf");
248: goto out;
1.1 provos 249: }
1.6 deraadt 250: return((int) lval);
1.1 provos 251:
1.6 deraadt 252: /* If no amount of tries is specified, return a default of
1.1 provos 253: * 3, meaning that after 3 attempts where the user is foiled
254: * by the password checks, it will no longer be checked and
255: * they can set it to whatever they like.
256: */
257: out:
258: return (3);
259: }