xref: /openbsd-src/usr.bin/login/failedlogin.c (revision b7041c0781c8668129da8084451ded41b0c43954)
1 /*	$OpenBSD: failedlogin.c,v 1.19 2021/10/24 21:24:16 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Todd C. Miller <millert@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * failedlogin.c
21  *	Log to failedlogin file and read from it, reporting the number of
22  *	failed logins since the last good login and when/from where
23  *	the last failed login was.
24  */
25 
26 #include <sys/stat.h>
27 #include <sys/time.h>
28 
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <utmp.h>
34 
35 #include "pathnames.h"
36 
37 struct badlogin {
38 	char	bl_line[UT_LINESIZE];	/* tty used */
39 	char	bl_name[UT_NAMESIZE];	/* remote username */
40 	char	bl_host[UT_HOSTSIZE];	/* remote host */
41 	time_t	bl_time;		/* time of the login attempt */
42 	size_t	count;			/* number of bad logins */
43 };
44 
45 void	 log_failedlogin(uid_t, char *, char *, char *);
46 int	 check_failedlogin(uid_t);
47 
48 /*
49  * Log a bad login to the failedlogin file.
50  */
51 void
log_failedlogin(uid_t uid,char * host,char * name,char * tty)52 log_failedlogin(uid_t uid, char *host, char *name, char *tty)
53 {
54 	struct badlogin failedlogin;
55 	int fd;
56 
57 	/* Add O_CREAT if you want to create failedlogin if it doesn't exist */
58 	if ((fd = open(_PATH_FAILEDLOGIN, O_RDWR)) >= 0) {
59 		(void)lseek(fd, (off_t)uid * sizeof(failedlogin), SEEK_SET);
60 
61 		/* Read in last bad login so can get the count */
62 		if (read(fd, (char *)&failedlogin, sizeof(failedlogin)) !=
63 			sizeof(failedlogin) || failedlogin.bl_time == 0)
64 			memset((void *)&failedlogin, 0, sizeof(failedlogin));
65 
66 		(void)lseek(fd, (off_t)uid * sizeof(failedlogin), SEEK_SET);
67 		/* Increment count of bad logins */
68 		++failedlogin.count;
69 		(void)time(&failedlogin.bl_time);
70 		strncpy(failedlogin.bl_line, tty, sizeof(failedlogin.bl_line));
71 		if (host)
72 			strncpy(failedlogin.bl_host, host, sizeof(failedlogin.bl_host));
73 		else
74 			*failedlogin.bl_host = '\0';	/* NULL host field */
75 		if (name)
76 			strncpy(failedlogin.bl_name, name, sizeof(failedlogin.bl_name));
77 		else
78 			*failedlogin.bl_name = '\0';	/* NULL name field */
79 		(void)write(fd, (char *)&failedlogin, sizeof(failedlogin));
80 		(void)close(fd);
81 	}
82 }
83 
84 /*
85  * Check the failedlogin file and report about the number of unsuccessful
86  * logins and info about the last one in lastlogin style.
87  * NOTE: zeros the count field since this is assumed to be called after the
88  * user has been validated.
89  */
90 int
check_failedlogin(uid_t uid)91 check_failedlogin(uid_t uid)
92 {
93 	struct badlogin failedlogin;
94 	int fd, was_bad = 0;
95 
96 	(void)memset((void *)&failedlogin, 0, sizeof(failedlogin));
97 
98 	if ((fd = open(_PATH_FAILEDLOGIN, O_RDWR)) >= 0) {
99 		(void)lseek(fd, (off_t)uid * sizeof(failedlogin), SEEK_SET);
100 		if (read(fd, (char *)&failedlogin, sizeof(failedlogin)) ==
101 		    sizeof(failedlogin) && failedlogin.count > 0 ) {
102 			/* There was a bad login */
103 			was_bad = 1;
104 			if (failedlogin.count > 1)
105 				(void)printf("There have been %lu unsuccessful "
106 				    "login attempts to your account.\n",
107 				    (u_long)failedlogin.count);
108 			(void)printf("Last unsuccessful login: %.*s", 24-5,
109 				(char *)ctime(&failedlogin.bl_time));
110 			(void)printf(" on %.*s",
111 			    (int)sizeof(failedlogin.bl_line),
112 			    failedlogin.bl_line);
113 			if (*failedlogin.bl_host != '\0') {
114 				if (*failedlogin.bl_name != '\0')
115 					(void)printf(" from %.*s@%.*s",
116 					    (int)sizeof(failedlogin.bl_name),
117 					    failedlogin.bl_name,
118 					    (int)sizeof(failedlogin.bl_host),
119 					    failedlogin.bl_host);
120 				else
121 					(void)printf(" from %.*s",
122 					    (int)sizeof(failedlogin.bl_host),
123 					    failedlogin.bl_host);
124 			}
125 			(void)putchar('\n');
126 
127 			/* Reset since this is a good login and write record */
128 			failedlogin.count = 0;
129 			(void)lseek(fd, (off_t)uid * sizeof(failedlogin),
130 			    SEEK_SET);
131 			(void)write(fd, (char *)&failedlogin,
132 			    sizeof(failedlogin));
133 		}
134 		(void)close(fd);
135 	}
136 	return(was_bad);
137 }
138