Coverage Report

Created: 2026-01-09 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proftpd/src/wtmp.c
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
}