17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 54a7ceb24Sjjj * Common Development and Distribution License (the "License"). 64a7ceb24Sjjj * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*c7402f07SJoep Vesseur * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <sys/stat.h> 277c478bd9Sstevel@tonic-gate #include <stdio.h> 287c478bd9Sstevel@tonic-gate #include <syslog.h> 297c478bd9Sstevel@tonic-gate #include <fcntl.h> 307c478bd9Sstevel@tonic-gate #include <time.h> 317c478bd9Sstevel@tonic-gate #include <errno.h> 327c478bd9Sstevel@tonic-gate #include <signal.h> 337c478bd9Sstevel@tonic-gate #include "packer.h" 347c478bd9Sstevel@tonic-gate 357c478bd9Sstevel@tonic-gate static int lockfd = -1; 367c478bd9Sstevel@tonic-gate static struct flock flock = { 0, 0, 0, 0, 0, 0 }; 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate char dblock[PATH_MAX]; 397c478bd9Sstevel@tonic-gate 404a7ceb24Sjjj #define LOCK_WAIT 1000000 414a7ceb24Sjjj #define LOCK_RETRIES 60 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate /* 447c478bd9Sstevel@tonic-gate * lock_db() 457c478bd9Sstevel@tonic-gate * 467c478bd9Sstevel@tonic-gate * Create a lockfile to prevent simultaneous access to the database 477c478bd9Sstevel@tonic-gate * creation routines. We set a timeout to LOCK_WAIT seconds. If we 484a7ceb24Sjjj * haven't obtained a lock after LOCK_RETIRES attempts, we bail out. 497c478bd9Sstevel@tonic-gate * 507c478bd9Sstevel@tonic-gate * returns 0 on succes, -1 on (lock) failure. 517c478bd9Sstevel@tonic-gate * side effect: the directory "path" will be created if it didn't exist. 527c478bd9Sstevel@tonic-gate */ 537c478bd9Sstevel@tonic-gate int 547c478bd9Sstevel@tonic-gate lock_db(char *path) 557c478bd9Sstevel@tonic-gate { 567c478bd9Sstevel@tonic-gate int retval; 577c478bd9Sstevel@tonic-gate struct stat st; 584a7ceb24Sjjj int retries = 0; 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate /* create directory "path" if it doesn't exist */ 617c478bd9Sstevel@tonic-gate if (stat(path, &st) == -1) { 627c478bd9Sstevel@tonic-gate if (errno != ENOENT || 637c478bd9Sstevel@tonic-gate (mkdir(path, 0755) == -1 || chmod(path, 0755) == -1)) 647c478bd9Sstevel@tonic-gate return (-1); 657c478bd9Sstevel@tonic-gate } 667c478bd9Sstevel@tonic-gate 677c478bd9Sstevel@tonic-gate (void) snprintf(dblock, sizeof (dblock), "%s/authtok_check.lock", path); 687c478bd9Sstevel@tonic-gate 697c478bd9Sstevel@tonic-gate if ((lockfd = open(dblock, O_WRONLY|O_CREAT|O_EXCL, 0400)) == -1) { 707c478bd9Sstevel@tonic-gate if (errno == EEXIST) 717c478bd9Sstevel@tonic-gate lockfd = open(dblock, O_WRONLY); 727c478bd9Sstevel@tonic-gate if (lockfd == -1) { 737c478bd9Sstevel@tonic-gate int olderrno = errno; 747c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "pam_authtok_check::pam_sm_chauthtok: " 757c478bd9Sstevel@tonic-gate "can't open lockfile: %s", strerror(errno)); 767c478bd9Sstevel@tonic-gate errno = olderrno; 777c478bd9Sstevel@tonic-gate return (-1); 787c478bd9Sstevel@tonic-gate } 797c478bd9Sstevel@tonic-gate } 807c478bd9Sstevel@tonic-gate 814a7ceb24Sjjj do { 827c478bd9Sstevel@tonic-gate flock.l_type = F_WRLCK; 834a7ceb24Sjjj retval = fcntl(lockfd, F_SETLK, &flock); 844a7ceb24Sjjj if (retval == -1) 854a7ceb24Sjjj (void) usleep(LOCK_WAIT); 864a7ceb24Sjjj } while (retval == -1 && ++retries < LOCK_RETRIES); 877c478bd9Sstevel@tonic-gate 884a7ceb24Sjjj if (retval == -1) { 894a7ceb24Sjjj int errno_saved = errno; 907c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "pam_authtok_check::pam_sm_chauthtok: timeout " 917c478bd9Sstevel@tonic-gate "waiting for dictionary lock."); 924a7ceb24Sjjj errno = errno_saved; 937c478bd9Sstevel@tonic-gate } 947c478bd9Sstevel@tonic-gate 957c478bd9Sstevel@tonic-gate return (retval); 967c478bd9Sstevel@tonic-gate } 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate /* 997c478bd9Sstevel@tonic-gate * unlock_db() 1007c478bd9Sstevel@tonic-gate * 1017c478bd9Sstevel@tonic-gate * Release the database lock 1027c478bd9Sstevel@tonic-gate */ 1037c478bd9Sstevel@tonic-gate void 1047c478bd9Sstevel@tonic-gate unlock_db(void) 1057c478bd9Sstevel@tonic-gate { 1067c478bd9Sstevel@tonic-gate if (lockfd != -1) { 1077c478bd9Sstevel@tonic-gate flock.l_type = F_UNLCK; 1087c478bd9Sstevel@tonic-gate (void) fcntl(lockfd, F_SETLK, &flock); 1097c478bd9Sstevel@tonic-gate (void) close(lockfd); 1107c478bd9Sstevel@tonic-gate lockfd = -1; 1117c478bd9Sstevel@tonic-gate } 1127c478bd9Sstevel@tonic-gate } 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate /* 1157c478bd9Sstevel@tonic-gate * database_present() 1167c478bd9Sstevel@tonic-gate * 1177c478bd9Sstevel@tonic-gate * returns 0 if the database files are found, and the database size is 118*c7402f07SJoep Vesseur * greater than 0 and the database version matches the current version. 1197c478bd9Sstevel@tonic-gate */ 1207c478bd9Sstevel@tonic-gate int 1217c478bd9Sstevel@tonic-gate database_present(char *path) 1227c478bd9Sstevel@tonic-gate { 1237c478bd9Sstevel@tonic-gate struct stat st; 1247c478bd9Sstevel@tonic-gate char dict_hwm[PATH_MAX]; 1257c478bd9Sstevel@tonic-gate char dict_pwd[PATH_MAX]; 1267c478bd9Sstevel@tonic-gate char dict_pwi[PATH_MAX]; 127*c7402f07SJoep Vesseur PWDICT *dict; 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate (void) snprintf(dict_hwm, sizeof (dict_hwm), "%s/%s", path, 1307c478bd9Sstevel@tonic-gate DICT_DATABASE_HWM); 1317c478bd9Sstevel@tonic-gate (void) snprintf(dict_pwd, sizeof (dict_pwd), "%s/%s", path, 1327c478bd9Sstevel@tonic-gate DICT_DATABASE_PWD); 1337c478bd9Sstevel@tonic-gate (void) snprintf(dict_pwi, sizeof (dict_pwi), "%s/%s", path, 1347c478bd9Sstevel@tonic-gate DICT_DATABASE_PWI); 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate if (stat(dict_hwm, &st) == -1 || 1377c478bd9Sstevel@tonic-gate (stat(dict_pwd, &st) == -1 || st.st_size == 0) || 1387c478bd9Sstevel@tonic-gate stat(dict_pwi, &st) == -1) 1397c478bd9Sstevel@tonic-gate return (NO_DICTDATABASE); 1407c478bd9Sstevel@tonic-gate 141*c7402f07SJoep Vesseur /* verify database version number by trying to open it */ 142*c7402f07SJoep Vesseur if ((dict = PWOpen(path, "r")) == NULL) { 143*c7402f07SJoep Vesseur /* the files are there, but an outdated version */ 144*c7402f07SJoep Vesseur PWRemove(path); 145*c7402f07SJoep Vesseur return (NO_DICTDATABASE); 146*c7402f07SJoep Vesseur } 147*c7402f07SJoep Vesseur (void) PWClose(dict); 1487c478bd9Sstevel@tonic-gate return (0); 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate /* 1527c478bd9Sstevel@tonic-gate * build_dict_database(list, char *path) 1537c478bd9Sstevel@tonic-gate * 1547c478bd9Sstevel@tonic-gate * Create the Crack Dictionary Database based on the list of sources 1557c478bd9Sstevel@tonic-gate * dictionaries specified in "list". Store the database in "path". 1567c478bd9Sstevel@tonic-gate */ 1577c478bd9Sstevel@tonic-gate int 1587c478bd9Sstevel@tonic-gate build_dict_database(char *list, char *path) 1597c478bd9Sstevel@tonic-gate { 1607c478bd9Sstevel@tonic-gate return (packer(list, path) == -1 ? DICTDATABASE_BUILD_ERR : 0); 1617c478bd9Sstevel@tonic-gate } 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate /* 1647c478bd9Sstevel@tonic-gate * Rebuild the database in "path" if the database is older than one of the 1657c478bd9Sstevel@tonic-gate * files listed in "list", or older than the config-file PWADMIN. 1667c478bd9Sstevel@tonic-gate */ 1677c478bd9Sstevel@tonic-gate int 1687c478bd9Sstevel@tonic-gate update_dict_database(char *list, char *path) 1697c478bd9Sstevel@tonic-gate { 1707c478bd9Sstevel@tonic-gate struct stat st_db; 1717c478bd9Sstevel@tonic-gate struct stat st_file; 1727c478bd9Sstevel@tonic-gate char *buf; 1737c478bd9Sstevel@tonic-gate char *listcopy; 1747c478bd9Sstevel@tonic-gate boolean_t update_needed = B_FALSE; 1757c478bd9Sstevel@tonic-gate char dbase_pwd[PATH_MAX]; 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate (void) snprintf(dbase_pwd, sizeof (dbase_pwd), "%s/%s", path, 1787c478bd9Sstevel@tonic-gate DICT_DATABASE_PWD); 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate if (stat(dbase_pwd, &st_db) == -1) 1817c478bd9Sstevel@tonic-gate return (DICTFILE_ERR); 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate if ((listcopy = strdup(list)) == NULL) 1847c478bd9Sstevel@tonic-gate return (DICTFILE_ERR); 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate buf = strtok(listcopy, "\t ,"); 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate /* Compare mtime of each listed dictionary against DB mtime */ 1897c478bd9Sstevel@tonic-gate while (update_needed == B_FALSE && buf != NULL) { 1907c478bd9Sstevel@tonic-gate if (stat(buf, &st_file) == -1) { 1917c478bd9Sstevel@tonic-gate if (errno == ENOENT) { 1927c478bd9Sstevel@tonic-gate syslog(LOG_ERR, 1937c478bd9Sstevel@tonic-gate "pam_authtok_check:update_dict_database: " 1947c478bd9Sstevel@tonic-gate "dictionary \"%s\" not present.", buf); 1957c478bd9Sstevel@tonic-gate } else { 1967c478bd9Sstevel@tonic-gate syslog(LOG_ERR, 1977c478bd9Sstevel@tonic-gate "pam_authtok_check:update_dict_database: " 1987c478bd9Sstevel@tonic-gate "stat(%s) failed: %s", buf, 1997c478bd9Sstevel@tonic-gate strerror(errno)); 2007c478bd9Sstevel@tonic-gate } 2017c478bd9Sstevel@tonic-gate free(listcopy); 2027c478bd9Sstevel@tonic-gate return (DICTFILE_ERR); 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate if (st_db.st_mtime < st_file.st_mtime) 2057c478bd9Sstevel@tonic-gate update_needed = B_TRUE; /* database out of date */ 2067c478bd9Sstevel@tonic-gate buf = strtok(NULL, "\t ,"); 2077c478bd9Sstevel@tonic-gate } 2087c478bd9Sstevel@tonic-gate 2097c478bd9Sstevel@tonic-gate free(listcopy); 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate /* 2127c478bd9Sstevel@tonic-gate * If /etc/default/passwd is updated, generate the database. 2137c478bd9Sstevel@tonic-gate * Because this is the only way we can check if files have been 2147c478bd9Sstevel@tonic-gate * added/deleted from DICTIONLIST. 2157c478bd9Sstevel@tonic-gate * Drawback: the database will also be generated when other 2167c478bd9Sstevel@tonic-gate * tunables are changed. 2177c478bd9Sstevel@tonic-gate */ 2187c478bd9Sstevel@tonic-gate if (stat(PWADMIN, &st_file) != -1 && st_db.st_mtime < st_file.st_mtime) 2197c478bd9Sstevel@tonic-gate update_needed = B_TRUE; 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate if (update_needed) { 2227c478bd9Sstevel@tonic-gate /* 2237c478bd9Sstevel@tonic-gate * Since we actually rebuild the database, we need to remove 2247c478bd9Sstevel@tonic-gate * the old database first. 2257c478bd9Sstevel@tonic-gate */ 2267c478bd9Sstevel@tonic-gate PWRemove(path); 2277c478bd9Sstevel@tonic-gate return (build_dict_database(list, path)); 2287c478bd9Sstevel@tonic-gate } 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate return (0); 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate /* 2347c478bd9Sstevel@tonic-gate * Build or update database, while holding the global lock. 2357c478bd9Sstevel@tonic-gate */ 2367c478bd9Sstevel@tonic-gate int 2377c478bd9Sstevel@tonic-gate make_dict_database(char *list, char *path) 2387c478bd9Sstevel@tonic-gate { 2397c478bd9Sstevel@tonic-gate int r = -1; 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate if (lock_db(path) == 0) { 2427c478bd9Sstevel@tonic-gate if (database_present(path) == NO_DICTDATABASE) 2437c478bd9Sstevel@tonic-gate r = build_dict_database(list, path); 2447c478bd9Sstevel@tonic-gate else 2457c478bd9Sstevel@tonic-gate r = update_dict_database(list, path); 2467c478bd9Sstevel@tonic-gate unlock_db(); 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate return (r); 2497c478bd9Sstevel@tonic-gate } 250