xref: /netbsd-src/crypto/external/bsd/openssh/dist/sshlogin.c (revision 17418e98f281f84e3d22de9717222f9c2ee84d3a)
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