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 = NULL; 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(char *fname) 64 { 65 struct stat st; 66 67 if ((fp = fopen(fname, "r+")) == NULL) 68 if ((fp = fopen(fname, "w+")) == NULL) { 69 if ((fp = fopen(fname, "r")) == 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, 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 143 #if 0 144 if (dbtype != UTX_DB_UTMPX) 145 setutxdb(UTX_DB_UTMPX, utfile); 146 #endif 147 (void)fseeko(fp, (off_t)sizeof(ut), SEEK_SET); 148 } 149 150 void 151 endutxent(void) 152 { 153 (void)memset(&ut, 0, sizeof(ut)); 154 155 if (fp != NULL) { 156 (void)fclose(fp); 157 fp = NULL; 158 readonly = 0; 159 } 160 } 161 162 struct utmpx * 163 getutxent(void) 164 { 165 if (fp == NULL) { 166 if ((_open_db(utfile)) == -1) 167 goto fail; 168 } 169 170 if (fread(&ut, sizeof(ut), 1, fp) != 1) 171 goto fail; 172 173 return &ut; 174 fail: 175 (void)memset(&ut, 0, sizeof(ut)); 176 return NULL; 177 } 178 179 struct utmpx * 180 getutxid(const struct utmpx *utx) 181 { 182 183 _DIAGASSERT(utx != NULL); 184 185 if (utx->ut_type == EMPTY) 186 return NULL; 187 188 do { 189 if (ut.ut_type == EMPTY) 190 continue; 191 switch (utx->ut_type) { 192 case EMPTY: 193 return NULL; 194 case RUN_LVL: 195 case BOOT_TIME: 196 case OLD_TIME: 197 case NEW_TIME: 198 if (ut.ut_type == utx->ut_type) 199 return &ut; 200 break; 201 case INIT_PROCESS: 202 case LOGIN_PROCESS: 203 case USER_PROCESS: 204 case DEAD_PROCESS: 205 switch (ut.ut_type) { 206 case INIT_PROCESS: 207 case LOGIN_PROCESS: 208 case USER_PROCESS: 209 case DEAD_PROCESS: 210 if (memcmp(ut.ut_id, utx->ut_id, 211 sizeof(ut.ut_id)) == 0) 212 return &ut; 213 break; 214 default: 215 break; 216 } 217 break; 218 default: 219 return NULL; 220 } 221 } while (getutxent() != NULL); 222 return NULL; 223 } 224 225 struct utmpx * 226 getutxline(const struct utmpx *utx) 227 { 228 229 _DIAGASSERT(utx != NULL); 230 231 do { 232 switch (ut.ut_type) { 233 case EMPTY: 234 break; 235 case LOGIN_PROCESS: 236 case USER_PROCESS: 237 if (strncmp(ut.ut_line, utx->ut_line, 238 sizeof(ut.ut_line)) == 0) 239 return &ut; 240 break; 241 default: 242 break; 243 } 244 } while (getutxent() != NULL); 245 return NULL; 246 } 247 248 struct utmpx * 249 pututxline(const struct utmpx *utx) 250 { 251 struct passwd *pw; 252 struct lastlogx ll; 253 struct utmpx temp, *u = NULL; 254 int gotlock = 0; 255 256 _DIAGASSERT(utx != NULL); 257 258 if (utx == NULL) 259 return NULL; 260 261 if (utx->ut_type == USER_PROCESS) { 262 ll.ll_tv = utx->ut_tv; 263 strcpy(ll.ll_host, utx->ut_host); 264 strcpy(ll.ll_line, utx->ut_line); 265 pw = getpwnam(utx->ut_name); 266 if (pw != NULL) 267 updlastlogx(_PATH_LASTLOGX, pw->pw_uid, &ll); 268 } 269 270 if (strcmp(_PATH_UTMPX, utfile) == 0) 271 if ((fp != NULL && readonly) || (fp == NULL && geteuid() != 0)) 272 return utmp_update(utx); 273 274 275 (void)memcpy(&temp, utx, sizeof(temp)); 276 277 if (fp == NULL) { 278 (void)getutxent(); 279 if (fp == NULL || readonly) 280 return NULL; 281 } 282 283 if (getutxid(&temp) == NULL) { 284 setutxent(); 285 if (getutxid(&temp) == NULL) { 286 if (lockf(fileno(fp), F_LOCK, (off_t)0) == -1) 287 return NULL; 288 gotlock++; 289 if (fseeko(fp, (off_t)0, SEEK_END) == -1) 290 goto fail; 291 } 292 } 293 294 if (!gotlock) { 295 /* we are not appending */ 296 if (fseeko(fp, -(off_t)sizeof(ut), SEEK_CUR) == -1) 297 return NULL; 298 } 299 300 if (fwrite(&temp, sizeof (temp), 1, fp) != 1) 301 goto fail; 302 303 if (fflush(fp) == -1) 304 goto fail; 305 306 u = memcpy(&ut, &temp, sizeof(ut)); 307 fail: 308 if (gotlock) { 309 if (lockf(fileno(fp), F_ULOCK, (off_t)0) == -1) 310 return NULL; 311 } 312 return u; 313 } 314 315 static struct utmpx * 316 utmp_update(const struct utmpx *utx) 317 { 318 char buf[sizeof(*utx) * 4 + 1]; 319 pid_t pid; 320 int status; 321 322 _DIAGASSERT(utx != NULL); 323 324 (void)strvisx(buf, (const char *)(const void *)utx, sizeof(*utx), 325 VIS_WHITE); 326 switch (pid = fork()) { 327 case 0: 328 (void)execl(_PATH_UTMP_UPDATE, 329 strrchr(_PATH_UTMP_UPDATE, '/') + 1, buf, NULL); 330 _exit(1); 331 /*NOTREACHED*/ 332 case -1: 333 return NULL; 334 default: 335 if (waitpid(pid, &status, 0) == -1) 336 return NULL; 337 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 338 return memcpy(&ut, utx, sizeof(ut)); 339 return NULL; 340 } 341 342 } 343 344 /* 345 * The following are extensions and not part of the X/Open spec. 346 */ 347 void 348 updwtmpx(const char *file, const struct utmpx *utx) 349 { 350 (void)_updwtmpx(file, utx); 351 } 352 353 int 354 _updwtmpx(const char *file, const struct utmpx *utx) 355 { 356 int fd; 357 int saved_errno; 358 359 _DIAGASSERT(file != NULL); 360 _DIAGASSERT(utx != NULL); 361 362 fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK); 363 364 if (fd == -1) { 365 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) 366 return -1; 367 (void)memset(&ut, 0, sizeof(ut)); 368 ut.ut_type = SIGNATURE; 369 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 370 if (write(fd, &ut, sizeof(ut)) == -1) 371 goto failed; 372 } 373 if (write(fd, utx, sizeof(*utx)) == -1) 374 goto failed; 375 if (close(fd) == -1) 376 return -1; 377 return 0; 378 379 failed: 380 saved_errno = errno; 381 (void) close(fd); 382 errno = saved_errno; 383 return -1; 384 } 385 386 int 387 utmpxname(const char *fname) 388 { 389 size_t len; 390 391 _DIAGASSERT(fname != NULL); 392 393 len = strlen(fname); 394 395 if (len >= sizeof(utfile)) 396 return 0; 397 398 /* must end in x! */ 399 if (fname[len - 1] != 'x') 400 return 0; 401 402 (void)strlcpy(utfile, fname, sizeof(utfile)); 403 endutxent(); 404 return 1; 405 } 406 407 void 408 getutmp(const struct utmpx *ux, struct utmp *u) 409 { 410 411 _DIAGASSERT(ux != NULL); 412 _DIAGASSERT(u != NULL); 413 414 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 415 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 416 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 417 u->ut_time = ux->ut_tv.tv_sec; 418 } 419 420 void 421 getutmpx(const struct utmp *u, struct utmpx *ux) 422 { 423 424 _DIAGASSERT(ux != NULL); 425 _DIAGASSERT(u != NULL); 426 427 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 428 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 429 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 430 ux->ut_tv.tv_sec = u->ut_time; 431 ux->ut_tv.tv_usec = 0; 432 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 433 ux->ut_pid = 0; 434 ux->ut_type = USER_PROCESS; 435 ux->ut_session = 0; 436 ux->ut_exit.e_termination = 0; 437 ux->ut_exit.e_exit = 0; 438 } 439 440 struct lastlogx * 441 getlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 442 { 443 DBT key, data; 444 DB *db; 445 446 _DIAGASSERT(fname != NULL); 447 _DIAGASSERT(ll != NULL); 448 449 db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); 450 451 if (db == NULL) 452 return NULL; 453 454 key.data = &uid; 455 key.size = sizeof(uid); 456 457 if ((db->get)(db, &key, &data, 0) != 0) 458 goto error; 459 460 if (data.size != sizeof(*ll)) { 461 errno = EFTYPE; 462 goto error; 463 } 464 465 if (ll == NULL) 466 if ((ll = malloc(sizeof(*ll))) == NULL) 467 goto done; 468 469 (void)memcpy(ll, data.data, sizeof(*ll)); 470 goto done; 471 error: 472 ll = NULL; 473 done: 474 (db->close)(db); 475 return ll; 476 } 477 478 int 479 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 480 { 481 DBT key, data; 482 int error = 0; 483 DB *db; 484 485 _DIAGASSERT(fname != NULL); 486 _DIAGASSERT(ll != NULL); 487 488 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL); 489 490 if (db == NULL) 491 return -1; 492 493 key.data = &uid; 494 key.size = sizeof(uid); 495 data.data = ll; 496 data.size = sizeof(*ll); 497 if ((db->put)(db, &key, &data, 0) != 0) 498 error = -1; 499 500 (db->close)(db); 501 return error; 502 } 503