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