1 |
|
|
/* $OpenBSD: sshlogin.c,v 1.32 2015/12/26 20:51:35 guenther Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 |
|
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
5 |
|
|
* All rights reserved |
6 |
|
|
* This file performs some of the things login(1) normally does. We cannot |
7 |
|
|
* easily use something like login -p -h host -f user, because there are |
8 |
|
|
* several different logins around, and it is hard to determined what kind of |
9 |
|
|
* login the current system has. Also, we want to be able to execute commands |
10 |
|
|
* on a tty. |
11 |
|
|
* |
12 |
|
|
* As far as I am concerned, the code I have written for this software |
13 |
|
|
* can be used freely for any purpose. Any derived versions of this |
14 |
|
|
* software must be clearly marked as such, and if the derived work is |
15 |
|
|
* incompatible with the protocol description in the RFC file, it must be |
16 |
|
|
* called by a name other than "ssh" or "Secure Shell". |
17 |
|
|
* |
18 |
|
|
* Copyright (c) 1999 Theo de Raadt. All rights reserved. |
19 |
|
|
* Copyright (c) 1999 Markus Friedl. All rights reserved. |
20 |
|
|
* |
21 |
|
|
* Redistribution and use in source and binary forms, with or without |
22 |
|
|
* modification, are permitted provided that the following conditions |
23 |
|
|
* are met: |
24 |
|
|
* 1. Redistributions of source code must retain the above copyright |
25 |
|
|
* notice, this list of conditions and the following disclaimer. |
26 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
27 |
|
|
* notice, this list of conditions and the following disclaimer in the |
28 |
|
|
* documentation and/or other materials provided with the distribution. |
29 |
|
|
* |
30 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
31 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
32 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
33 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
34 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
35 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
39 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 |
|
|
*/ |
41 |
|
|
|
42 |
|
|
#include <sys/types.h> |
43 |
|
|
#include <sys/socket.h> |
44 |
|
|
|
45 |
|
|
#include <errno.h> |
46 |
|
|
#include <fcntl.h> |
47 |
|
|
#include <stdio.h> |
48 |
|
|
#include <string.h> |
49 |
|
|
#include <time.h> |
50 |
|
|
#include <unistd.h> |
51 |
|
|
#include <util.h> |
52 |
|
|
#include <utmp.h> |
53 |
|
|
#include <stdarg.h> |
54 |
|
|
#include <limits.h> |
55 |
|
|
|
56 |
|
|
#include "sshlogin.h" |
57 |
|
|
#include "log.h" |
58 |
|
|
#include "buffer.h" |
59 |
|
|
#include "misc.h" |
60 |
|
|
#include "servconf.h" |
61 |
|
|
|
62 |
|
|
extern Buffer loginmsg; |
63 |
|
|
extern ServerOptions options; |
64 |
|
|
|
65 |
|
|
/* |
66 |
|
|
* Returns the time when the user last logged in. Returns 0 if the |
67 |
|
|
* information is not available. This must be called before record_login. |
68 |
|
|
* The host the user logged in from will be returned in buf. |
69 |
|
|
*/ |
70 |
|
|
time_t |
71 |
|
|
get_last_login_time(uid_t uid, const char *logname, |
72 |
|
|
char *buf, size_t bufsize) |
73 |
|
|
{ |
74 |
|
|
struct lastlog ll; |
75 |
|
|
char *lastlog; |
76 |
|
|
int fd; |
77 |
|
|
off_t pos, r; |
78 |
|
|
|
79 |
|
|
lastlog = _PATH_LASTLOG; |
80 |
|
|
buf[0] = '\0'; |
81 |
|
|
|
82 |
|
|
fd = open(lastlog, O_RDONLY); |
83 |
|
|
if (fd < 0) |
84 |
|
|
return 0; |
85 |
|
|
|
86 |
|
|
pos = (off_t)uid * sizeof(ll); |
87 |
|
|
r = lseek(fd, pos, SEEK_SET); |
88 |
|
|
if (r == -1) { |
89 |
|
|
error("%s: lseek: %s", __func__, strerror(errno)); |
90 |
|
|
close(fd); |
91 |
|
|
return (0); |
92 |
|
|
} |
93 |
|
|
if (r != pos) { |
94 |
|
|
debug("%s: truncated lastlog", __func__); |
95 |
|
|
close(fd); |
96 |
|
|
return (0); |
97 |
|
|
} |
98 |
|
|
if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) { |
99 |
|
|
close(fd); |
100 |
|
|
return 0; |
101 |
|
|
} |
102 |
|
|
close(fd); |
103 |
|
|
if (bufsize > sizeof(ll.ll_host) + 1) |
104 |
|
|
bufsize = sizeof(ll.ll_host) + 1; |
105 |
|
|
strncpy(buf, ll.ll_host, bufsize - 1); |
106 |
|
|
buf[bufsize - 1] = '\0'; |
107 |
|
|
return (time_t)ll.ll_time; |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
/* |
111 |
|
|
* Generate and store last login message. This must be done before |
112 |
|
|
* login_login() is called and lastlog is updated. |
113 |
|
|
*/ |
114 |
|
|
static void |
115 |
|
|
store_lastlog_message(const char *user, uid_t uid) |
116 |
|
|
{ |
117 |
|
|
char *time_string, hostname[HOST_NAME_MAX+1] = "", buf[512]; |
118 |
|
|
time_t last_login_time; |
119 |
|
|
|
120 |
|
|
if (!options.print_lastlog) |
121 |
|
|
return; |
122 |
|
|
|
123 |
|
|
last_login_time = get_last_login_time(uid, user, hostname, |
124 |
|
|
sizeof(hostname)); |
125 |
|
|
|
126 |
|
|
if (last_login_time != 0) { |
127 |
|
|
time_string = ctime(&last_login_time); |
128 |
|
|
time_string[strcspn(time_string, "\n")] = '\0'; |
129 |
|
|
if (strcmp(hostname, "") == 0) |
130 |
|
|
snprintf(buf, sizeof(buf), "Last login: %s\r\n", |
131 |
|
|
time_string); |
132 |
|
|
else |
133 |
|
|
snprintf(buf, sizeof(buf), "Last login: %s from %s\r\n", |
134 |
|
|
time_string, hostname); |
135 |
|
|
buffer_append(&loginmsg, buf, strlen(buf)); |
136 |
|
|
} |
137 |
|
|
} |
138 |
|
|
|
139 |
|
|
/* |
140 |
|
|
* Records that the user has logged in. I wish these parts of operating |
141 |
|
|
* systems were more standardized. |
142 |
|
|
*/ |
143 |
|
|
void |
144 |
|
|
record_login(pid_t pid, const char *tty, const char *user, uid_t uid, |
145 |
|
|
const char *host, struct sockaddr *addr, socklen_t addrlen) |
146 |
|
|
{ |
147 |
|
|
int fd; |
148 |
|
|
struct lastlog ll; |
149 |
|
|
char *lastlog; |
150 |
|
|
struct utmp u; |
151 |
|
|
|
152 |
|
|
/* save previous login details before writing new */ |
153 |
|
|
store_lastlog_message(user, uid); |
154 |
|
|
|
155 |
|
|
/* Construct an utmp/wtmp entry. */ |
156 |
|
|
memset(&u, 0, sizeof(u)); |
157 |
|
|
strncpy(u.ut_line, tty + 5, sizeof(u.ut_line)); |
158 |
|
|
u.ut_time = time(NULL); |
159 |
|
|
strncpy(u.ut_name, user, sizeof(u.ut_name)); |
160 |
|
|
strncpy(u.ut_host, host, sizeof(u.ut_host)); |
161 |
|
|
|
162 |
|
|
login(&u); |
163 |
|
|
lastlog = _PATH_LASTLOG; |
164 |
|
|
|
165 |
|
|
/* Update lastlog unless actually recording a logout. */ |
166 |
|
|
if (strcmp(user, "") != 0) { |
167 |
|
|
/* |
168 |
|
|
* It is safer to memset the lastlog structure first because |
169 |
|
|
* some systems might have some extra fields in it (e.g. SGI) |
170 |
|
|
*/ |
171 |
|
|
memset(&ll, 0, sizeof(ll)); |
172 |
|
|
|
173 |
|
|
/* Update lastlog. */ |
174 |
|
|
ll.ll_time = time(NULL); |
175 |
|
|
strncpy(ll.ll_line, tty + 5, sizeof(ll.ll_line)); |
176 |
|
|
strncpy(ll.ll_host, host, sizeof(ll.ll_host)); |
177 |
|
|
fd = open(lastlog, O_RDWR); |
178 |
|
|
if (fd >= 0) { |
179 |
|
|
lseek(fd, (off_t)uid * sizeof(ll), SEEK_SET); |
180 |
|
|
if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) |
181 |
|
|
logit("Could not write %.100s: %.100s", lastlog, strerror(errno)); |
182 |
|
|
close(fd); |
183 |
|
|
} |
184 |
|
|
} |
185 |
|
|
} |
186 |
|
|
|
187 |
|
|
/* Records that the user has logged out. */ |
188 |
|
|
void |
189 |
|
|
record_logout(pid_t pid, const char *tty) |
190 |
|
|
{ |
191 |
|
|
const char *line = tty + 5; /* /dev/ttyq8 -> ttyq8 */ |
192 |
|
|
if (logout(line)) |
193 |
|
|
logwtmp(line, "", ""); |
194 |
|
|
} |