1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD: stable/12/lib/libc/gen/pututxline.c 326193 2017-11-25 17:12:48Z pfg $");
31
32 #include "namespace.h"
33 #include <sys/endian.h>
34 #include <sys/stat.h>
35 #include <sys/uio.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <utmpx.h>
42 #include "utxdb.h"
43 #include "un-namespace.h"
44
45 static FILE *
futx_open(const char * file)46 futx_open(const char *file)
47 {
48 FILE *fp;
49 struct stat sb;
50 int fd;
51
52 fd = _open(file, O_CREAT|O_RDWR|O_EXLOCK|O_CLOEXEC, 0644);
53 if (fd < 0)
54 return (NULL);
55
56 /* Safety check: never use broken files. */
57 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0) {
58 _close(fd);
59 errno = EFTYPE;
60 return (NULL);
61 }
62
63 fp = fdopen(fd, "r+");
64 if (fp == NULL) {
65 _close(fd);
66 return (NULL);
67 }
68 return (fp);
69 }
70
71 static int
utx_active_add(const struct futx * fu)72 utx_active_add(const struct futx *fu)
73 {
74 FILE *fp;
75 struct futx fe;
76 off_t partial;
77 int error, ret;
78
79 partial = -1;
80 ret = 0;
81
82 /*
83 * Register user login sessions. Overwrite entries of sessions
84 * that have already been terminated.
85 */
86 fp = futx_open(_PATH_UTX_ACTIVE);
87 if (fp == NULL)
88 return (-1);
89 while (fread(&fe, sizeof(fe), 1, fp) == 1) {
90 switch (fe.fu_type) {
91 case BOOT_TIME:
92 /* Leave these intact. */
93 break;
94 case USER_PROCESS:
95 case INIT_PROCESS:
96 case LOGIN_PROCESS:
97 case DEAD_PROCESS:
98 /* Overwrite when ut_id matches. */
99 if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) ==
100 0) {
101 ret = fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR);
102 goto exact;
103 }
104 if (fe.fu_type != DEAD_PROCESS)
105 break;
106 /* FALLTHROUGH */
107 default:
108 /* Allow us to overwrite unused records. */
109 if (partial == -1) {
110 partial = ftello(fp);
111 /*
112 * Distinguish errors from valid values so we
113 * don't overwrite good data by accident.
114 */
115 if (partial != -1)
116 partial -= (off_t)sizeof(fe);
117 }
118 break;
119 }
120 }
121
122 /*
123 * No exact match found. Use the partial match. If no partial
124 * match was found, just append a new record.
125 */
126 if (partial != -1)
127 ret = fseeko(fp, partial, SEEK_SET);
128 exact:
129 if (ret == -1)
130 error = errno;
131 else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
132 error = errno;
133 else
134 error = 0;
135 fclose(fp);
136 if (error != 0)
137 errno = error;
138 return (error == 0 ? 0 : 1);
139 }
140
141 static int
utx_active_remove(struct futx * fu)142 utx_active_remove(struct futx *fu)
143 {
144 FILE *fp;
145 struct futx fe;
146 int error, ret;
147
148 /*
149 * Remove user login sessions, having the same ut_id.
150 */
151 fp = futx_open(_PATH_UTX_ACTIVE);
152 if (fp == NULL)
153 return (-1);
154 error = ESRCH;
155 ret = -1;
156 while (fread(&fe, sizeof(fe), 1, fp) == 1 && ret != 0)
157 switch (fe.fu_type) {
158 case USER_PROCESS:
159 case INIT_PROCESS:
160 case LOGIN_PROCESS:
161 if (memcmp(fu->fu_id, fe.fu_id, sizeof(fe.fu_id)) != 0)
162 continue;
163
164 /* Terminate session. */
165 if (fseeko(fp, -(off_t)sizeof(fe), SEEK_CUR) == -1)
166 error = errno;
167 else if (fwrite(fu, sizeof(*fu), 1, fp) < 1)
168 error = errno;
169 else
170 ret = 0;
171
172 }
173
174 fclose(fp);
175 if (ret != 0)
176 errno = error;
177 return (ret);
178 }
179
180 static void
utx_active_init(const struct futx * fu)181 utx_active_init(const struct futx *fu)
182 {
183 int fd;
184
185 /* Initialize utx.active with a single BOOT_TIME record. */
186 fd = _open(_PATH_UTX_ACTIVE, O_CREAT|O_RDWR|O_TRUNC, 0644);
187 if (fd < 0)
188 return;
189 _write(fd, fu, sizeof(*fu));
190 _close(fd);
191 }
192
193 static void
utx_active_purge(void)194 utx_active_purge(void)
195 {
196
197 truncate(_PATH_UTX_ACTIVE, 0);
198 }
199
200 static int
utx_lastlogin_add(const struct futx * fu)201 utx_lastlogin_add(const struct futx *fu)
202 {
203 struct futx fe;
204 FILE *fp;
205 int error, ret;
206
207 ret = 0;
208
209 /*
210 * Write an entry to lastlogin. Overwrite the entry if the
211 * current user already has an entry. If not, append a new
212 * entry.
213 */
214 fp = futx_open(_PATH_UTX_LASTLOGIN);
215 if (fp == NULL)
216 return (-1);
217 while (fread(&fe, sizeof fe, 1, fp) == 1) {
218 if (strncmp(fu->fu_user, fe.fu_user, sizeof fe.fu_user) != 0)
219 continue;
220
221 /* Found a previous lastlogin entry for this user. */
222 ret = fseeko(fp, -(off_t)sizeof fe, SEEK_CUR);
223 break;
224 }
225 if (ret == -1)
226 error = errno;
227 else if (fwrite(fu, sizeof *fu, 1, fp) < 1) {
228 error = errno;
229 ret = -1;
230 }
231 fclose(fp);
232 if (ret == -1)
233 errno = error;
234 return (ret);
235 }
236
237 static void
utx_lastlogin_upgrade(void)238 utx_lastlogin_upgrade(void)
239 {
240 struct stat sb;
241 int fd;
242
243 fd = _open(_PATH_UTX_LASTLOGIN, O_RDWR|O_CLOEXEC, 0644);
244 if (fd < 0)
245 return;
246
247 /*
248 * Truncate broken lastlogin files. In the future we should
249 * check for older versions of the file format here and try to
250 * upgrade it.
251 */
252 if (_fstat(fd, &sb) != -1 && sb.st_size % sizeof(struct futx) != 0)
253 ftruncate(fd, 0);
254 _close(fd);
255 }
256
257 static int
utx_log_add(const struct futx * fu)258 utx_log_add(const struct futx *fu)
259 {
260 struct iovec vec[2];
261 int error, fd;
262 uint16_t l;
263
264 /*
265 * Append an entry to the log file. We only need to append
266 * records to this file, so to conserve space, trim any trailing
267 * zero-bytes. Prepend a length field, indicating the length of
268 * the record, excluding the length field itself.
269 */
270 for (l = sizeof(*fu); l > 0 && ((const char *)fu)[l - 1] == '\0'; l--) ;
271 vec[0].iov_base = &l;
272 vec[0].iov_len = sizeof(l);
273 vec[1].iov_base = __DECONST(void *, fu);
274 vec[1].iov_len = l;
275 l = htobe16(l);
276
277 fd = _open(_PATH_UTX_LOG, O_CREAT|O_WRONLY|O_APPEND|O_CLOEXEC, 0644);
278 if (fd < 0)
279 return (-1);
280 if (_writev(fd, vec, 2) == -1)
281 error = errno;
282 else
283 error = 0;
284 _close(fd);
285 if (error != 0)
286 errno = error;
287 return (error == 0 ? 0 : 1);
288 }
289
290 struct utmpx *
pututxline(const struct utmpx * utmpx)291 pututxline(const struct utmpx *utmpx)
292 {
293 struct futx fu;
294 int bad;
295
296 bad = 0;
297
298 utx_to_futx(utmpx, &fu);
299
300 switch (fu.fu_type) {
301 case BOOT_TIME:
302 utx_active_init(&fu);
303 utx_lastlogin_upgrade();
304 break;
305 case SHUTDOWN_TIME:
306 utx_active_purge();
307 break;
308 case OLD_TIME:
309 case NEW_TIME:
310 break;
311 case USER_PROCESS:
312 bad |= utx_active_add(&fu);
313 bad |= utx_lastlogin_add(&fu);
314 break;
315 #if 0 /* XXX: Are these records of any use to us? */
316 case INIT_PROCESS:
317 case LOGIN_PROCESS:
318 bad |= utx_active_add(&fu);
319 break;
320 #endif
321 case DEAD_PROCESS:
322 /*
323 * In case writing a logout entry fails, never attempt
324 * to write it to utx.log. The logout entry's ut_id
325 * might be invalid.
326 */
327 if (utx_active_remove(&fu) != 0)
328 return (NULL);
329 break;
330 default:
331 errno = EINVAL;
332 return (NULL);
333 }
334
335 bad |= utx_log_add(&fu);
336 return (bad ? NULL : futx_to_utx(&fu));
337 }
338