Line | Count | Source |
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 | | |
32 | | int log_wtmp(const char *line, const char *name, const char *host, |
33 | 0 | const pr_netaddr_t *ip) { |
34 | 0 | struct stat buf; |
35 | 0 | int res = 0; |
36 | |
|
37 | | #if ((defined(SVR4) || defined(__SVR4)) || \ |
38 | | (defined(__NetBSD__) && defined(HAVE_UTMPX_H)) || \ |
39 | | (defined(__FreeBSD_version) && __FreeBSD_version >= 900007 && defined(HAVE_UTMPX_H))) && \ |
40 | | !(defined(LINUX) || defined(__hpux) || defined (_AIX)) |
41 | | /* This "auxiliary" utmp doesn't exist under linux. */ |
42 | | |
43 | | #if (defined(__sparcv9) || defined(__sun)) && !defined(__NetBSD__) && !defined(__FreeBSD__) |
44 | | struct futmpx utx; |
45 | | time_t t; |
46 | | |
47 | | #else |
48 | | struct utmpx utx; |
49 | | #endif |
50 | | |
51 | | static int fdx = -1; |
52 | | |
53 | | #if !defined(WTMPX_FILE) |
54 | | # if defined(_PATH_WTMPX) |
55 | | # define WTMPX_FILE _PATH_WTMPX |
56 | | # elif defined(_PATH_UTMPX) |
57 | | # define WTMPX_FILE _PATH_UTMPX |
58 | | # else |
59 | | /* This path works for FreeBSD; not sure what to do for other platforms which |
60 | | * don't define _PATH_WTMPX or _PATH_UTMPX. |
61 | | */ |
62 | | # define WTMPX_FILE "/var/log/utx.log" |
63 | | # endif |
64 | | #endif |
65 | | |
66 | | if (fdx < 0 && |
67 | | (fdx = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { |
68 | | int xerrno = errno; |
69 | | |
70 | | pr_log_pri(PR_LOG_WARNING, "failed to open wtmpx %s: %s", WTMPX_FILE, |
71 | | strerror(xerrno)); |
72 | | |
73 | | errno = xerrno; |
74 | | return -1; |
75 | | } |
76 | | |
77 | | (void) pr_fs_get_usable_fd2(&fdx); |
78 | | |
79 | | /* Unfortunately, utmp string fields are terminated by '\0' if they are |
80 | | * shorter than the size of the field, but if they are exactly the size of |
81 | | * the field they don't have to be terminated at all. Frankly, this sucks. |
82 | | * Insane if you ask me. Unless there's massive uproar, I prefer to err on |
83 | | * the side of caution and always null-terminate our strings. |
84 | | */ |
85 | | if (fstat(fdx, &buf) == 0) { |
86 | | memset(&utx, 0, sizeof(utx)); |
87 | | |
88 | | sstrncpy(utx.ut_user, name, sizeof(utx.ut_user)); |
89 | | sstrncpy(utx.ut_id, pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT), |
90 | | sizeof(utx.ut_user)); |
91 | | sstrncpy(utx.ut_line, line, sizeof(utx.ut_line)); |
92 | | sstrncpy(utx.ut_host, host, sizeof(utx.ut_host)); |
93 | | utx.ut_pid = session.pid ? session.pid : getpid(); |
94 | | |
95 | | #if defined(__NetBSD__) && defined(HAVE_UTMPX_H) |
96 | | memcpy(&utx.ut_ss, pr_netaddr_get_inaddr(ip), sizeof(utx.ut_ss)); |
97 | | gettimeofday(&utx.ut_tv, NULL); |
98 | | |
99 | | #elif defined(__FreeBSD_version) && __FreeBSD_version >= 900007 && defined(HAVE_UTMPX_H) |
100 | | gettimeofday(&utx.ut_tv, NULL); |
101 | | |
102 | | #else /* SVR4 */ |
103 | | utx.ut_syslen = strlen(utx.ut_host)+1; |
104 | | |
105 | | # if (defined(__sparcv9) || defined(__sun)) && !defined(__FreeBSD__) |
106 | | time(&t); |
107 | | utx.ut_tv.tv_sec = (time32_t)t; |
108 | | # else |
109 | | time(&utx.ut_tv.tv_sec); |
110 | | # endif |
111 | | |
112 | | #endif /* SVR4 */ |
113 | | |
114 | | if (*name) |
115 | | utx.ut_type = USER_PROCESS; |
116 | | else |
117 | | utx.ut_type = DEAD_PROCESS; |
118 | | |
119 | | #ifdef HAVE_UT_UT_EXIT |
120 | | utx.ut_exit.e_termination = 0; |
121 | | utx.ut_exit.e_exit = 0; |
122 | | #endif /* HAVE_UT_UT_EXIT */ |
123 | | |
124 | | if (write(fdx, (char *) &utx, sizeof(utx)) != sizeof(utx)) { |
125 | | (void) ftruncate(fdx, buf.st_size); |
126 | | } |
127 | | |
128 | | } else { |
129 | | pr_log_debug(DEBUG0, "%s fstat(): %s", WTMPX_FILE, strerror(errno)); |
130 | | res = -1; |
131 | | } |
132 | | |
133 | | #else /* Non-SVR4 systems */ |
134 | 0 | struct utmp ut; |
135 | 0 | static int fd = -1; |
136 | |
|
137 | 0 | if (fd < 0 && |
138 | 0 | (fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { |
139 | 0 | int xerrno = errno; |
140 | |
|
141 | 0 | pr_log_pri(PR_LOG_WARNING, "failed to open wtmp %s: %s", WTMP_FILE, |
142 | 0 | strerror(xerrno)); |
143 | |
|
144 | 0 | errno = xerrno; |
145 | 0 | return -1; |
146 | 0 | } |
147 | | |
148 | 0 | (void) pr_fs_get_usable_fd2(&fd); |
149 | |
|
150 | 0 | if (fstat(fd, &buf) == 0) { |
151 | 0 | memset(&ut, 0, sizeof(ut)); |
152 | |
|
153 | | #ifdef HAVE_UTMAXTYPE |
154 | | |
155 | | # ifdef LINUX |
156 | | if (ip) |
157 | | # ifndef PR_USE_IPV6 |
158 | | memcpy(&ut.ut_addr, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr)); |
159 | | # else |
160 | | memcpy(&ut.ut_addr_v6, pr_netaddr_get_inaddr(ip), sizeof(ut.ut_addr_v6)); |
161 | | # endif /* !PR_USE_IPV6 */ |
162 | | |
163 | | # else |
164 | | sstrncpy(ut.ut_id, pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT), |
165 | | sizeof(ut.ut_id)); |
166 | | |
167 | | # ifdef HAVE_UT_UT_EXIT |
168 | | ut.ut_exit.e_termination = 0; |
169 | | ut.ut_exit.e_exit = 0; |
170 | | # endif /* !HAVE_UT_UT_EXIT */ |
171 | | |
172 | | # endif /* !LINUX */ |
173 | | sstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); |
174 | | |
175 | | if (name && *name) |
176 | | sstrncpy(ut.ut_user, name, sizeof(ut.ut_user)); |
177 | | |
178 | | ut.ut_pid = session.pid ? session.pid : getpid(); |
179 | | |
180 | | if (name && *name) |
181 | | ut.ut_type = USER_PROCESS; |
182 | | else |
183 | | ut.ut_type = DEAD_PROCESS; |
184 | | |
185 | | #else /* !HAVE_UTMAXTYPE */ |
186 | 0 | sstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); |
187 | |
|
188 | 0 | if (name && *name) { |
189 | 0 | sstrncpy(ut.ut_name, name, sizeof(ut.ut_name)); |
190 | 0 | } |
191 | 0 | #endif /* HAVE_UTMAXTYPE */ |
192 | |
|
193 | 0 | #ifdef HAVE_UT_UT_HOST |
194 | 0 | if (host && *host) { |
195 | 0 | sstrncpy(ut.ut_host, host, sizeof(ut.ut_host)); |
196 | 0 | } |
197 | 0 | #endif /* HAVE_UT_UT_HOST */ |
198 | |
|
199 | 0 | ut.ut_time = time(NULL); |
200 | |
|
201 | 0 | if (write(fd, (char *) &ut, sizeof(ut)) != sizeof(ut)) { |
202 | 0 | if (ftruncate(fd, buf.st_size) < 0) { |
203 | 0 | pr_log_debug(DEBUG0, "error truncating '%s': %s", WTMP_FILE, |
204 | 0 | strerror(errno)); |
205 | 0 | } |
206 | 0 | } |
207 | |
|
208 | 0 | } else { |
209 | 0 | pr_log_debug(DEBUG0, "%s fstat(): %s", WTMP_FILE, strerror(errno)); |
210 | 0 | res = -1; |
211 | 0 | } |
212 | 0 | #endif /* SVR4 */ |
213 | |
|
214 | 0 | return res; |
215 | 0 | } |