1*17418e98Schristos /* $NetBSD: sshlogin.c,v 1.13 2021/03/05 17:47:16 christos Exp $ */
2*17418e98Schristos /* $OpenBSD: sshlogin.c,v 1.35 2020/10/18 11:32:02 djm Exp $ */
3*17418e98Schristos
4ca32bd8dSchristos /*
5ca32bd8dSchristos * Author: Tatu Ylonen <ylo@cs.hut.fi>
6ca32bd8dSchristos * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
7ca32bd8dSchristos * All rights reserved
8ca32bd8dSchristos * This file performs some of the things login(1) normally does. We cannot
9ca32bd8dSchristos * easily use something like login -p -h host -f user, because there are
10ca32bd8dSchristos * several different logins around, and it is hard to determined what kind of
11ca32bd8dSchristos * login the current system has. Also, we want to be able to execute commands
12ca32bd8dSchristos * on a tty.
13ca32bd8dSchristos *
14ca32bd8dSchristos * As far as I am concerned, the code I have written for this software
15ca32bd8dSchristos * can be used freely for any purpose. Any derived versions of this
16ca32bd8dSchristos * software must be clearly marked as such, and if the derived work is
17ca32bd8dSchristos * incompatible with the protocol description in the RFC file, it must be
18ca32bd8dSchristos * called by a name other than "ssh" or "Secure Shell".
19ca32bd8dSchristos *
20ca32bd8dSchristos * Copyright (c) 1999 Theo de Raadt. All rights reserved.
21ca32bd8dSchristos * Copyright (c) 1999 Markus Friedl. All rights reserved.
22ca32bd8dSchristos *
23ca32bd8dSchristos * Redistribution and use in source and binary forms, with or without
24ca32bd8dSchristos * modification, are permitted provided that the following conditions
25ca32bd8dSchristos * are met:
26ca32bd8dSchristos * 1. Redistributions of source code must retain the above copyright
27ca32bd8dSchristos * notice, this list of conditions and the following disclaimer.
28ca32bd8dSchristos * 2. Redistributions in binary form must reproduce the above copyright
29ca32bd8dSchristos * notice, this list of conditions and the following disclaimer in the
30ca32bd8dSchristos * documentation and/or other materials provided with the distribution.
31ca32bd8dSchristos *
32ca32bd8dSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
33ca32bd8dSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34ca32bd8dSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
35ca32bd8dSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
36ca32bd8dSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37ca32bd8dSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38ca32bd8dSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39ca32bd8dSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40ca32bd8dSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
41ca32bd8dSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42ca32bd8dSchristos */
43ca32bd8dSchristos
44313c6c94Schristos #include "includes.h"
45*17418e98Schristos __RCSID("$NetBSD: sshlogin.c,v 1.13 2021/03/05 17:47:16 christos Exp $");
46ca32bd8dSchristos #include <sys/param.h>
47e4d43b82Schristos #include <sys/types.h>
48ca32bd8dSchristos #include <sys/socket.h>
49ca32bd8dSchristos
50ca32bd8dSchristos #include <errno.h>
51ca32bd8dSchristos #include <fcntl.h>
52ca32bd8dSchristos #include <stdio.h>
53ca32bd8dSchristos #include <string.h>
54ca32bd8dSchristos #include <time.h>
55ca32bd8dSchristos #include <unistd.h>
56ca32bd8dSchristos #include <util.h>
57313c6c94Schristos #ifdef SUPPORT_UTMP
58ca32bd8dSchristos #include <utmp.h>
59313c6c94Schristos #endif
60313c6c94Schristos #ifdef SUPPORT_UTMPX
61313c6c94Schristos #include <utmpx.h>
62313c6c94Schristos #endif
63ca32bd8dSchristos #include <stdarg.h>
64e4d43b82Schristos #include <limits.h>
65ca32bd8dSchristos
66ca32bd8dSchristos #include "sshlogin.h"
6755a4608bSchristos #include "ssherr.h"
68ca32bd8dSchristos #include "log.h"
6955a4608bSchristos #include "sshbuf.h"
708a4530f9Schristos #include "misc.h"
71ca32bd8dSchristos #include "servconf.h"
72ca32bd8dSchristos
73e4d43b82Schristos #ifndef HOST_NAME_MAX
74e4d43b82Schristos #define HOST_NAME_MAX MAXHOSTNAMELEN
75e4d43b82Schristos #endif
76e4d43b82Schristos
7755a4608bSchristos extern struct sshbuf *loginmsg;
78ca32bd8dSchristos extern ServerOptions options;
79ca32bd8dSchristos
80ca32bd8dSchristos /*
81ca32bd8dSchristos * Returns the time when the user last logged in. Returns 0 if the
82ca32bd8dSchristos * information is not available. This must be called before record_login.
83ca32bd8dSchristos * The host the user logged in from will be returned in buf.
84ca32bd8dSchristos */
85ca32bd8dSchristos time_t
get_last_login_time(uid_t uid,const char * logname,char * buf,size_t bufsize)86ca32bd8dSchristos get_last_login_time(uid_t uid, const char *logname,
87ca32bd8dSchristos char *buf, size_t bufsize)
88ca32bd8dSchristos {
89313c6c94Schristos #ifdef SUPPORT_UTMPX
90313c6c94Schristos struct lastlogx llx, *llxp;
91313c6c94Schristos #endif
92313c6c94Schristos #ifdef SUPPORT_UTMP
93ca32bd8dSchristos struct lastlog ll;
94ca32bd8dSchristos int fd;
95313c6c94Schristos #endif
96ca32bd8dSchristos off_t pos, r;
97ca32bd8dSchristos
98ca32bd8dSchristos buf[0] = '\0';
99313c6c94Schristos #ifdef SUPPORT_UTMPX
100313c6c94Schristos if ((llxp = getlastlogx(_PATH_LASTLOGX, uid, &llx)) != NULL) {
101313c6c94Schristos if (bufsize > sizeof(llxp->ll_host) + 1)
102313c6c94Schristos bufsize = sizeof(llxp->ll_host) + 1;
103313c6c94Schristos strncpy(buf, llxp->ll_host, bufsize - 1);
104313c6c94Schristos buf[bufsize - 1] = 0;
105313c6c94Schristos return llxp->ll_tv.tv_sec;
106313c6c94Schristos }
107313c6c94Schristos #endif
108313c6c94Schristos #ifdef SUPPORT_UTMP
109313c6c94Schristos fd = open(_PATH_LASTLOG, O_RDONLY);
110cd4ada6aSchristos if (fd == -1)
111ca32bd8dSchristos return 0;
112ca32bd8dSchristos
11379976551Schristos pos = (off_t)uid * sizeof(ll);
114ca32bd8dSchristos r = lseek(fd, pos, SEEK_SET);
115ca32bd8dSchristos if (r == -1) {
116*17418e98Schristos error_f("lseek: %s", strerror(errno));
117185c8f97Schristos close(fd);
118ca32bd8dSchristos return (0);
119ca32bd8dSchristos }
120ca32bd8dSchristos if (r != pos) {
121*17418e98Schristos debug_f("truncated lastlog");
122185c8f97Schristos close(fd);
123ca32bd8dSchristos return (0);
124ca32bd8dSchristos }
125ca32bd8dSchristos if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) {
126ca32bd8dSchristos close(fd);
127ca32bd8dSchristos return 0;
128ca32bd8dSchristos }
129ca32bd8dSchristos close(fd);
130ca32bd8dSchristos if (bufsize > sizeof(ll.ll_host) + 1)
131ca32bd8dSchristos bufsize = sizeof(ll.ll_host) + 1;
132ca32bd8dSchristos strncpy(buf, ll.ll_host, bufsize - 1);
133ca32bd8dSchristos buf[bufsize - 1] = '\0';
134ca32bd8dSchristos return (time_t)ll.ll_time;
135313c6c94Schristos #else
136313c6c94Schristos return 0;
137313c6c94Schristos #endif
138ca32bd8dSchristos }
139ca32bd8dSchristos
140ca32bd8dSchristos /*
141ca32bd8dSchristos * Generate and store last login message. This must be done before
142ca32bd8dSchristos * login_login() is called and lastlog is updated.
143ca32bd8dSchristos */
144ca32bd8dSchristos static void
store_lastlog_message(const char * user,uid_t uid)145ca32bd8dSchristos store_lastlog_message(const char *user, uid_t uid)
146ca32bd8dSchristos {
14755a4608bSchristos char *time_string, hostname[HOST_NAME_MAX+1] = "";
148ca32bd8dSchristos time_t last_login_time;
14955a4608bSchristos int r;
150ca32bd8dSchristos
151ca32bd8dSchristos if (!options.print_lastlog)
152ca32bd8dSchristos return;
153ca32bd8dSchristos
154ca32bd8dSchristos last_login_time = get_last_login_time(uid, user, hostname,
155ca32bd8dSchristos sizeof(hostname));
156ca32bd8dSchristos
157ca32bd8dSchristos if (last_login_time != 0) {
158f1b28409Schristos if ((time_string = ctime(&last_login_time)) != NULL)
159ca32bd8dSchristos time_string[strcspn(time_string, "\n")] = '\0';
160ca32bd8dSchristos if (strcmp(hostname, "") == 0)
16155a4608bSchristos r = sshbuf_putf(loginmsg, "Last login: %s\r\n",
162ca32bd8dSchristos time_string);
163ca32bd8dSchristos else
16455a4608bSchristos r = sshbuf_putf(loginmsg, "Last login: %s from %s\r\n",
165ca32bd8dSchristos time_string, hostname);
16655a4608bSchristos if (r != 0)
167*17418e98Schristos fatal_fr(r, "sshbuf_putf");
168ca32bd8dSchristos }
169ca32bd8dSchristos }
170ca32bd8dSchristos
171ca32bd8dSchristos /*
172ca32bd8dSchristos * Records that the user has logged in. I wish these parts of operating
173ca32bd8dSchristos * systems were more standardized.
174ca32bd8dSchristos */
175ca32bd8dSchristos void
record_login(pid_t pid,const char * tty,const char * user,uid_t uid,const char * host,struct sockaddr * addr,socklen_t addrlen)176ca32bd8dSchristos record_login(pid_t pid, const char *tty, const char *user, uid_t uid,
177ca32bd8dSchristos const char *host, struct sockaddr *addr, socklen_t addrlen)
178ca32bd8dSchristos {
179313c6c94Schristos #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
180ca32bd8dSchristos int fd;
181313c6c94Schristos #endif
182313c6c94Schristos struct timeval tv;
183313c6c94Schristos #ifdef SUPPORT_UTMP
184ca32bd8dSchristos struct utmp u;
185313c6c94Schristos struct lastlog ll;
186313c6c94Schristos #endif
187313c6c94Schristos #ifdef SUPPORT_UTMPX
188313c6c94Schristos struct utmpx ux, *uxp = &ux;
189313c6c94Schristos struct lastlogx llx;
190313c6c94Schristos #endif
191313c6c94Schristos (void)gettimeofday(&tv, NULL);
192313c6c94Schristos /*
193313c6c94Schristos * XXX: why do we need to handle logout cases here?
194313c6c94Schristos * Isn't the function below taking care of this?
195313c6c94Schristos */
196ca32bd8dSchristos /* save previous login details before writing new */
197ca32bd8dSchristos store_lastlog_message(user, uid);
198ca32bd8dSchristos
199313c6c94Schristos #ifdef SUPPORT_UTMP
200ca32bd8dSchristos /* Construct an utmp/wtmp entry. */
201ca32bd8dSchristos memset(&u, 0, sizeof(u));
202ca32bd8dSchristos strncpy(u.ut_line, tty + 5, sizeof(u.ut_line));
203313c6c94Schristos u.ut_time = (time_t)tv.tv_sec;
204ca32bd8dSchristos strncpy(u.ut_name, user, sizeof(u.ut_name));
205ca32bd8dSchristos strncpy(u.ut_host, host, sizeof(u.ut_host));
206ca32bd8dSchristos
207ca32bd8dSchristos login(&u);
208ca32bd8dSchristos
209ca32bd8dSchristos /* Update lastlog unless actually recording a logout. */
210313c6c94Schristos if (*user != '\0') {
211ca32bd8dSchristos /*
2128a4530f9Schristos * It is safer to memset the lastlog structure first because
213ca32bd8dSchristos * some systems might have some extra fields in it (e.g. SGI)
214ca32bd8dSchristos */
215ca32bd8dSchristos memset(&ll, 0, sizeof(ll));
216ca32bd8dSchristos
217ca32bd8dSchristos /* Update lastlog. */
218ca32bd8dSchristos ll.ll_time = time(NULL);
219ca32bd8dSchristos strncpy(ll.ll_line, tty + 5, sizeof(ll.ll_line));
220ca32bd8dSchristos strncpy(ll.ll_host, host, sizeof(ll.ll_host));
221313c6c94Schristos fd = open(_PATH_LASTLOG, O_RDWR);
222ca32bd8dSchristos if (fd >= 0) {
22379976551Schristos lseek(fd, (off_t)uid * sizeof(ll), SEEK_SET);
224ca32bd8dSchristos if (write(fd, &ll, sizeof(ll)) != sizeof(ll))
225313c6c94Schristos logit("Could not write %.100s: %.100s", _PATH_LASTLOG, strerror(errno));
226ca32bd8dSchristos close(fd);
227ca32bd8dSchristos }
228ca32bd8dSchristos }
229313c6c94Schristos #endif
230313c6c94Schristos #ifdef SUPPORT_UTMPX
231313c6c94Schristos /* Construct an utmpx/wtmpx entry. */
232313c6c94Schristos memset(&ux, 0, sizeof(ux));
233313c6c94Schristos strncpy(ux.ut_line, tty + 5, sizeof(ux.ut_line));
234313c6c94Schristos if (*user) {
235313c6c94Schristos ux.ut_pid = pid;
236313c6c94Schristos ux.ut_type = USER_PROCESS;
237313c6c94Schristos ux.ut_tv = tv;
238313c6c94Schristos strncpy(ux.ut_name, user, sizeof(ux.ut_name));
239313c6c94Schristos strncpy(ux.ut_host, host, sizeof(ux.ut_host));
240313c6c94Schristos /* XXX: need ut_id, use last 4 char of tty */
241313c6c94Schristos if (strlen(tty) > sizeof(ux.ut_id)) {
242313c6c94Schristos strncpy(ux.ut_id,
243313c6c94Schristos tty + strlen(tty) - sizeof(ux.ut_id),
244313c6c94Schristos sizeof(ux.ut_id));
245313c6c94Schristos } else
246313c6c94Schristos strncpy(ux.ut_id, tty, sizeof(ux.ut_id));
247313c6c94Schristos /* XXX: It would be better if we had sockaddr_storage here */
248313c6c94Schristos if (addrlen > sizeof(ux.ut_ss))
249313c6c94Schristos addrlen = sizeof(ux.ut_ss);
250313c6c94Schristos (void)memcpy(&ux.ut_ss, addr, addrlen);
251313c6c94Schristos if (pututxline(&ux) == NULL)
252313c6c94Schristos logit("could not add utmpx line: %.100s",
253313c6c94Schristos strerror(errno));
254313c6c94Schristos /* Update lastlog. */
255313c6c94Schristos (void)gettimeofday(&llx.ll_tv, NULL);
256313c6c94Schristos strncpy(llx.ll_line, tty + 5, sizeof(llx.ll_line));
257313c6c94Schristos strncpy(llx.ll_host, host, sizeof(llx.ll_host));
258313c6c94Schristos (void)memcpy(&llx.ll_ss, addr, addrlen);
259313c6c94Schristos if (updlastlogx(_PATH_LASTLOGX, uid, &llx) == -1)
260313c6c94Schristos logit("Could not update %.100s: %.100s",
261313c6c94Schristos _PATH_LASTLOGX, strerror(errno));
262313c6c94Schristos } else {
263313c6c94Schristos if ((uxp = getutxline(&ux)) == NULL)
264313c6c94Schristos logit("could not find utmpx line for %.100s", tty);
265313c6c94Schristos else {
266313c6c94Schristos uxp->ut_type = DEAD_PROCESS;
267313c6c94Schristos uxp->ut_tv = tv;
268313c6c94Schristos /* XXX: we don't record exit info yet */
269313c6c94Schristos if (pututxline(&ux) == NULL)
270313c6c94Schristos logit("could not replace utmpx line: %.100s",
271313c6c94Schristos strerror(errno));
272313c6c94Schristos }
273313c6c94Schristos }
274313c6c94Schristos endutxent();
275313c6c94Schristos updwtmpx(_PATH_WTMPX, uxp);
276313c6c94Schristos #endif
277ca32bd8dSchristos }
278ca32bd8dSchristos
279ca32bd8dSchristos /* Records that the user has logged out. */
280ca32bd8dSchristos void
record_logout(pid_t pid,const char * tty)281ca32bd8dSchristos record_logout(pid_t pid, const char *tty)
282ca32bd8dSchristos {
283313c6c94Schristos #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
284ca32bd8dSchristos const char *line = tty + 5; /* /dev/ttyq8 -> ttyq8 */
285313c6c94Schristos #endif
286313c6c94Schristos #ifdef SUPPORT_UTMP
287ca32bd8dSchristos if (logout(line))
288ca32bd8dSchristos logwtmp(line, "", "");
289313c6c94Schristos #endif
290313c6c94Schristos #ifdef SUPPORT_UTMPX
291313c6c94Schristos /* XXX: no exit info yet */
292313c6c94Schristos if (logoutx(line, 0, DEAD_PROCESS))
293313c6c94Schristos logwtmpx(line, "", "", 0, DEAD_PROCESS);
294313c6c94Schristos #endif
295ca32bd8dSchristos }
296