]> granicus.if.org Git - linux-pam/blob - modules/pam_unix/lckpwdf.-c
Relevant BUGIDs:
[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 #ifdef WITH_SELINUX
30 #include <selinux/selinux.h>
31 #endif
32
33 #define LOCKFILE "/etc/.pwd.lock"
34 #define TIMEOUT 15
35
36 static int lockfd = -1;
37
38 static int set_close_on_exec(int fd)
39 {
40         int flags = fcntl(fd, F_GETFD, 0);
41         if (flags == -1)
42                 return -1;
43         flags |= FD_CLOEXEC;
44         return fcntl(fd, F_SETFD, flags);
45 }
46
47 static int do_lock(int fd)
48 {
49         struct flock fl;
50
51         memset(&fl, 0, sizeof fl);
52         fl.l_type = F_WRLCK;
53         fl.l_whence = SEEK_SET;
54         return fcntl(fd, F_SETLKW, &fl);
55 }
56
57 static void alarm_catch(int sig)
58 {
59 /* does nothing, but fcntl F_SETLKW will fail with EINTR */
60 }
61
62 static int lckpwdf(void)
63 {
64         struct sigaction act, oldact;
65         sigset_t set, oldset;
66
67         if (lockfd != -1)
68                 return -1;
69
70 #ifdef WITH_SELINUX
71         if(is_selinux_enabled()>0)
72         {
73                 lockfd = open(LOCKFILE, O_WRONLY);
74                 if(lockfd == -1 && errno == ENOENT)
75                 {
76                         security_context_t create_context;
77                         int rc;
78
79                         if(getfilecon("/etc/passwd", &create_context))
80                                 return -1;
81                         rc = setfscreatecon(create_context);
82                         freecon(create_context);
83                         if(rc)
84                                 return -1;
85                         lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
86                         if(setfscreatecon(NULL))
87                                 return -1;
88                 }
89         }
90         else
91 #endif
92         lockfd = open(LOCKFILE, O_CREAT | O_WRONLY, 0600);
93         if (lockfd == -1)
94                 return -1;
95         if (set_close_on_exec(lockfd) == -1)
96                 goto cleanup_fd;
97
98         memset(&act, 0, sizeof act);
99         act.sa_handler = alarm_catch;
100         act.sa_flags = 0;
101         sigfillset(&act.sa_mask);
102         if (sigaction(SIGALRM, &act, &oldact) == -1)
103                 goto cleanup_fd;
104
105         sigemptyset(&set);
106         sigaddset(&set, SIGALRM);
107         if (sigprocmask(SIG_UNBLOCK, &set, &oldset) == -1)
108                 goto cleanup_sig;
109
110         alarm(TIMEOUT);
111         if (do_lock(lockfd) == -1)
112                 goto cleanup_alarm;
113         alarm(0);
114         sigprocmask(SIG_SETMASK, &oldset, NULL);
115         sigaction(SIGALRM, &oldact, NULL);
116         return 0;
117
118       cleanup_alarm:
119         alarm(0);
120         sigprocmask(SIG_SETMASK, &oldset, NULL);
121       cleanup_sig:
122         sigaction(SIGALRM, &oldact, NULL);
123       cleanup_fd:
124         close(lockfd);
125         lockfd = -1;
126         return -1;
127 }
128
129 static int ulckpwdf(void)
130 {
131         unlink(LOCKFILE);
132         if (lockfd == -1)
133                 return -1;
134
135         if (close(lockfd) == -1) {
136                 lockfd = -1;
137                 return -1;
138         }
139         lockfd = -1;
140         return 0;
141 }
142 /* END_HACK */