1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/stat.h> 30 #include <stdio.h> 31 #include <syslog.h> 32 #include <fcntl.h> 33 #include <time.h> 34 #include <errno.h> 35 #include <signal.h> 36 #include "packer.h" 37 38 static int lockfd = -1; 39 static struct flock flock = { 0, 0, 0, 0, 0, 0 }; 40 41 char dblock[PATH_MAX]; 42 43 #define LOCK_WAIT 60 44 static int timedout = 0; 45 46 /*ARGSUSED*/ 47 void 48 alarm_handler(int sig) 49 { 50 timedout = 1; 51 } 52 53 /* 54 * lock_db() 55 * 56 * Create a lockfile to prevent simultaneous access to the database 57 * creation routines. We set a timeout to LOCK_WAIT seconds. If we 58 * haven't obtained a lock by that time, we bail out. 59 * 60 * returns 0 on succes, -1 on (lock) failure. 61 * side effect: the directory "path" will be created if it didn't exist. 62 */ 63 int 64 lock_db(char *path) 65 { 66 void (*oldhandler)(int); 67 int retval; 68 struct stat st; 69 70 /* create directory "path" if it doesn't exist */ 71 if (stat(path, &st) == -1) { 72 if (errno != ENOENT || 73 (mkdir(path, 0755) == -1 || chmod(path, 0755) == -1)) 74 return (-1); 75 } 76 77 (void) snprintf(dblock, sizeof (dblock), "%s/authtok_check.lock", path); 78 79 if ((lockfd = open(dblock, O_WRONLY|O_CREAT|O_EXCL, 0400)) == -1) { 80 if (errno == EEXIST) 81 lockfd = open(dblock, O_WRONLY); 82 if (lockfd == -1) { 83 int olderrno = errno; 84 syslog(LOG_ERR, "pam_authtok_check::pam_sm_chauthtok: " 85 "can't open lockfile: %s", strerror(errno)); 86 errno = olderrno; 87 return (-1); 88 } 89 } 90 91 flock.l_type = F_WRLCK; 92 oldhandler = sigset(SIGALRM, alarm_handler); 93 (void) alarm(LOCK_WAIT); 94 retval = fcntl(lockfd, F_SETLKW, &flock); 95 (void) alarm(0); 96 (void) sigset(SIGALRM, oldhandler); 97 98 if (timedout) { 99 syslog(LOG_ERR, "pam_authtok_check::pam_sm_chauthtok: timeout " 100 "waiting for dictionary lock."); 101 timedout = 0; 102 } 103 104 return (retval); 105 } 106 107 /* 108 * unlock_db() 109 * 110 * Release the database lock 111 */ 112 void 113 unlock_db(void) 114 { 115 if (lockfd != -1) { 116 flock.l_type = F_UNLCK; 117 (void) fcntl(lockfd, F_SETLK, &flock); 118 (void) close(lockfd); 119 lockfd = -1; 120 } 121 } 122 123 /* 124 * database_present() 125 * 126 * returns 0 if the database files are found, and the database size is 127 * greater than 0 128 */ 129 int 130 database_present(char *path) 131 { 132 struct stat st; 133 char dict_hwm[PATH_MAX]; 134 char dict_pwd[PATH_MAX]; 135 char dict_pwi[PATH_MAX]; 136 137 (void) snprintf(dict_hwm, sizeof (dict_hwm), "%s/%s", path, 138 DICT_DATABASE_HWM); 139 (void) snprintf(dict_pwd, sizeof (dict_pwd), "%s/%s", path, 140 DICT_DATABASE_PWD); 141 (void) snprintf(dict_pwi, sizeof (dict_pwi), "%s/%s", path, 142 DICT_DATABASE_PWI); 143 144 if (stat(dict_hwm, &st) == -1 || 145 (stat(dict_pwd, &st) == -1 || st.st_size == 0) || 146 stat(dict_pwi, &st) == -1) 147 return (NO_DICTDATABASE); 148 149 return (0); 150 } 151 152 /* 153 * build_dict_database(list, char *path) 154 * 155 * Create the Crack Dictionary Database based on the list of sources 156 * dictionaries specified in "list". Store the database in "path". 157 */ 158 int 159 build_dict_database(char *list, char *path) 160 { 161 return (packer(list, path) == -1 ? DICTDATABASE_BUILD_ERR : 0); 162 } 163 164 /* 165 * Rebuild the database in "path" if the database is older than one of the 166 * files listed in "list", or older than the config-file PWADMIN. 167 */ 168 int 169 update_dict_database(char *list, char *path) 170 { 171 struct stat st_db; 172 struct stat st_file; 173 char *buf; 174 char *listcopy; 175 boolean_t update_needed = B_FALSE; 176 char dbase_pwd[PATH_MAX]; 177 178 (void) snprintf(dbase_pwd, sizeof (dbase_pwd), "%s/%s", path, 179 DICT_DATABASE_PWD); 180 181 if (stat(dbase_pwd, &st_db) == -1) 182 return (DICTFILE_ERR); 183 184 if ((listcopy = strdup(list)) == NULL) 185 return (DICTFILE_ERR); 186 187 buf = strtok(listcopy, "\t ,"); 188 189 /* Compare mtime of each listed dictionary against DB mtime */ 190 while (update_needed == B_FALSE && buf != NULL) { 191 if (stat(buf, &st_file) == -1) { 192 if (errno == ENOENT) { 193 syslog(LOG_ERR, 194 "pam_authtok_check:update_dict_database: " 195 "dictionary \"%s\" not present.", buf); 196 } else { 197 syslog(LOG_ERR, 198 "pam_authtok_check:update_dict_database: " 199 "stat(%s) failed: %s", buf, 200 strerror(errno)); 201 } 202 free(listcopy); 203 return (DICTFILE_ERR); 204 } 205 if (st_db.st_mtime < st_file.st_mtime) 206 update_needed = B_TRUE; /* database out of date */ 207 buf = strtok(NULL, "\t ,"); 208 } 209 210 free(listcopy); 211 212 /* 213 * If /etc/default/passwd is updated, generate the database. 214 * Because this is the only way we can check if files have been 215 * added/deleted from DICTIONLIST. 216 * Drawback: the database will also be generated when other 217 * tunables are changed. 218 */ 219 if (stat(PWADMIN, &st_file) != -1 && st_db.st_mtime < st_file.st_mtime) 220 update_needed = B_TRUE; 221 222 if (update_needed) { 223 /* 224 * Since we actually rebuild the database, we need to remove 225 * the old database first. 226 */ 227 PWRemove(path); 228 return (build_dict_database(list, path)); 229 } 230 231 return (0); 232 } 233 234 /* 235 * Build or update database, while holding the global lock. 236 */ 237 int 238 make_dict_database(char *list, char *path) 239 { 240 int r = -1; 241 242 if (lock_db(path) == 0) { 243 if (database_present(path) == NO_DICTDATABASE) 244 r = build_dict_database(list, path); 245 else 246 r = update_dict_database(list, path); 247 unlock_db(); 248 } 249 return (r); 250 } 251