/* FILE $Id: lckpwdf.c 6 2005-11-24 00:02:59Z ggw $ PURPOSE In general we want to insure that any operations on /etc/passwd and/or /etc/shadow done by mysqlISP applications do not interfere with other OS applications like adduser etc. Some non linux BSD OSs don't come with lckpwdf and ulckpwdf functions that do provide this. From linux man pages... "The lckpwdf and ulckpwdf routines should be used to insure exclusive access to the /etc/shadow file. lckpwdf attempts to acquire a lock using pw_lock for up to 15 seconds. It continues by attempting to acquire a second lock using spw_lock for the remainder of the initial 15 seconds. Should either attempt fail after a total of 15 seconds, lckpwdf returns -1. When both locks are acquired 0 is returned." Solution: We include this file with openisp.net mysqlISP family of applications for FreeBSD and other BSD unix variant support. Only used if makefile has been modified accordingly usually just by defining -DFreeBSD or similar compile time make define. OTHER Other solutions have been reported and may be better or more compatible with general purpose as stated above: FreeBSD: shell> install /usr/ports/security/cryptlib WARNING Only tested on a few FreeBSD servers and kernels. More testing is needed. Please test and report to us: support @ openisp . net */ /* * lckpwdf.c -- prevent simultaneous updates of password files * * Before modifying any of the password files, call lckpwdf(). It may block * for up to 15 seconds trying to get the lock. Return value is 0 on success * or -1 on failure. When you are done, call ulckpwdf() to release the lock. * The lock is also released automatically when the process exits. Only one * process at a time may hold the lock. * * These functions are supposed to be conformant with AT&T SVID Issue 3. * * Written by Marek Michalkiewicz , * public domain. */ #include #include #include #include #define LOCKFILE "/etc/.pwd.lock" #define TIMEOUT 15 static int lockfd = -1; int lckpwdf(void); int ulckpwdf(void); static int set_close_on_exec(int fd) { int flags = fcntl(fd, F_GETFD, 0); if (flags == -1) return -1; flags |= FD_CLOEXEC; return fcntl(fd, F_SETFD, flags); } static int do_lock(int fd) { struct flock fl; memset(&fl, 0, sizeof fl); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; return fcntl(fd, F_SETLKW, &fl); } static void alarm_catch(int sig) { /* does nothing, but fcntl F_SETLKW will fail with EINTR */ } int lckpwdf(void) { struct sigaction act, oldact; sigset_t set, oldset; if (lockfd != -1) return -1; lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600); if (lockfd == -1) return -1; if (set_close_on_exec(lockfd) == -1) goto cleanup_fd; memset(&act, 0, sizeof act); act.sa_handler = alarm_catch; act.sa_flags = 0; sigfillset(&act.sa_mask); if (sigaction(SIGALRM, &act, &oldact) == -1) goto cleanup_fd; sigemptyset(&set); sigaddset(&set, SIGALRM); if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1) goto cleanup_sig; alarm(TIMEOUT); if (do_lock(lockfd) == -1) goto cleanup_alarm; alarm(0); sigprocmask(SIG_SETMASK, &oldset, NULL); sigaction(SIGALRM, &oldact, NULL); return 0; cleanup_alarm: alarm(0); sigprocmask(SIG_SETMASK, &oldset, NULL); cleanup_sig: sigaction(SIGALRM, &oldact, NULL); cleanup_fd: close(lockfd); lockfd = -1; return -1; } int ulckpwdf(void) { if (lockfd == -1) return -1; if (close(lockfd) == -1) { lockfd = -1; return -1; } lockfd = -1; return 0; }