/* * This is a hack, but until libc and glibc both include this function * by default (libc only includes it if nys is not being used, at the * moment, and glibc doesn't appear to have it at all) we need to have * it here, too. :-( * * This should not become an official part of PAM. * * BEGIN_HACK */ /* * 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 #ifdef WITH_SELINUX #include #endif #define LOCKFILE "/etc/.pwd.lock" #define TIMEOUT 15 static int lockfd = -1; 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 */ } static int lckpwdf(void) { struct sigaction act, oldact; sigset_t set, oldset; if (lockfd != -1) return -1; #ifdef WITH_SELINUX if(is_selinux_enabled()>0) { lockfd = open(LOCKFILE, O_WRONLY); if(lockfd == -1 && errno == ENOENT) { security_context_t create_context; int rc; if(getfilecon("/etc/passwd", &create_context)) return -1; rc = setfscreatecon(create_context); freecon(create_context); if(rc) return -1; lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600); if(setfscreatecon(NULL)) return -1; } } else #endif 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; } static int ulckpwdf(void) { unlink(LOCKFILE); if (lockfd == -1) return -1; if (close(lockfd) == -1) { lockfd = -1; return -1; } lockfd = -1; return 0; } /* END_HACK */