Initial commit for Tizen
[profile/extras/shadow-utils.git] / libmisc / failure.c
1 /*
2  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 1998, Marek Michałkiewicz
4  * Copyright (c) 2002 - 2005, Tomasz Kłoczko
5  * Copyright (c) 2008       , Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id: failure.c 2829 2009-04-28 19:14:50Z nekral-guest $"
36
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <unistd.h>
40 #include "defines.h"
41 #include "faillog.h"
42 #include "getdef.h"
43 #include "failure.h"
44 #define YEAR    (365L*DAY)
45 /*
46  * failure - make failure entry
47  *
48  *      failure() creates a new (struct faillog) entry or updates an
49  *      existing one with the current failed login information.
50  */
51 void failure (uid_t uid, const char *tty, struct faillog *fl)
52 {
53         int fd;
54         off_t offset_uid = (off_t) (sizeof *fl) * uid;
55
56         /*
57          * Don't do anything if failure logging isn't set up.
58          */
59
60         if (access (FAILLOG_FILE, F_OK) != 0) {
61                 return;
62         }
63
64         fd = open (FAILLOG_FILE, O_RDWR);
65         if (fd < 0) {
66                 SYSLOG ((LOG_WARN,
67                          "Can't write faillog entry for UID %lu in %s.",
68                          (unsigned long) uid, FAILLOG_FILE));
69                 return;
70         }
71
72         /*
73          * The file is indexed by UID value meaning that shared UID's
74          * share failure log records.  That's OK since they really
75          * share just about everything else ...
76          */
77
78         if (   (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
79             || (read (fd, (char *) fl, sizeof *fl) != (ssize_t) sizeof *fl)) {
80                 /* This is not necessarily a failure. The file is
81                  * initially zero length.
82                  *
83                  * If lseek() or read() failed for any other reason, this
84                  * might reset the counter. But the new failure will be
85                  * logged.
86                  */
87                 memzero (fl, sizeof *fl);
88         }
89
90         /*
91          * Update the record.  We increment the failure count to log the
92          * latest failure.  The only concern here is overflow, and we'll
93          * check for that.  The line name and time of day are both
94          * updated as well.
95          */
96
97         if (fl->fail_cnt + 1 > 0) {
98                 fl->fail_cnt++;
99         }
100
101         strncpy (fl->fail_line, tty, sizeof fl->fail_line);
102         (void) time (&fl->fail_time);
103
104         /*
105          * Seek back to the correct position in the file and write the
106          * record out.  Ideally we should lock the file in case the same
107          * account is being logged simultaneously.  But the risk doesn't
108          * seem that great.
109          */
110
111         if (   (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
112             || (write (fd, (char *) fl, sizeof *fl) != (ssize_t) sizeof *fl)
113             || (close (fd) != 0)) {
114                 SYSLOG ((LOG_WARN,
115                          "Can't write faillog entry for UID %lu in %s.",
116                          (unsigned long) uid, FAILLOG_FILE));
117                 (void) close (fd);
118         }
119 }
120
121 static bool too_many_failures (const struct faillog *fl)
122 {
123         time_t now;
124
125         if ((0 == fl->fail_max) || (fl->fail_cnt < fl->fail_max)) {
126                 return false;
127         }
128
129         if (0 == fl->fail_locktime) {
130                 return true;    /* locked until reset manually */
131         }
132
133         (void) time (&now);
134         if ((fl->fail_time + fl->fail_locktime) < now) {
135                 return false;   /* enough time since last failure */
136         }
137
138         return true;
139 }
140
141 /*
142  * failcheck - check for failures > allowable
143  *
144  *      failcheck() is called AFTER the password has been validated.  If the
145  *      account has been "attacked" with too many login failures, failcheck()
146  *      returns 0 to indicate that the login should be denied even though
147  *      the password is valid.
148  *
149  *      failed indicates if the login failed AFTER the password has been
150  *             validated.
151  */
152
153 int failcheck (uid_t uid, struct faillog *fl, bool failed)
154 {
155         int fd;
156         struct faillog fail;
157         off_t offset_uid = (off_t) (sizeof *fl) * uid;
158
159         /*
160          * Suppress the check if the log file isn't there.
161          */
162
163         if (access (FAILLOG_FILE, F_OK) != 0) {
164                 return 1;
165         }
166
167         fd = open (FAILLOG_FILE, failed?O_RDONLY:O_RDWR);
168         if (fd < 0) {
169                 SYSLOG ((LOG_WARN,
170                          "Can't open the faillog file (%s) to check UID %lu. "
171                          "User access authorized.",
172                          FAILLOG_FILE, (unsigned long) uid));
173                 return 1;
174         }
175
176         /*
177          * Get the record from the file and determine if the user has
178          * exceeded the failure limit.  If "max" is zero, any number
179          * of failures are permitted.  Only when "max" is non-zero and
180          * "cnt" is greater than or equal to "max" is the account
181          * considered to be locked.
182          *
183          * If read fails, there is no record for this user yet (the
184          * file is initially zero length and extended by writes), so
185          * no need to reset the count.
186          */
187
188         if (   (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
189             || (read (fd, (char *) fl, sizeof *fl) != (ssize_t) sizeof *fl)) {
190                 (void) close (fd);
191                 return 1;
192         }
193
194         if (too_many_failures (fl)) {
195                 (void) close (fd);
196                 return 0;
197         }
198
199         /*
200          * The record is updated if this is not a failure.  The count will
201          * be reset to zero, but the rest of the information will be left
202          * in the record in case someone wants to see where the failed
203          * login originated.
204          */
205
206         if (!failed) {
207                 fail = *fl;
208                 fail.fail_cnt = 0;
209
210                 if (   (lseek (fd, offset_uid, SEEK_SET) != offset_uid)
211                     || (write (fd, (const void *) &fail, sizeof fail) != (ssize_t) sizeof fail)
212                     || (close (fd) != 0)) {
213                         SYSLOG ((LOG_WARN,
214                                  "Can't reset faillog entry for UID %lu in %s.",
215                                  (unsigned long) uid, FAILLOG_FILE));
216                         (void) close (fd);
217                 }
218         } else {
219                 (void) close (fd);
220         }
221
222         return 1;
223 }
224
225 /*
226  * failprint - print line of failure information
227  *
228  *      failprint takes a (struct faillog) entry and formats it into a
229  *      message which is displayed at login time.
230  */
231
232 void failprint (const struct faillog *fail)
233 {
234         struct tm *tp;
235
236 #if HAVE_STRFTIME
237         char lasttimeb[256];
238         char *lasttime = lasttimeb;
239 #else
240         char *lasttime;
241 #endif
242         time_t NOW;
243
244         if (0 == fail->fail_cnt) {
245                 return;
246         }
247
248         tp = localtime (&(fail->fail_time));
249         (void) time (&NOW);
250
251 #if HAVE_STRFTIME
252         /*
253          * Print all information we have.
254          */
255         (void) strftime (lasttimeb, sizeof lasttimeb, "%c", tp);
256 #else
257
258         /*
259          * Do the same thing, but don't use strftime since it
260          * probably doesn't exist on this system
261          */
262         lasttime = asctime (tp);
263         lasttime[24] = '\0';
264
265         if ((NOW - fail->fail_time) < YEAR) {
266                 lasttime[19] = '\0';
267         }
268         if ((NOW - fail->fail_time) < DAY) {
269                 lasttime = lasttime + 11;
270         }
271
272         if (' ' == *lasttime) {
273                 lasttime++;
274         }
275 #endif
276         (void) printf (ngettext ("%d failure since last login.\n"
277                                  "Last was %s on %s.\n",
278                                  "%d failures since last login.\n"
279                                  "Last was %s on %s.\n",
280                                  (unsigned long) fail->fail_cnt),
281                        fail->fail_cnt, lasttime, fail->fail_line);
282 }
283
284 /*
285  * failtmp - update the cumulative failure log
286  *
287  *      failtmp updates the (struct utmp) formatted failure log which
288  *      maintains a record of all login failures.
289  */
290
291 void failtmp (const char *username,
292 #ifdef USE_UTMPX
293                      const struct utmpx *failent
294 #else                           /* !USE_UTMPX */
295                      const struct utmp *failent
296 #endif                          /* !USE_UTMPX */
297     )
298 {
299         char *ftmp;
300         int fd;
301
302         /*
303          * Get the name of the failure file.  If no file has been defined
304          * in login.defs, don't do this.
305          */
306
307         ftmp = getdef_str ("FTMP_FILE");
308         if (NULL == ftmp) {
309                 return;
310         }
311
312         /*
313          * Open the file for append.  It must already exist for this
314          * feature to be used.
315          */
316
317         if (access (ftmp, F_OK) != 0) {
318                 return;
319         }
320
321         fd = open (ftmp, O_WRONLY | O_APPEND);
322         if (-1 == fd) {
323                 SYSLOG ((LOG_WARN,
324                          "Can't append failure of user %s to %s.",
325                          username, ftmp));
326                 return;
327         }
328
329         /*
330          * Append the new failure record and close the log file.
331          */
332
333         if (   (write (fd, (const void *) failent, sizeof *failent) != (ssize_t) sizeof *failent)
334             || (close (fd) != 0)) {
335                 SYSLOG ((LOG_WARN,
336                          "Can't append failure of user %s to %s.",
337                          username, ftmp));
338                 (void) close (fd);
339         }
340 }
341