1*6528d8bdStedu /* $OpenBSD: pwd_check.c,v 1.7 2004/03/14 22:53:18 tedu Exp $ */ 298cf3d57Sprovos /* 398cf3d57Sprovos * Copyright 2000 Niels Provos <provos@citi.umich.edu> 498cf3d57Sprovos * All rights reserved. 598cf3d57Sprovos * 698cf3d57Sprovos * Redistribution and use in source and binary forms, with or without 798cf3d57Sprovos * modification, are permitted provided that the following conditions 898cf3d57Sprovos * are met: 998cf3d57Sprovos * 1. Redistributions of source code must retain the above copyright 1098cf3d57Sprovos * notice, this list of conditions and the following disclaimer. 1198cf3d57Sprovos * 2. Redistributions in binary form must reproduce the above copyright 1298cf3d57Sprovos * notice, this list of conditions and the following disclaimer in the 1398cf3d57Sprovos * documentation and/or other materials provided with the distribution. 1498cf3d57Sprovos * 3. All advertising materials mentioning features or use of this software 1598cf3d57Sprovos * must display the following acknowledgement: 1698cf3d57Sprovos * This product includes software developed by Niels Provos. 1798cf3d57Sprovos * 4. The name of the author may not be used to endorse or promote products 1898cf3d57Sprovos * derived from this software without specific prior written permission. 1998cf3d57Sprovos * 2098cf3d57Sprovos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2198cf3d57Sprovos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2298cf3d57Sprovos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2398cf3d57Sprovos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2498cf3d57Sprovos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2598cf3d57Sprovos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2698cf3d57Sprovos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2798cf3d57Sprovos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2898cf3d57Sprovos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2998cf3d57Sprovos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3098cf3d57Sprovos */ 3198cf3d57Sprovos 3298cf3d57Sprovos #include <sys/types.h> 3398cf3d57Sprovos #include <sys/wait.h> 34f063c472Smillert 35f063c472Smillert #include <stdio.h> 3698cf3d57Sprovos #include <stdlib.h> 37f063c472Smillert #include <string.h> 3898cf3d57Sprovos #include <unistd.h> 3998cf3d57Sprovos #include <limits.h> 40f063c472Smillert #include <errno.h> 41b85939c3Smillert #include <err.h> 4298cf3d57Sprovos #include <regex.h> 4398cf3d57Sprovos #include <grp.h> 4498cf3d57Sprovos #include <paths.h> 4598cf3d57Sprovos #include <pwd.h> 46b85939c3Smillert #include <util.h> 47fc30494eSmillert #include <login_cap.h> 4898cf3d57Sprovos 4998cf3d57Sprovos struct pattern { 5098cf3d57Sprovos char *match; 5198cf3d57Sprovos int flags; 5298cf3d57Sprovos char *response; 5398cf3d57Sprovos }; 5498cf3d57Sprovos 5598cf3d57Sprovos struct pattern patterns[] = { 5698cf3d57Sprovos { 5798cf3d57Sprovos "^[0-9]*$", 5898cf3d57Sprovos REG_EXTENDED|REG_NOSUB, 5998cf3d57Sprovos "Please don't use all-digit passwords." 6098cf3d57Sprovos }, 6198cf3d57Sprovos { 6298cf3d57Sprovos "^[a-z]{1,9}$", 6398cf3d57Sprovos REG_EXTENDED|REG_NOSUB, 6498cf3d57Sprovos "Please don't use an all-lower case password." 6598cf3d57Sprovos }, 6698cf3d57Sprovos { 6798cf3d57Sprovos "^[a-z]{1,6}[0-9]+$", 6898cf3d57Sprovos REG_EXTENDED|REG_NOSUB|REG_ICASE, 6998cf3d57Sprovos "Please use a more complicated password." 7098cf3d57Sprovos }, 7198cf3d57Sprovos { 7298cf3d57Sprovos "^([a-z][0-9]){1,4}$", 7398cf3d57Sprovos REG_EXTENDED|REG_NOSUB|REG_ICASE, 7498cf3d57Sprovos "Please use a more complicated password." 7598cf3d57Sprovos }, 7698cf3d57Sprovos { 7798cf3d57Sprovos "^([0-9][a-z]){1,4}$", 7898cf3d57Sprovos REG_EXTENDED|REG_NOSUB|REG_ICASE, 7998cf3d57Sprovos "Please use a more complicated password." 8098cf3d57Sprovos } 8198cf3d57Sprovos }; 8298cf3d57Sprovos 8398cf3d57Sprovos int 84fc30494eSmillert pwd_check(struct passwd *pwd, login_cap_t *lc, char *password) 8598cf3d57Sprovos { 8698cf3d57Sprovos regex_t rgx; 87fc30494eSmillert int i, res, min_len; 88fc30494eSmillert char *cp, option[LINE_MAX]; 8998cf3d57Sprovos int pipefds[2]; 90*6528d8bdStedu pid_t child; 9198cf3d57Sprovos 92fc30494eSmillert min_len = (int) login_getcapnum(lc, "minpasswordlen", 6, 6); 93fc30494eSmillert if (min_len > 0 && strlen(password) < min_len) { 9498cf3d57Sprovos printf("Please enter a longer password.\n"); 9598cf3d57Sprovos return (0); 9698cf3d57Sprovos } 9798cf3d57Sprovos 9898cf3d57Sprovos for (i = 0; i < sizeof(patterns)/sizeof(struct pattern); i++) { 9998cf3d57Sprovos if (regcomp(&rgx, patterns[i].match, patterns[i].flags) != 0) 10098cf3d57Sprovos continue; 10198cf3d57Sprovos res = regexec(&rgx, password, 0, NULL, 0); 10298cf3d57Sprovos regfree(&rgx); 10398cf3d57Sprovos if (!res) { 10498cf3d57Sprovos printf("%s\nUnusual capitalization, control characters or digits are suggested.\n", patterns[i].response); 10598cf3d57Sprovos return (0); 10698cf3d57Sprovos } 10798cf3d57Sprovos } 10898cf3d57Sprovos 10998cf3d57Sprovos /* Okay, now pass control to an external program */ 110fc30494eSmillert 111fc30494eSmillert /* 112fc30494eSmillert * Check login.conf, falling back onto the deprecated passwd.conf 113fc30494eSmillert */ 114fc30494eSmillert if ((cp = login_getcapstr(lc, "passwordcheck", NULL, NULL)) != NULL) { 115fc30494eSmillert strlcpy(option, cp, sizeof(option)); 116fc30494eSmillert free(cp); 117fc30494eSmillert } else { 11898cf3d57Sprovos pw_getconf(option, LINE_MAX, pwd->pw_name, "pwdcheck"); 11998cf3d57Sprovos 12098cf3d57Sprovos /* Try to find an entry for the group */ 12198cf3d57Sprovos if (*option == 0) { 12298cf3d57Sprovos struct group *grp; 12398cf3d57Sprovos char grpkey[LINE_MAX]; 12498cf3d57Sprovos 12598cf3d57Sprovos grp = getgrgid(pwd->pw_gid); 12698cf3d57Sprovos if (grp != NULL) { 127ac597990Sderaadt snprintf(grpkey, LINE_MAX, ":%s", 12869af51b1Sitojun grp->gr_name); 12969af51b1Sitojun pw_getconf(option, LINE_MAX, grpkey, 13069af51b1Sitojun "pwdcheck"); 13169af51b1Sitojun } 13269af51b1Sitojun if (grp != NULL && *option == 0 && 13369af51b1Sitojun strchr(pwd->pw_name, '.') == NULL) { 134ac597990Sderaadt snprintf(grpkey, LINE_MAX, ".%s", 135fc30494eSmillert grp->gr_name); 136fc30494eSmillert pw_getconf(option, LINE_MAX, grpkey, 137fc30494eSmillert "pwdcheck"); 13898cf3d57Sprovos } 13998cf3d57Sprovos if (*option == 0) 140fc30494eSmillert pw_getconf(option, LINE_MAX, "default", 141fc30494eSmillert "pwdcheck"); 142fc30494eSmillert } 14398cf3d57Sprovos } 14498cf3d57Sprovos 14598cf3d57Sprovos /* If no checker is specified, we accept the password */ 14698cf3d57Sprovos if (*option == 0) 14798cf3d57Sprovos return (1); 14898cf3d57Sprovos 14998cf3d57Sprovos if (pipe(pipefds) == -1) { 15098cf3d57Sprovos warn("pipe"); 15198cf3d57Sprovos goto out; 15298cf3d57Sprovos } 15398cf3d57Sprovos 154*6528d8bdStedu child = fork(); 155*6528d8bdStedu if (child == 0) { 15698cf3d57Sprovos char *argp[] = { "sh", "-c", NULL, NULL}; 15798cf3d57Sprovos 15898cf3d57Sprovos /* Drop privileges */ 15998cf3d57Sprovos seteuid(getuid()); 16098cf3d57Sprovos setuid(getuid()); 16198cf3d57Sprovos 16298cf3d57Sprovos if (dup2(pipefds[0], STDIN_FILENO) == -1) 16398cf3d57Sprovos exit(1); 16498cf3d57Sprovos 16598cf3d57Sprovos close(pipefds[0]); 16698cf3d57Sprovos close(pipefds[1]); 16798cf3d57Sprovos 16898cf3d57Sprovos argp[2] = option; 16998cf3d57Sprovos if (execv(_PATH_BSHELL, argp) == -1) 17098cf3d57Sprovos exit(1); 17198cf3d57Sprovos /* NOT REACHED */ 172*6528d8bdStedu } else if (child == -1) { 17398cf3d57Sprovos warn("fork"); 17498cf3d57Sprovos goto out; 17598cf3d57Sprovos } 17698cf3d57Sprovos close(pipefds[0]); 17798cf3d57Sprovos 17898cf3d57Sprovos /* Send the password to STDIN of child */ 17998cf3d57Sprovos write(pipefds[1], password, strlen(password) + 1); 18098cf3d57Sprovos close(pipefds[1]); 18198cf3d57Sprovos 18298cf3d57Sprovos /* get the return value from the child */ 183*6528d8bdStedu wait(&child); 184*6528d8bdStedu if (WIFEXITED(child) && WEXITSTATUS(child) == 0) 18598cf3d57Sprovos return (1); 18698cf3d57Sprovos 18798cf3d57Sprovos out: 188ac597990Sderaadt printf("Please use a different password. Unusual capitalization,\n"); 189ac597990Sderaadt printf("control characters, or digits are suggested.\n"); 19098cf3d57Sprovos return (0); 19198cf3d57Sprovos } 19298cf3d57Sprovos 193ac597990Sderaadt int 194ac597990Sderaadt pwd_gettries(struct passwd *pwd, login_cap_t *lc) 195fc30494eSmillert { 19698cf3d57Sprovos char option[LINE_MAX]; 19798cf3d57Sprovos char *ep = option; 198fc30494eSmillert quad_t ntries; 199ac597990Sderaadt long lval; 200fc30494eSmillert 201fc30494eSmillert /* 202fc30494eSmillert * Check login.conf, falling back onto the deprecated passwd.conf 203fc30494eSmillert */ 204fc30494eSmillert if ((ntries = login_getcapnum(lc, "passwordtries", -1, -1)) != -1) { 205fc30494eSmillert if (ntries > INT_MAX || ntries < 0) { 206fc30494eSmillert fprintf(stderr, 207fc30494eSmillert "Warning: pwdtries out of range in /etc/login.conf"); 208fc30494eSmillert goto out; 209fc30494eSmillert } 210fc30494eSmillert return((int)ntries); 211fc30494eSmillert } 21298cf3d57Sprovos 21398cf3d57Sprovos pw_getconf(option, LINE_MAX, pwd->pw_name, "pwdtries"); 21498cf3d57Sprovos 21598cf3d57Sprovos /* Try to find an entry for the group */ 21698cf3d57Sprovos if (*option == 0) { 21798cf3d57Sprovos struct group *grp; 21898cf3d57Sprovos char grpkey[LINE_MAX]; 21998cf3d57Sprovos 22098cf3d57Sprovos grp = getgrgid(pwd->pw_gid); 22198cf3d57Sprovos if (grp != NULL) { 222ac597990Sderaadt snprintf(grpkey, LINE_MAX, ":%s", grp->gr_name); 22369af51b1Sitojun pw_getconf(option, LINE_MAX, grpkey, "pwdtries"); 22469af51b1Sitojun } 22569af51b1Sitojun if (grp != NULL && *option == 0 && 22669af51b1Sitojun strchr(pwd->pw_name, '.') == NULL) { 227ac597990Sderaadt snprintf(grpkey, LINE_MAX, ".%s", grp->gr_name); 22898cf3d57Sprovos pw_getconf(option, LINE_MAX, grpkey, "pwdtries"); 22998cf3d57Sprovos } 23098cf3d57Sprovos if (*option == 0) 23198cf3d57Sprovos pw_getconf(option, LINE_MAX, "default", "pwdtries"); 23298cf3d57Sprovos } 23398cf3d57Sprovos 23498cf3d57Sprovos if (*option == 0) 23598cf3d57Sprovos goto out; 236ac597990Sderaadt 23798cf3d57Sprovos errno = 0; 23898cf3d57Sprovos lval = strtol(option, &ep, 10); 23998cf3d57Sprovos if (option[0] == '\0' || *ep != '\0') { 24098cf3d57Sprovos fprintf(stderr, 24198cf3d57Sprovos "Warning: Bad pwdtries line in /etc/passwd.conf"); 24298cf3d57Sprovos goto out; 24398cf3d57Sprovos } 244ac597990Sderaadt if ((errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN)) || 245fc30494eSmillert (lval > INT_MAX || lval < 0)) { 24698cf3d57Sprovos fprintf(stderr, 24798cf3d57Sprovos "Warning: pwdtries out of range in /etc/passwd.conf"); 24898cf3d57Sprovos goto out; 24998cf3d57Sprovos } 25098cf3d57Sprovos return((int) lval); 25198cf3d57Sprovos 25298cf3d57Sprovos /* If no amount of tries is specified, return a default of 25398cf3d57Sprovos * 3, meaning that after 3 attempts where the user is foiled 25498cf3d57Sprovos * by the password checks, it will no longer be checked and 25598cf3d57Sprovos * they can set it to whatever they like. 25698cf3d57Sprovos */ 25798cf3d57Sprovos out: 25898cf3d57Sprovos return (3); 25998cf3d57Sprovos } 260