1 /* $OpenBSD: sshlogin.c,v 1.26 2007/09/11 15:47:17 gilles Exp $ */
2 /*
3  * Author: Tatu Ylonen <ylo@cs.hut.fi>
4  * Copyright © 2013
5  *	Thorsten “mirabilos” Glaser <tg@mirbsd.org>
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 <sys/param.h>
45 #include <sys/socket.h>
46 
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <util.h>
54 #include <utmp.h>
55 #include <stdarg.h>
56 
57 #include "sshlogin.h"
58 #include "log.h"
59 #include "buffer.h"
60 #include "servconf.h"
61 
62 __RCSID("$MirOS: src/usr.bin/ssh/sshlogin.c,v 1.7 2013/10/31 20:07:15 tg Exp $");
63 
64 extern Buffer loginmsg;
65 extern ServerOptions options;
66 
67 #ifdef SMALL
68 #define usmall	__attribute__((__unused__))
69 #else
70 #define usmall	/* nothing */
71 static time_t get_last_login_time(uid_t, const char *, char *, size_t);
72 
73 /*
74  * Returns the time when the user last logged in.  Returns 0 if the
75  * information is not available.  This must be called before record_login.
76  * The host the user logged in from will be returned in buf.
77  */
78 static time_t
get_last_login_time(uid_t uid,const char * logname,char * buf,size_t bufsize)79 get_last_login_time(uid_t uid, const char *logname __attribute__((__unused__)),
80     char *buf, size_t bufsize)
81 {
82 	struct lastlog ll;
83 	const char *lastlog;
84 	int fd;
85 	off_t pos, r;
86 
87 	lastlog = _PATH_LASTLOG;
88 	buf[0] = '\0';
89 
90 	fd = open(lastlog, O_RDONLY);
91 	if (fd < 0)
92 		return 0;
93 
94 	pos = (long) uid * sizeof(ll);
95 	r = lseek(fd, pos, SEEK_SET);
96 	if (r == -1) {
97 		error("%s: lseek: %s", __func__, strerror(errno));
98 		return (0);
99 	}
100 	if (r != pos) {
101 		debug("%s: truncated lastlog", __func__);
102 		return (0);
103 	}
104 	if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) {
105 		close(fd);
106 		return 0;
107 	}
108 	close(fd);
109 	if (bufsize > sizeof(ll.ll_host) + 1)
110 		bufsize = sizeof(ll.ll_host) + 1;
111 	strncpy(buf, ll.ll_host, bufsize - 1);
112 	buf[bufsize - 1] = '\0';
113 	return (time_t)ll.ll_time;
114 }
115 
116 /*
117  * Generate and store last login message.  This must be done before
118  * login_login() is called and lastlog is updated.
119  */
120 static void
store_lastlog_message(const char * user,uid_t uid)121 store_lastlog_message(const char *user, uid_t uid)
122 {
123 	char *time_string, hostname[MAXHOSTNAMELEN] = "", buf[512];
124 	time_t last_login_time;
125 
126 	if (!options.print_lastlog)
127 		return;
128 
129 	last_login_time = get_last_login_time(uid, user, hostname,
130 	    sizeof(hostname));
131 
132 	if (last_login_time != 0) {
133 		time_string = ctime(&last_login_time);
134 		time_string[strcspn(time_string, "\n")] = '\0';
135 		if (strcmp(hostname, "") == 0)
136 			snprintf(buf, sizeof(buf), "Last login: %s\r\n",
137 			    time_string);
138 		else
139 			snprintf(buf, sizeof(buf), "Last login: %s from %s\r\n",
140 			    time_string, hostname);
141 		buffer_append(&loginmsg, buf, strlen(buf));
142 	}
143 }
144 #endif
145 
146 /*
147  * Records that the user has logged in.  I wish these parts of operating
148  * systems were more standardized.
149  */
150 void
record_login(pid_t pid,const char * tty usmall,const char * user usmall,uid_t uid usmall,const char * host usmall,struct sockaddr * addr,socklen_t addrlen)151 record_login(pid_t pid __attribute__((__unused__)), const char *tty usmall,
152     const char *user usmall, uid_t uid usmall, const char *host usmall,
153     struct sockaddr *addr __attribute__((__unused__)),
154     socklen_t addrlen __attribute__((__unused__)))
155 {
156 #ifndef SMALL
157 	int fd;
158 	struct lastlog ll;
159 	const char *lastlog;
160 	struct utmp u;
161 
162 	/* save previous login details before writing new */
163 	store_lastlog_message(user, uid);
164 
165 	/* Construct an utmp/wtmp entry. */
166 	memset(&u, 0, sizeof(u));
167 	strncpy(u.ut_line, tty + 5, sizeof(u.ut_line));
168 	u.ut_time = time(NULL);
169 	strncpy(u.ut_name, user, sizeof(u.ut_name));
170 	strncpy(u.ut_host, host, sizeof(u.ut_host));
171 
172 	login(&u);
173 	lastlog = _PATH_LASTLOG;
174 
175 	/* Update lastlog unless actually recording a logout. */
176 	if (strcmp(user, "") != 0) {
177 		/*
178 		 * It is safer to bzero the lastlog structure first because
179 		 * some systems might have some extra fields in it (e.g. SGI)
180 		 */
181 		memset(&ll, 0, sizeof(ll));
182 
183 		/* Update lastlog. */
184 		ll.ll_time = time(NULL);
185 		strncpy(ll.ll_line, tty + 5, sizeof(ll.ll_line));
186 		strncpy(ll.ll_host, host, sizeof(ll.ll_host));
187 		fd = open(lastlog, O_RDWR);
188 		if (fd >= 0) {
189 			lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET);
190 			if (write(fd, &ll, sizeof(ll)) != sizeof(ll))
191 				logit("Could not write %.100s: %.100s", lastlog, strerror(errno));
192 			close(fd);
193 		}
194 	}
195 #endif
196 }
197 
198 /* Records that the user has logged out. */
199 void
record_logout(pid_t pid,const char * tty usmall)200 record_logout(pid_t pid __attribute__((__unused__)), const char *tty usmall)
201 {
202 #ifndef SMALL
203 	const char *line = tty + 5;	/* /dev/ttyq8 -> ttyq8 */
204 	if (logout(line))
205 		logwtmp(line, "", "");
206 #endif
207 }
208