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