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