1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
Index: kcheckpass.c
===================================================================
--- kcheckpass/kcheckpass.c (revision 453871)
+++ kcheckpass/kcheckpass.c (working copy)
@@ -14,7 +14,7 @@
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
* kcheckpass is a simple password checker. Just invoke and
@@ -264,8 +264,13 @@
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
+ va_end(ap);
}
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
static void ATTR_NORETURN
usage(int exitval)
{
@@ -286,6 +291,14 @@
exit(exitval);
}
+static int exclusive_lock(int fd)
+{
+ struct flock lk;
+ lk.l_type = F_WRLCK;
+ lk.l_whence = SEEK_SET;
+ lk.l_start = lk.l_len = 0;
+ return fcntl(fd, F_SETLKW, &lk);
+}
int
main(int argc, char **argv)
@@ -299,10 +312,13 @@
char *p;
#endif
struct passwd *pw;
- int c, nfd, lfd, numtries;
+ int c, nfd, tfd, lfd;
uid_t uid;
- long lasttime;
+ time_t lasttime;
AuthReturn ret;
+ char tmpname[64], fname[64], fcont[64];
+ time_t left = 3;
+ lfd = tfd = 0;
#ifdef HAVE_OSF_C2_PASSWD
initialize_osf_security(argc, argv);
@@ -371,6 +387,41 @@
return AuthError;
}
}
+
+ /* see if we had already a failed attempt */
+ if ( uid != geteuid() ) {
+ strcpy(tmpname, "/var/spool/lock/kcheckpass.tmp.XXXXXX");
+ if ((tfd=mkstemp(tmpname)) < 0)
+ return AuthError;
+
+ /* try locking out concurrent kcheckpass processes */
+ exclusive_lock(tfd);
+
+ write(tfd, fcont, sprintf(fcont, "%lu\n", time(0)+left));
+ (void) lseek(tfd, 0, SEEK_SET);
+
+ sprintf(fname, "/var/spool/lock/kcheckpass.%d", uid );
+
+ if ((lfd = open(fname, O_RDWR | O_NOFOLLOW)) >= 0) {
+ if (exclusive_lock(lfd) == 0) {
+ if ((c = read(lfd, fcont, sizeof(fcont)-1)) > 0 &&
+ (fcont[c] = '\0', sscanf(fcont, "%ld", &lasttime) == 1))
+ {
+ time_t ct = time(0);
+
+ /* in case we were killed early, sleep the remaining time
+ * to properly enforce invocation throttling and make sure
+ * that users can't use kcheckpass for bruteforcing password
+ */
+ if(lasttime > ct && lasttime < ct + left)
+ sleep (lasttime - ct);
+ }
+ }
+ close(lfd);
+ }
+ rename(tmpname, fname);
+ }
+
/* Now do the fandango */
ret = Authenticate(
#ifdef HAVE_PAM
@@ -379,35 +430,21 @@
method,
username,
sfd < 0 ? conv_legacy : conv_server);
+
if (ret == AuthOk || ret == AuthBad) {
/* Security: Don't undermine the shadow system. */
if (uid != geteuid()) {
- char fname[32], fcont[32];
- sprintf(fname, "/var/lock/kcheckpass.%d", uid);
- if ((lfd = open(fname, O_RDWR | O_CREAT)) >= 0) {
- struct flock lk;
- lk.l_type = F_WRLCK;
- lk.l_whence = SEEK_SET;
- lk.l_start = lk.l_len = 0;
- if (fcntl(lfd, F_SETLKW, &lk))
- return AuthError;
- if ((c = read(lfd, fcont, sizeof(fcont))) > 0 &&
- (fcont[c] = 0, sscanf(fcont, "%ld %d\n", &lasttime, &numtries) == 2))
- {
- time_t left = lasttime - time(0);
- if (numtries < 20)
- numtries++;
- left += 2 << (numtries > 10 ? numtries - 10 : 0);
- if (left > 0)
- sleep(left);
- } else
- numtries = 0;
- if (ret == AuthBad) {
- lseek(lfd, 0, SEEK_SET);
- write(lfd, fcont, sprintf(fcont, "%ld %d\n", time(0), numtries));
- } else
- unlink(fname);
- }
+ if (ret == AuthBad) {
+ write(tfd, fcont, sprintf(fcont, "%lu\n", time(0)+left));
+ } else
+ unlink(fname);
+
+ unlink(tmpname);
+
+ if (ret == AuthBad)
+ sleep(left);
+
+ close(tfd);
}
if (ret == AuthBad) {
message("Authentication failure\n");
@@ -417,6 +454,7 @@
}
}
}
+
return ret;
}
|