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