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