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