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