1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2009-2016 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, The ProFTPD Project and other respective copyright
20 * holders give permission to link this program with OpenSSL, and distribute
21 * the resulting executable, without including the source code for OpenSSL in
22 * the source distribution.
23 */
24
25 #include "conf.h"
26
27 /* This next function logs an entry to wtmp, it MUST be called as root BEFORE
28 * a chroot occurs. Note: This has some portability ifdefs in it. They
29 * should work, but I haven't been able to test them.
30 */
31
log_wtmp(const char * line,const char * name,const char * host,const pr_netaddr_t * ip)32 int log_wtmp(const char *line, const char *name, const char *host,
33 const pr_netaddr_t *ip) {
34 struct stat buf;
35 int res = 0;
36
37 #if ((defined(SVR4) || defined(__SVR4)) || \
38 (defined(__NetBSD__) && defined(HAVE_UTMPX_H)) || \
39 (defined(__DragonFly__) && defined(HAVE_UTMPX_H)) || \
40 (defined(__FreeBSD_version) && __FreeBSD_version >= 900007 && defined(HAVE_UTMPX_H))) && \
41 !(defined(LINUX) || defined(__hpux) || defined (_AIX))
42 /* This "auxiliary" utmp doesn't exist under linux. */
43
44 #if (defined(__sparcv9) || defined(__sun)) && !defined(__NetBSD__) && !defined(__FreeBSD__)
45 struct futmpx utx;
46 time_t t;
47
48 #else
49 struct utmpx utx;
50 #endif
51
52 static int fdx = -1;
53
54 #if !defined(WTMPX_FILE)
55 # if defined(_PATH_WTMPX)
56 # define WTMPX_FILE _PATH_WTMPX
57 # elif defined(_PATH_UTMPX)
58 # define WTMPX_FILE _PATH_UTMPX
59 # else
60 /* This path works for FreeBSD; not sure what to do for other platforms which
61 * don't define _PATH_WTMPX or _PATH_UTMPX.
62 */
63 # define WTMPX_FILE "/var/log/utx.log"
64 # endif
65 #endif
66
67 if (fdx < 0 &&
68 (fdx = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
69 int xerrno = errno;
70
71 pr_log_pri(PR_LOG_WARNING, "failed to open wtmpx %s: %s", WTMPX_FILE,
72 strerror(xerrno));
73
74 errno = xerrno;
75 return -1;
76 }
77
78 (void) pr_fs_get_usable_fd2(&fdx);
79
80 /* Unfortunately, utmp string fields are terminated by '\0' if they are
81 * shorter than the size of the field, but if they are exactly the size of
82 * the field they don't have to be terminated at all. Frankly, this sucks.
83 * Insane if you ask me. Unless there's massive uproar, I prefer to err on
84 * the side of caution and always null-terminate our strings.
85 */
86 if (fstat(fdx, &buf) == 0) {
87 memset(&utx, 0, sizeof(utx));
88
89 sstrncpy(utx.ut_user, name, sizeof(utx.ut_user));
90 sstrncpy(utx.ut_id, pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT),
91 sizeof(utx.ut_user));
92 sstrncpy(utx.ut_line, line, sizeof(utx.ut_line));
93 sstrncpy(utx.ut_host, host, sizeof(utx.ut_host));
94 utx.ut_pid = session.pid ? session.pid : getpid();
95
96 #if defined(__NetBSD__) && defined(HAVE_UTMPX_H)
97 memcpy(&utx.ut_ss, pr_netaddr_get_inaddr(ip), sizeof(utx.ut_ss));
98 gettimeofday(&utx.ut_tv, NULL);
99
100 #elif defined(__DragonFly__) && defined(HAVE_UTMPX_H)
101 gettimeofday(&utx.ut_tv, NULL);
102
103 #elif defined(__FreeBSD_version) && __FreeBSD_version >= 900007 && defined(HAVE_UTMPX_H)
104 gettimeofday(&utx.ut_tv, NULL);
105
106 #else /* SVR4 */
107 utx.ut_syslen = strlen(utx.ut_host)+1;
108
109 # if (defined(__sparcv9) || defined(__sun)) && !defined(__FreeBSD__)
110 time(&t);
111 utx.ut_tv.tv_sec = (time32_t)t;
112 # else
113 time(&utx.ut_tv.tv_sec);
114 # endif
115
116 #endif /* SVR4 */
117
118 if (*name)
119 utx.ut_type = USER_PROCESS;
120 else
121 utx.ut_type = DEAD_PROCESS;
122
123 #ifdef HAVE_UT_UT_EXIT
124 utx.ut_exit.e_termination = 0;
125 utx.ut_exit.e_exit = 0;
126 #endif /* HAVE_UT_UT_EXIT */
127
128 if (write(fdx, (char *) &utx, sizeof(utx)) != sizeof(utx)) {
129 (void) ftruncate(fdx, buf.st_size);
130 }
131
132 } else {
133 pr_log_debug(DEBUG0, "%s fstat(): %s", WTMPX_FILE, strerror(errno));
134 res = -1;
135 }
136
137 #else /* Non-SVR4 systems */
138 struct utmp ut;
139 static int fd = -1;
140
141 if (fd < 0 &&
142 (fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) {
143 int xerrno = errno;
144
145 pr_log_pri(PR_LOG_WARNING, "failed to open wtmp %s: %s", WTMP_FILE,
146 strerror(xerrno));
147
148 errno = xerrno;
149 return -1;
150 }
151
152 (void) pr_fs_get_usable_fd2(&fd);
153
154 if (fstat(fd, &buf) == 0) {
155 memset(&ut, 0, sizeof(ut));
156
157 #ifdef HAVE_UTMAXTYPE
158
159 # ifdef LINUX
160 if (ip)
161 # ifndef PR_USE_IPV6
162 memcpy(&ut.ut_addr, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr));
163 # else
164 memcpy(&ut.ut_addr_v6, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr_v6));
165 # endif /* !PR_USE_IPV6 */
166
167 # else
168 sstrncpy(ut.ut_id, pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT),
169 sizeof(ut.ut_id));
170
171 # ifdef HAVE_UT_UT_EXIT
172 ut.ut_exit.e_termination = 0;
173 ut.ut_exit.e_exit = 0;
174 # endif /* !HAVE_UT_UT_EXIT */
175
176 # endif /* !LINUX */
177 sstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
178
179 if (name && *name)
180 sstrncpy(ut.ut_user, name, sizeof(ut.ut_user));
181
182 ut.ut_pid = session.pid ? session.pid : getpid();
183
184 if (name && *name)
185 ut.ut_type = USER_PROCESS;
186 else
187 ut.ut_type = DEAD_PROCESS;
188
189 #else /* !HAVE_UTMAXTYPE */
190 sstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
191
192 if (name && *name) {
193 sstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
194 }
195 #endif /* HAVE_UTMAXTYPE */
196
197 #ifdef HAVE_UT_UT_HOST
198 if (host && *host) {
199 sstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
200 }
201 #endif /* HAVE_UT_UT_HOST */
202
203 ut.ut_time = time(NULL);
204
205 if (write(fd, (char *) &ut, sizeof(ut)) != sizeof(ut)) {
206 if (ftruncate(fd, buf.st_size) < 0) {
207 pr_log_debug(DEBUG0, "error truncating '%s': %s", WTMP_FILE,
208 strerror(errno));
209 }
210 }
211
212 } else {
213 pr_log_debug(DEBUG0, "%s fstat(): %s", WTMP_FILE, strerror(errno));
214 res = -1;
215 }
216 #endif /* SVR4 */
217
218 return res;
219 }
220