]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/lckpwdf.-c
Relevant BUGIDs: 440107
[linux-pam] / modules / pam_unix / lckpwdf.-c
1 /*
2  * This is a hack, but until libc and glibc both include this function
3  * by default (libc only includes it if nys is not being used, at the
4  * moment, and glibc doesn't appear to have it at all) we need to have
5  * it here, too.  :-(
6  *
7  * This should not become an official part of PAM.
8  *
9  * BEGIN_HACK
10  */
11
12 /*
13  * lckpwdf.c -- prevent simultaneous updates of password files
14  *
15  * Before modifying any of the password files, call lckpwdf().  It may block
16  * for up to 15 seconds trying to get the lock.  Return value is 0 on success
17  * or -1 on failure.  When you are done, call ulckpwdf() to release the lock.
18  * The lock is also released automatically when the process exits.  Only one
19  * process at a time may hold the lock.
20  *
21  * These functions are supposed to be conformant with AT&T SVID Issue 3.
22  *
23  * Written by Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl>,
24  * public domain.
25  */
26
27 #include <fcntl.h>
28 #include <signal.h>
29
30 #define LOCKFILE "/etc/.pwd.lock"
31 #define TIMEOUT 15
32
33 static int lockfd = -1;
34
35 static int set_close_on_exec(int fd)
36 {
37         int flags = fcntl(fd, F_GETFD, 0);
38         if (flags == -1)
39                 return -1;
40         flags |= FD_CLOEXEC;
41         return fcntl(fd, F_SETFD, flags);
42 }
43
44 static int do_lock(int fd)
45 {
46         struct flock fl;
47
48         memset(&fl, 0, sizeof fl);
49         fl.l_type = F_WRLCK;
50         fl.l_whence = SEEK_SET;
51         return fcntl(fd, F_SETLKW, &fl);
52 }
53
54 static void alarm_catch(int sig)
55 {
56 /* does nothing, but fcntl F_SETLKW will fail with EINTR */
57 }
58
59 static int lckpwdf(void)
60 {
61         struct sigaction act, oldact;
62         sigset_t set, oldset;
63
64         if (lockfd != -1)
65                 return -1;
66
67         lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
68         if (lockfd == -1)
69                 return -1;
70         if (set_close_on_exec(lockfd) == -1)
71                 goto cleanup_fd;
72
73         memset(&act, 0, sizeof act);
74         act.sa_handler = alarm_catch;
75         act.sa_flags = 0;
76         sigfillset(&act.sa_mask);
77         if (sigaction(SIGALRM, &act, &oldact) == -1)
78                 goto cleanup_fd;
79
80         sigemptyset(&set);
81         sigaddset(&set, SIGALRM);
82         if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1)
83                 goto cleanup_sig;
84
85         alarm(TIMEOUT);
86         if (do_lock(lockfd) == -1)
87                 goto cleanup_alarm;
88         alarm(0);
89         sigprocmask(SIG_SETMASK, &oldset, NULL);
90         sigaction(SIGALRM, &oldact, NULL);
91         return 0;
92
93       cleanup_alarm:
94         alarm(0);
95         sigprocmask(SIG_SETMASK, &oldset, NULL);
96       cleanup_sig:
97         sigaction(SIGALRM, &oldact, NULL);
98       cleanup_fd:
99         close(lockfd);
100         lockfd = -1;
101         return -1;
102 }
103
104 static int ulckpwdf(void)
105 {
106         unlink(LOCKFILE);
107         if (lockfd == -1)
108                 return -1;
109
110         if (close(lockfd) == -1) {
111                 lockfd = -1;
112                 return -1;
113         }
114         lockfd = -1;
115         return 0;
116 }
117 /* END_HACK */