Annotation of src/usr.bin/passwd/pwd_check.c, Revision 1.6
1.6 ! deraadt 1: /* $OpenBSD: pwd_check.c,v 1.5 2002/05/27 21:12:54 itojun 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.6 ! deraadt 90:
1.4 millert 91: min_len = (int) login_getcapnum(lc, "minpasswordlen", 6, 6);
92: if (min_len > 0 && strlen(password) < min_len) {
1.1 provos 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:
1.4 millert 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) {
1.6 ! deraadt 126: snprintf(grpkey, LINE_MAX, ":%s",
1.5 itojun 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) {
1.6 ! deraadt 133: snprintf(grpkey, LINE_MAX, ".%s",
1.4 millert 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");
1.1 provos 141: }
142: }
1.6 ! deraadt 143:
1.1 provos 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};
1.6 ! deraadt 156:
1.1 provos 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:
1.6 ! deraadt 187: printf("Please use a different password. Unusual capitalization,\n");
! 188: printf("control characters, or digits are suggested.\n");
1.1 provos 189: return (0);
190: }
191:
1.6 ! deraadt 192: int
! 193: pwd_gettries( struct passwd *pwd, login_cap_t *lc )
1.4 millert 194: {
1.1 provos 195: char option[LINE_MAX];
1.6 ! deraadt 196: char *ep = option;
1.4 millert 197: quad_t ntries;
1.6 ! deraadt 198: long lval;
1.4 millert 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,
1.6 ! deraadt 206: "Warning: pwdtries out of range in /etc/login.conf");
1.4 millert 207: goto out;
208: }
209: return((int)ntries);
210: }
1.1 provos 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) {
1.4 millert 216: struct group *grp;
217: char grpkey[LINE_MAX];
1.1 provos 218:
1.4 millert 219: grp = getgrgid(pwd->pw_gid);
220: if (grp != NULL) {
1.6 ! deraadt 221: snprintf(grpkey, LINE_MAX, ":%s", grp->gr_name);
1.5 itojun 222: pw_getconf(option, LINE_MAX, grpkey, "pwdtries");
223: }
224: if (grp != NULL && *option == 0 &&
225: strchr(pwd->pw_name, '.') == NULL) {
1.6 ! deraadt 226: snprintf(grpkey, LINE_MAX, ".%s", grp->gr_name);
1.1 provos 227: pw_getconf(option, LINE_MAX, grpkey, "pwdtries");
228: }
229: if (*option == 0)
1.4 millert 230: pw_getconf(option, LINE_MAX, "default", "pwdtries");
1.1 provos 231: }
1.6 ! deraadt 232:
1.1 provos 233: if (*option == 0)
234: goto out;
1.6 ! deraadt 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;
1.1 provos 248: }
1.6 ! deraadt 249: return((int) lval);
1.1 provos 250:
1.6 ! deraadt 251: /* If no amount of tries is specified, return a default of
1.1 provos 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: }