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
77extern struct sshbuf *loginmsg;
78extern 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 */
85time_t
86get_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 */
144static void
145store_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 */
175void
176record_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. */
280void
281record_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