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