1/* $NetBSD: sshlogin.c,v 1.4 2011/07/25 03:03:11 christos Exp $ */ 2/* $OpenBSD: sshlogin.c,v 1.27 2011/01/11 06:06:09 djm 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.4 2011/07/25 03:03:11 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 69extern Buffer loginmsg; 70extern 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 */ 77time_t 78get_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 close(fd); 110 return (0); 111 } 112 if (r != pos) { 113 debug("%s: truncated lastlog", __func__); 114 close(fd); 115 return (0); 116 } 117 if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) { 118 close(fd); 119 return 0; 120 } 121 close(fd); 122 if (bufsize > sizeof(ll.ll_host) + 1) 123 bufsize = sizeof(ll.ll_host) + 1; 124 strncpy(buf, ll.ll_host, bufsize - 1); 125 buf[bufsize - 1] = '\0'; 126 return (time_t)ll.ll_time; 127#else 128 return 0; 129#endif 130} 131 132/* 133 * Generate and store last login message. This must be done before 134 * login_login() is called and lastlog is updated. 135 */ 136static void 137store_lastlog_message(const char *user, uid_t uid) 138{ 139 char *time_string, hostname[MAXHOSTNAMELEN] = "", buf[512]; 140 time_t last_login_time; 141 142 if (!options.print_lastlog) 143 return; 144 145 last_login_time = get_last_login_time(uid, user, hostname, 146 sizeof(hostname)); 147 148 if (last_login_time != 0) { 149 if ((time_string = ctime(&last_login_time)) != NULL) 150 time_string[strcspn(time_string, "\n")] = '\0'; 151 if (strcmp(hostname, "") == 0) 152 snprintf(buf, sizeof(buf), "Last login: %s\r\n", 153 time_string); 154 else 155 snprintf(buf, sizeof(buf), "Last login: %s from %s\r\n", 156 time_string, hostname); 157 buffer_append(&loginmsg, buf, strlen(buf)); 158 } 159} 160 161/* 162 * Records that the user has logged in. I wish these parts of operating 163 * systems were more standardized. 164 */ 165void 166record_login(pid_t pid, const char *tty, const char *user, uid_t uid, 167 const char *host, struct sockaddr *addr, socklen_t addrlen) 168{ 169#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 170 int fd; 171#endif 172 struct timeval tv; 173#ifdef SUPPORT_UTMP 174 struct utmp u; 175 struct lastlog ll; 176#endif 177#ifdef SUPPORT_UTMPX 178 struct utmpx ux, *uxp = &ux; 179 struct lastlogx llx; 180#endif 181 (void)gettimeofday(&tv, NULL); 182 /* 183 * XXX: why do we need to handle logout cases here? 184 * Isn't the function below taking care of this? 185 */ 186 /* save previous login details before writing new */ 187 store_lastlog_message(user, uid); 188 189#ifdef SUPPORT_UTMP 190 /* Construct an utmp/wtmp entry. */ 191 memset(&u, 0, sizeof(u)); 192 strncpy(u.ut_line, tty + 5, sizeof(u.ut_line)); 193 u.ut_time = (time_t)tv.tv_sec; 194 strncpy(u.ut_name, user, sizeof(u.ut_name)); 195 strncpy(u.ut_host, host, sizeof(u.ut_host)); 196 197 login(&u); 198 199 /* Update lastlog unless actually recording a logout. */ 200 if (*user != '\0') { 201 /* 202 * It is safer to bzero the lastlog structure first because 203 * some systems might have some extra fields in it (e.g. SGI) 204 */ 205 memset(&ll, 0, sizeof(ll)); 206 207 /* Update lastlog. */ 208 ll.ll_time = time(NULL); 209 strncpy(ll.ll_line, tty + 5, sizeof(ll.ll_line)); 210 strncpy(ll.ll_host, host, sizeof(ll.ll_host)); 211 fd = open(_PATH_LASTLOG, O_RDWR); 212 if (fd >= 0) { 213 lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); 214 if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) 215 logit("Could not write %.100s: %.100s", _PATH_LASTLOG, strerror(errno)); 216 close(fd); 217 } 218 } 219#endif 220#ifdef SUPPORT_UTMPX 221 /* Construct an utmpx/wtmpx entry. */ 222 memset(&ux, 0, sizeof(ux)); 223 strncpy(ux.ut_line, tty + 5, sizeof(ux.ut_line)); 224 if (*user) { 225 ux.ut_pid = pid; 226 ux.ut_type = USER_PROCESS; 227 ux.ut_tv = tv; 228 strncpy(ux.ut_name, user, sizeof(ux.ut_name)); 229 strncpy(ux.ut_host, host, sizeof(ux.ut_host)); 230 /* XXX: need ut_id, use last 4 char of tty */ 231 if (strlen(tty) > sizeof(ux.ut_id)) { 232 strncpy(ux.ut_id, 233 tty + strlen(tty) - sizeof(ux.ut_id), 234 sizeof(ux.ut_id)); 235 } else 236 strncpy(ux.ut_id, tty, sizeof(ux.ut_id)); 237 /* XXX: It would be better if we had sockaddr_storage here */ 238 if (addrlen > sizeof(ux.ut_ss)) 239 addrlen = sizeof(ux.ut_ss); 240 (void)memcpy(&ux.ut_ss, addr, addrlen); 241 if (pututxline(&ux) == NULL) 242 logit("could not add utmpx line: %.100s", 243 strerror(errno)); 244 /* Update lastlog. */ 245 (void)gettimeofday(&llx.ll_tv, NULL); 246 strncpy(llx.ll_line, tty + 5, sizeof(llx.ll_line)); 247 strncpy(llx.ll_host, host, sizeof(llx.ll_host)); 248 (void)memcpy(&llx.ll_ss, addr, addrlen); 249 if (updlastlogx(_PATH_LASTLOGX, uid, &llx) == -1) 250 logit("Could not update %.100s: %.100s", 251 _PATH_LASTLOGX, strerror(errno)); 252 } else { 253 if ((uxp = getutxline(&ux)) == NULL) 254 logit("could not find utmpx line for %.100s", tty); 255 else { 256 uxp->ut_type = DEAD_PROCESS; 257 uxp->ut_tv = tv; 258 /* XXX: we don't record exit info yet */ 259 if (pututxline(&ux) == NULL) 260 logit("could not replace utmpx line: %.100s", 261 strerror(errno)); 262 } 263 } 264 endutxent(); 265 updwtmpx(_PATH_WTMPX, uxp); 266#endif 267} 268 269/* Records that the user has logged out. */ 270void 271record_logout(pid_t pid, const char *tty) 272{ 273#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) 274 const char *line = tty + 5; /* /dev/ttyq8 -> ttyq8 */ 275#endif 276#ifdef SUPPORT_UTMP 277 if (logout(line)) 278 logwtmp(line, "", ""); 279#endif 280#ifdef SUPPORT_UTMPX 281 /* XXX: no exit info yet */ 282 if (logoutx(line, 0, DEAD_PROCESS)) 283 logwtmpx(line, "", "", 0, DEAD_PROCESS); 284#endif 285} 286