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