1 /* $NetBSD: utmpx.c,v 1.35 2015/05/23 11:48:13 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christos Zoulas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 #include <sys/cdefs.h> 32 33 #if defined(LIBC_SCCS) && !defined(lint) 34 __RCSID("$NetBSD: utmpx.c,v 1.35 2015/05/23 11:48:13 christos Exp $"); 35 #endif /* LIBC_SCCS and not lint */ 36 37 #include "namespace.h" 38 #include <sys/types.h> 39 #include <sys/param.h> 40 #include <sys/socket.h> 41 #include <sys/stat.h> 42 #include <sys/time.h> 43 #include <sys/wait.h> 44 45 #include <assert.h> 46 #include <db.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <utmp.h> 54 #include <utmpx.h> 55 #include <vis.h> 56 57 static FILE *fp; 58 static int readonly = 0; 59 static int version = 1; 60 static struct utmpx ut; 61 static char utfile[MAXPATHLEN] = _PATH_UTMPX; 62 63 static struct utmpx *utmp_update(const struct utmpx *); 64 65 static const char vers[] = "utmpx-2.00"; 66 67 struct otimeval { 68 long tv_sec; 69 long tv_usec; 70 }; 71 72 static void 73 old2new(struct utmpx *utx) 74 { 75 struct otimeval otv; 76 struct timeval *tv = &utx->ut_tv; 77 (void)memcpy(&otv, tv, sizeof(otv)); 78 tv->tv_sec = otv.tv_sec; 79 tv->tv_usec = (suseconds_t)otv.tv_usec; 80 } 81 82 static void 83 new2old(struct utmpx *utx) 84 { 85 struct otimeval otv; 86 struct timeval *tv = &utx->ut_tv; 87 88 otv.tv_sec = (long)tv->tv_sec; 89 otv.tv_usec = (long)tv->tv_usec; 90 (void)memcpy(tv, &otv, sizeof(otv)); 91 } 92 93 void 94 setutxent(void) 95 { 96 97 (void)memset(&ut, 0, sizeof(ut)); 98 if (fp == NULL) 99 return; 100 (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); 101 } 102 103 104 void 105 endutxent(void) 106 { 107 108 (void)memset(&ut, 0, sizeof(ut)); 109 if (fp != NULL) { 110 (void)fclose(fp); 111 fp = NULL; 112 readonly = 0; 113 } 114 } 115 116 117 struct utmpx * 118 getutxent(void) 119 { 120 121 if (fp == NULL) { 122 struct stat st; 123 124 if ((fp = fopen(utfile, "re+")) == NULL) 125 if ((fp = fopen(utfile, "we+")) == NULL) { 126 if ((fp = fopen(utfile, "re")) == NULL) 127 goto fail; 128 else 129 readonly = 1; 130 } 131 132 133 /* get file size in order to check if new file */ 134 if (fstat(fileno(fp), &st) == -1) 135 goto failclose; 136 137 if (st.st_size == 0) { 138 /* new file, add signature record */ 139 (void)memset(&ut, 0, sizeof(ut)); 140 ut.ut_type = SIGNATURE; 141 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 142 if (fwrite(&ut, sizeof(ut), 1, fp) != 1) 143 goto failclose; 144 } else { 145 /* old file, read signature record */ 146 if (fread(&ut, sizeof(ut), 1, fp) != 1) 147 goto failclose; 148 if (memcmp(ut.ut_user, vers, 5) != 0 || 149 ut.ut_type != SIGNATURE) 150 goto failclose; 151 } 152 version = ut.ut_user[6] - '0'; 153 } 154 155 if (fread(&ut, sizeof(ut), 1, fp) != 1) 156 goto fail; 157 if (version == 1) 158 old2new(&ut); 159 160 return &ut; 161 failclose: 162 (void)fclose(fp); 163 fail: 164 (void)memset(&ut, 0, sizeof(ut)); 165 return NULL; 166 } 167 168 169 struct utmpx * 170 getutxid(const struct utmpx *utx) 171 { 172 173 _DIAGASSERT(utx != NULL); 174 175 if (utx->ut_type == EMPTY) 176 return NULL; 177 178 do { 179 if (ut.ut_type == EMPTY) 180 continue; 181 switch (utx->ut_type) { 182 case EMPTY: 183 return NULL; 184 case RUN_LVL: 185 case BOOT_TIME: 186 case OLD_TIME: 187 case NEW_TIME: 188 if (ut.ut_type == utx->ut_type) 189 return &ut; 190 break; 191 case INIT_PROCESS: 192 case LOGIN_PROCESS: 193 case USER_PROCESS: 194 case DEAD_PROCESS: 195 switch (ut.ut_type) { 196 case INIT_PROCESS: 197 case LOGIN_PROCESS: 198 case USER_PROCESS: 199 case DEAD_PROCESS: 200 if (memcmp(ut.ut_id, utx->ut_id, 201 sizeof(ut.ut_id)) == 0) 202 return &ut; 203 break; 204 default: 205 break; 206 } 207 break; 208 default: 209 return NULL; 210 } 211 } while (getutxent() != NULL); 212 return NULL; 213 } 214 215 216 struct utmpx * 217 getutxline(const struct utmpx *utx) 218 { 219 220 _DIAGASSERT(utx != NULL); 221 222 do { 223 switch (ut.ut_type) { 224 case EMPTY: 225 break; 226 case LOGIN_PROCESS: 227 case USER_PROCESS: 228 if (strncmp(ut.ut_line, utx->ut_line, 229 sizeof(ut.ut_line)) == 0) 230 return &ut; 231 break; 232 default: 233 break; 234 } 235 } while (getutxent() != NULL); 236 return NULL; 237 } 238 239 240 struct utmpx * 241 pututxline(const struct utmpx *utx) 242 { 243 struct utmpx temp, *u = NULL; 244 int gotlock = 0; 245 246 _DIAGASSERT(utx != NULL); 247 248 if (utx == NULL) 249 return NULL; 250 251 if (strcmp(_PATH_UTMPX, utfile) == 0) { 252 if (geteuid() == 0) { 253 if (fp != NULL && readonly) 254 endutxent(); 255 } else { 256 if (fp == NULL || readonly) 257 return utmp_update(utx); 258 } 259 } 260 261 262 (void)memcpy(&temp, utx, sizeof(temp)); 263 264 if (fp == NULL) { 265 (void)getutxent(); 266 if (fp == NULL || readonly) 267 return NULL; 268 } 269 270 if (getutxid(&temp) == NULL) { 271 setutxent(); 272 if (getutxid(&temp) == NULL) { 273 if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) 274 return NULL; 275 gotlock++; 276 if (fseeko(fp, (off_t)0, SEEK_END) == -1) 277 goto fail; 278 } 279 } 280 281 if (!gotlock) { 282 /* we are not appending */ 283 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) 284 return NULL; 285 } 286 287 if (version == 1) 288 new2old(&temp); 289 if (fwrite(&temp, sizeof (temp), 1, fp) != 1) 290 goto fail; 291 292 if (fflush(fp) == -1) 293 goto fail; 294 295 u = memcpy(&ut, &temp, sizeof(ut)); 296 fail: 297 if (gotlock) { 298 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) 299 return NULL; 300 } 301 return u; 302 } 303 304 305 static struct utmpx * 306 utmp_update(const struct utmpx *utx) 307 { 308 char buf[sizeof(*utx) * 4 + 1]; 309 pid_t pid; 310 int status; 311 312 _DIAGASSERT(utx != NULL); 313 314 (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx), 315 VIS_WHITE | VIS_NOLOCALE); 316 switch (pid = fork()) { 317 case 0: 318 (void)execl(_PATH_UTMP_UPDATE, 319 strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL); 320 _exit(1); 321 /*NOTREACHED*/ 322 case -1: 323 return NULL; 324 default: 325 if (waitpid(pid, &status, 0) == -1) 326 return NULL; 327 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 328 return memcpy(&ut, utx, sizeof(ut)); 329 return NULL; 330 } 331 332 } 333 334 /* 335 * The following are extensions and not part of the X/Open spec. 336 */ 337 int 338 updwtmpx(const char *file, const struct utmpx *utx) 339 { 340 int fd; 341 int saved_errno; 342 343 _DIAGASSERT(file != NULL); 344 _DIAGASSERT(utx != NULL); 345 346 fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK|O_CLOEXEC); 347 348 if (fd == -1) { 349 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK|O_CLOEXEC, 0644)) == -1) 350 return -1; 351 (void)memset(&ut, 0, sizeof(ut)); 352 ut.ut_type = SIGNATURE; 353 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 354 if (write(fd, &ut, sizeof(ut)) == -1) 355 goto failed; 356 } 357 if (write(fd, utx, sizeof(*utx)) == -1) 358 goto failed; 359 if (close(fd) == -1) 360 return -1; 361 return 0; 362 363 failed: 364 saved_errno = errno; 365 (void) close(fd); 366 errno = saved_errno; 367 return -1; 368 } 369 370 371 int 372 utmpxname(const char *fname) 373 { 374 size_t len; 375 376 _DIAGASSERT(fname != NULL); 377 378 len = strlen(fname); 379 380 if (len >= sizeof(utfile)) 381 return 0; 382 383 /* must end in x! */ 384 if (fname[len - 1] != 'x') 385 return 0; 386 387 (void)strlcpy(utfile, fname, sizeof(utfile)); 388 endutxent(); 389 return 1; 390 } 391 392 393 void 394 getutmp(const struct utmpx *ux, struct utmp *u) 395 { 396 397 _DIAGASSERT(ux != NULL); 398 _DIAGASSERT(u != NULL); 399 400 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 401 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 402 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 403 u->ut_time = ux->ut_tv.tv_sec; 404 } 405 406 void 407 getutmpx(const struct utmp *u, struct utmpx *ux) 408 { 409 410 _DIAGASSERT(ux != NULL); 411 _DIAGASSERT(u != NULL); 412 413 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 414 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 415 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 416 ux->ut_tv.tv_sec = u->ut_time; 417 ux->ut_tv.tv_usec = 0; 418 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 419 ux->ut_pid = 0; 420 ux->ut_type = USER_PROCESS; 421 ux->ut_session = 0; 422 ux->ut_exit.e_termination = 0; 423 ux->ut_exit.e_exit = 0; 424 } 425 426 struct lastlogx * 427 getlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 428 { 429 DBT key, data; 430 DB *db; 431 432 _DIAGASSERT(fname != NULL); 433 _DIAGASSERT(ll != NULL); 434 435 db = dbopen(fname, O_RDONLY|O_SHLOCK|O_CLOEXEC, 0, DB_HASH, NULL); 436 437 if (db == NULL) 438 return NULL; 439 440 key.data = &uid; 441 key.size = sizeof(uid); 442 443 if ((db->get)(db, &key, &data, 0) != 0) 444 goto error; 445 446 if (data.size != sizeof(*ll)) { 447 errno = EFTYPE; 448 goto error; 449 } 450 451 if (ll == NULL) 452 if ((ll = malloc(sizeof(*ll))) == NULL) 453 goto done; 454 455 (void)memcpy(ll, data.data, sizeof(*ll)); 456 goto done; 457 error: 458 ll = NULL; 459 done: 460 (db->close)(db); 461 return ll; 462 } 463 464 int 465 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 466 { 467 DBT key, data; 468 int error = 0; 469 DB *db; 470 471 _DIAGASSERT(fname != NULL); 472 _DIAGASSERT(ll != NULL); 473 474 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK|O_CLOEXEC, 0644, DB_HASH, NULL); 475 476 if (db == NULL) 477 return -1; 478 479 key.data = &uid; 480 key.size = sizeof(uid); 481 data.data = ll; 482 data.size = sizeof(*ll); 483 if ((db->put)(db, &key, &data, 0) != 0) 484 error = -1; 485 486 (db->close)(db); 487 return error; 488 } 489