2 /* $OpenBSD: sshlogin.c,v 1.26 2007/09/11 15:47:17 gilles Exp $ */
4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
5 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
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
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".
19 * Copyright (c) 1999 Theo de Raadt. All rights reserved.
20 * Copyright (c) 1999 Markus Friedl. All rights reserved.
22 * Redistribution and use in source and binary forms, with or without
23 * modification, are permitted provided that the following conditions
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.
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.
44 __RCSID("$NetBSD: sshlogin.c,v 1.18 2008/04/06 23:38:20 christos Exp $");
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <sys/socket.h>
69 extern Buffer loginmsg
;
70 extern ServerOptions options
;
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.
78 get_last_login_time(uid_t uid
, const char *logname
,
79 char *buf
, size_t bufsize
)
82 struct lastlogx llx
, *llxp
;
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);
97 return llxp
->ll_tv
.tv_sec
;
101 fd
= open(_PATH_LASTLOG
, O_RDONLY
);
105 pos
= (long) uid
* sizeof(ll
);
106 r
= lseek(fd
, pos
, SEEK_SET
);
108 error("%s: lseek: %s", __func__
, strerror(errno
));
112 debug("%s: truncated lastlog", __func__
);
115 if (read(fd
, &ll
, sizeof(ll
)) != sizeof(ll
)) {
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
;
131 * Generate and store last login message. This must be done before
132 * login_login() is called and lastlog is updated.
135 store_lastlog_message(const char *user
, uid_t uid
)
137 char *time_string
, hostname
[MAXHOSTNAMELEN
] = "", buf
[512];
138 time_t last_login_time
;
140 if (!options
.print_lastlog
)
143 last_login_time
= get_last_login_time(uid
, user
, hostname
,
146 if (last_login_time
!= 0) {
147 time_string
= ctime(&last_login_time
);
148 time_string
[strcspn(time_string
, "\n")] = '\0';
149 if (strcmp(hostname
, "") == 0)
150 snprintf(buf
, sizeof(buf
), "Last login: %s\r\n",
153 snprintf(buf
, sizeof(buf
), "Last login: %s from %s\r\n",
154 time_string
, hostname
);
155 buffer_append(&loginmsg
, buf
, strlen(buf
));
160 * Records that the user has logged in. I wish these parts of operating
161 * systems were more standardized.
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
)
167 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
176 struct utmpx ux
, *uxp
= &ux
;
179 (void)gettimeofday(&tv
, NULL
);
181 * XXX: why do we need to handle logout cases here?
182 * Isn't the function below taking care of this?
184 /* save previous login details before writing new */
185 store_lastlog_message(user
, uid
);
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
));
197 /* Update lastlog unless actually recording a logout. */
200 * It is safer to bzero the lastlog structure first because
201 * some systems might have some extra fields in it (e.g. SGI)
203 memset(&ll
, 0, sizeof(ll
));
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
);
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
));
219 /* Construct an utmpx/wtmpx entry. */
220 memset(&ux
, 0, sizeof(ux
));
221 strncpy(ux
.ut_line
, tty
+ 5, sizeof(ux
.ut_line
));
224 ux
.ut_type
= USER_PROCESS
;
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
)) {
231 tty
+ strlen(tty
) - sizeof(ux
.ut_id
),
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",
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
));
251 if ((uxp
= getutxline(&ux
)) == NULL
)
252 logit("could not find utmpx line for %.100s", tty
);
254 uxp
->ut_type
= DEAD_PROCESS
;
256 /* XXX: we don't record exit info yet */
257 if (pututxline(&ux
) == NULL
)
258 logit("could not replace utmpx line: %.100s",
263 updwtmpx(_PATH_WTMPX
, uxp
);
267 /* Records that the user has logged out. */
269 record_logout(pid_t pid
, const char *tty
)
271 #if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX)
272 const char *line
= tty
+ 5; /* /dev/ttyq8 -> ttyq8 */
276 logwtmp(line
, "", "");
279 /* XXX: no exit info yet */
280 if (logoutx(line
, 0, DEAD_PROCESS
))
281 logwtmpx(line
, "", "", 0, DEAD_PROCESS
);