1 /* $NetBSD: sshlogin.c,v 1.13 2021/03/05 17:47:16 christos Exp $ */
2 /* $OpenBSD: sshlogin.c,v 1.35 2020/10/18 11:32:02 djm Exp $ */
3
4 /*
5 * Author: Tatu Ylonen <ylo@cs.hut.fi>
6 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
7 * All rights reserved
8 * This file performs some of the things login(1) normally does. We cannot
9 * easily use something like login -p -h host -f user, because there are
10 * several different logins around, and it is hard to determined what kind of
11 * login the current system has. Also, we want to be able to execute commands
12 * on a tty.
13 *
14 * As far as I am concerned, the code I have written for this software
15 * can be used freely for any purpose. Any derived versions of this
16 * software must be clearly marked as such, and if the derived work is
17 * incompatible with the protocol description in the RFC file, it must be
18 * called by a name other than "ssh" or "Secure Shell".
19 *
20 * Copyright (c) 1999 Theo de Raadt. All rights reserved.
21 * Copyright (c) 1999 Markus Friedl. All rights reserved.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the above copyright
27 * notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 * notice, this list of conditions and the following disclaimer in the
30 * documentation and/or other materials provided with the distribution.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
33 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
35 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
36 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
41 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 */
43
44 #include "includes.h"
45 __RCSID("$NetBSD: sshlogin.c,v 1.13 2021/03/05 17:47:16 christos Exp $");
46 #include <sys/param.h>
47 #include <sys/types.h>
48 #include <sys/socket.h>
49
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <stdio.h>
53 #include <string.h>
54 #include <time.h>
55 #include <unistd.h>
56 #include <util.h>
57 #ifdef SUPPORT_UTMP
58 #include <utmp.h>
59 #endif
60 #ifdef SUPPORT_UTMPX
61 #include <utmpx.h>
62 #endif
63 #include <stdarg.h>
64 #include <limits.h>
65
66 #include "sshlogin.h"
67 #include "ssherr.h"
68 #include "log.h"
69 #include "sshbuf.h"
70 #include "misc.h"
71 #include "servconf.h"
72
73 #ifndef HOST_NAME_MAX
74 #define HOST_NAME_MAX MAXHOSTNAMELEN
75 #endif
76
77 extern struct sshbuf *loginmsg;
78 extern ServerOptions options;
79
80 /*
81 * Returns the time when the user last logged in. Returns 0 if the
82 * information is not available. This must be called before record_login.
83 * The host the user logged in from will be returned in buf.
84 */
85 time_t
get_last_login_time(uid_t uid,const char * logname,char * buf,size_t bufsize)86 get_last_login_time(uid_t uid, const char *logname,
87 char *buf, size_t bufsize)
88 {
89 #ifdef SUPPORT_UTMPX
90 struct lastlogx llx, *llxp;
91 #endif
92 #ifdef SUPPORT_UTMP
93 struct lastlog ll;
94 int fd;
95 #endif
96 off_t pos, r;
97
98 buf[0] = '\0';
99 #ifdef SUPPORT_UTMPX
100 if ((llxp = getlastlogx(_PATH_LASTLOGX, uid, &llx)) != NULL) {
101 if (bufsize > sizeof(llxp->ll_host) + 1)
102 bufsize = sizeof(llxp->ll_host) + 1;
103 strncpy(buf, llxp->ll_host, bufsize - 1);
104 buf[bufsize - 1] = 0;
105 return llxp->ll_tv.tv_sec;
106 }
107 #endif
108 #ifdef SUPPORT_UTMP
109 fd = open(_PATH_LASTLOG, O_RDONLY);
110 if (fd == -1)
111 return 0;
112
113 pos = (off_t)uid * sizeof(ll);
114 r = lseek(fd, pos, SEEK_SET);
115 if (r == -1) {
116 error_f("lseek: %s", strerror(errno));
117 close(fd);
118 return (0);
119 }
120 if (r != pos) {
121 debug_f("truncated lastlog");
122 close(fd);
123 return (0);
124 }
125 if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) {
126 close(fd);
127 return 0;
128 }
129 close(fd);
130 if (bufsize > sizeof(ll.ll_host) + 1)
131 bufsize = sizeof(ll.ll_host) + 1;
132 strncpy(buf, ll.ll_host, bufsize - 1);
133 buf[bufsize - 1] = '\0';
134 return (time_t)ll.ll_time;
135 #else
136 return 0;
137 #endif
138 }
139
140 /*
141 * Generate and store last login message. This must be done before
142 * login_login() is called and lastlog is updated.
143 */
144 static void
store_lastlog_message(const char * user,uid_t uid)145 store_lastlog_message(const char *user, uid_t uid)
146 {
147 char *time_string, hostname[HOST_NAME_MAX+1] = "";
148 time_t last_login_time;
149 int r;
150
151 if (!options.print_lastlog)
152 return;
153
154 last_login_time = get_last_login_time(uid, user, hostname,
155 sizeof(hostname));
156
157 if (last_login_time != 0) {
158 if ((time_string = ctime(&last_login_time)) != NULL)
159 time_string[strcspn(time_string, "\n")] = '\0';
160 if (strcmp(hostname, "") == 0)
161 r = sshbuf_putf(loginmsg, "Last login: %s\r\n",
162 time_string);
163 else
164 r = sshbuf_putf(loginmsg, "Last login: %s from %s\r\n",
165 time_string, hostname);
166 if (r != 0)
167 fatal_fr(r, "sshbuf_putf");
168 }
169 }
170
171 /*
172 * Records that the user has logged in. I wish these parts of operating
173 * systems were more standardized.
174 */
175 void
record_login(pid_t pid,const char * tty,const char * user,uid_t uid,const char * host,struct sockaddr * addr,socklen_t addrlen)176 record_login(pid_t pid, const char *tty, const char *user, uid_t uid,
177 const char *host, struct sockaddr *addr, socklen_t addrlen)
178 {
179 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
180 int fd;
181 #endif
182 struct timeval tv;
183 #ifdef SUPPORT_UTMP
184 struct utmp u;
185 struct lastlog ll;
186 #endif
187 #ifdef SUPPORT_UTMPX
188 struct utmpx ux, *uxp = &ux;
189 struct lastlogx llx;
190 #endif
191 (void)gettimeofday(&tv, NULL);
192 /*
193 * XXX: why do we need to handle logout cases here?
194 * Isn't the function below taking care of this?
195 */
196 /* save previous login details before writing new */
197 store_lastlog_message(user, uid);
198
199 #ifdef SUPPORT_UTMP
200 /* Construct an utmp/wtmp entry. */
201 memset(&u, 0, sizeof(u));
202 strncpy(u.ut_line, tty + 5, sizeof(u.ut_line));
203 u.ut_time = (time_t)tv.tv_sec;
204 strncpy(u.ut_name, user, sizeof(u.ut_name));
205 strncpy(u.ut_host, host, sizeof(u.ut_host));
206
207 login(&u);
208
209 /* Update lastlog unless actually recording a logout. */
210 if (*user != '\0') {
211 /*
212 * It is safer to memset the lastlog structure first because
213 * some systems might have some extra fields in it (e.g. SGI)
214 */
215 memset(&ll, 0, sizeof(ll));
216
217 /* Update lastlog. */
218 ll.ll_time = time(NULL);
219 strncpy(ll.ll_line, tty + 5, sizeof(ll.ll_line));
220 strncpy(ll.ll_host, host, sizeof(ll.ll_host));
221 fd = open(_PATH_LASTLOG, O_RDWR);
222 if (fd >= 0) {
223 lseek(fd, (off_t)uid * sizeof(ll), SEEK_SET);
224 if (write(fd, &ll, sizeof(ll)) != sizeof(ll))
225 logit("Could not write %.100s: %.100s", _PATH_LASTLOG, strerror(errno));
226 close(fd);
227 }
228 }
229 #endif
230 #ifdef SUPPORT_UTMPX
231 /* Construct an utmpx/wtmpx entry. */
232 memset(&ux, 0, sizeof(ux));
233 strncpy(ux.ut_line, tty + 5, sizeof(ux.ut_line));
234 if (*user) {
235 ux.ut_pid = pid;
236 ux.ut_type = USER_PROCESS;
237 ux.ut_tv = tv;
238 strncpy(ux.ut_name, user, sizeof(ux.ut_name));
239 strncpy(ux.ut_host, host, sizeof(ux.ut_host));
240 /* XXX: need ut_id, use last 4 char of tty */
241 if (strlen(tty) > sizeof(ux.ut_id)) {
242 strncpy(ux.ut_id,
243 tty + strlen(tty) - sizeof(ux.ut_id),
244 sizeof(ux.ut_id));
245 } else
246 strncpy(ux.ut_id, tty, sizeof(ux.ut_id));
247 /* XXX: It would be better if we had sockaddr_storage here */
248 if (addrlen > sizeof(ux.ut_ss))
249 addrlen = sizeof(ux.ut_ss);
250 (void)memcpy(&ux.ut_ss, addr, addrlen);
251 if (pututxline(&ux) == NULL)
252 logit("could not add utmpx line: %.100s",
253 strerror(errno));
254 /* Update lastlog. */
255 (void)gettimeofday(&llx.ll_tv, NULL);
256 strncpy(llx.ll_line, tty + 5, sizeof(llx.ll_line));
257 strncpy(llx.ll_host, host, sizeof(llx.ll_host));
258 (void)memcpy(&llx.ll_ss, addr, addrlen);
259 if (updlastlogx(_PATH_LASTLOGX, uid, &llx) == -1)
260 logit("Could not update %.100s: %.100s",
261 _PATH_LASTLOGX, strerror(errno));
262 } else {
263 if ((uxp = getutxline(&ux)) == NULL)
264 logit("could not find utmpx line for %.100s", tty);
265 else {
266 uxp->ut_type = DEAD_PROCESS;
267 uxp->ut_tv = tv;
268 /* XXX: we don't record exit info yet */
269 if (pututxline(&ux) == NULL)
270 logit("could not replace utmpx line: %.100s",
271 strerror(errno));
272 }
273 }
274 endutxent();
275 updwtmpx(_PATH_WTMPX, uxp);
276 #endif
277 }
278
279 /* Records that the user has logged out. */
280 void
record_logout(pid_t pid,const char * tty)281 record_logout(pid_t pid, const char *tty)
282 {
283 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
284 const char *line = tty + 5; /* /dev/ttyq8 -> ttyq8 */
285 #endif
286 #ifdef SUPPORT_UTMP
287 if (logout(line))
288 logwtmp(line, "", "");
289 #endif
290 #ifdef SUPPORT_UTMPX
291 /* XXX: no exit info yet */
292 if (logoutx(line, 0, DEAD_PROCESS))
293 logwtmpx(line, "", "", 0, DEAD_PROCESS);
294 #endif
295 }
296