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 int 349 updwtmpx(const char *file, const struct utmpx *utx) 350 { 351 int fd; 352 int saved_errno; 353 354 _DIAGASSERT(file != NULL); 355 _DIAGASSERT(utx != NULL); 356 357 fd = open(file, O_WRONLY|O_APPEND|O_SHLOCK); 358 359 if (fd == -1) { 360 if ((fd = open(file, O_CREAT|O_WRONLY|O_EXLOCK, 0644)) == -1) 361 return -1; 362 (void)memset(&ut, 0, sizeof(ut)); 363 ut.ut_type = SIGNATURE; 364 (void)memcpy(ut.ut_user, vers, sizeof(vers)); 365 if (write(fd, &ut, sizeof(ut)) == -1) 366 goto failed; 367 } 368 if (write(fd, utx, sizeof(*utx)) == -1) 369 goto failed; 370 if (close(fd) == -1) 371 return -1; 372 return 0; 373 374 failed: 375 saved_errno = errno; 376 (void) close(fd); 377 errno = saved_errno; 378 return -1; 379 } 380 381 int 382 utmpxname(const char *fname) 383 { 384 size_t len; 385 386 _DIAGASSERT(fname != NULL); 387 388 len = strlen(fname); 389 390 if (len >= sizeof(utfile)) 391 return 0; 392 393 /* must end in x! */ 394 if (fname[len - 1] != 'x') 395 return 0; 396 397 (void)strlcpy(utfile, fname, sizeof(utfile)); 398 endutxent(); 399 return 1; 400 } 401 402 void 403 getutmp(const struct utmpx *ux, struct utmp *u) 404 { 405 406 _DIAGASSERT(ux != NULL); 407 _DIAGASSERT(u != NULL); 408 409 (void)memcpy(u->ut_name, ux->ut_name, sizeof(u->ut_name)); 410 (void)memcpy(u->ut_line, ux->ut_line, sizeof(u->ut_line)); 411 (void)memcpy(u->ut_host, ux->ut_host, sizeof(u->ut_host)); 412 u->ut_time = ux->ut_tv.tv_sec; 413 } 414 415 void 416 getutmpx(const struct utmp *u, struct utmpx *ux) 417 { 418 419 _DIAGASSERT(ux != NULL); 420 _DIAGASSERT(u != NULL); 421 422 (void)memcpy(ux->ut_name, u->ut_name, sizeof(u->ut_name)); 423 (void)memcpy(ux->ut_line, u->ut_line, sizeof(u->ut_line)); 424 (void)memcpy(ux->ut_host, u->ut_host, sizeof(u->ut_host)); 425 ux->ut_tv.tv_sec = u->ut_time; 426 ux->ut_tv.tv_usec = 0; 427 (void)memset(&ux->ut_ss, 0, sizeof(ux->ut_ss)); 428 ux->ut_pid = 0; 429 ux->ut_type = USER_PROCESS; 430 ux->ut_session = 0; 431 ux->ut_exit.e_termination = 0; 432 ux->ut_exit.e_exit = 0; 433 } 434 435 struct lastlogx * 436 getlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 437 { 438 DBT key, data; 439 DB *db; 440 441 _DIAGASSERT(fname != NULL); 442 _DIAGASSERT(ll != NULL); 443 444 db = dbopen(fname, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL); 445 446 if (db == NULL) 447 return NULL; 448 449 key.data = &uid; 450 key.size = sizeof(uid); 451 452 if ((db->get)(db, &key, &data, 0) != 0) 453 goto error; 454 455 if (data.size != sizeof(*ll)) { 456 errno = EFTYPE; 457 goto error; 458 } 459 460 if (ll == NULL) 461 if ((ll = malloc(sizeof(*ll))) == NULL) 462 goto done; 463 464 (void)memcpy(ll, data.data, sizeof(*ll)); 465 goto done; 466 error: 467 ll = NULL; 468 done: 469 (db->close)(db); 470 return ll; 471 } 472 473 int 474 updlastlogx(const char *fname, uid_t uid, struct lastlogx *ll) 475 { 476 DBT key, data; 477 int error = 0; 478 DB *db; 479 480 _DIAGASSERT(fname != NULL); 481 _DIAGASSERT(ll != NULL); 482 483 db = dbopen(fname, O_RDWR|O_CREAT|O_EXLOCK, 0644, DB_HASH, NULL); 484 485 if (db == NULL) 486 return -1; 487 488 key.data = &uid; 489 key.size = sizeof(uid); 490 data.data = ll; 491 data.size = sizeof(*ll); 492 if ((db->put)(db, &key, &data, 0) != 0) 493 error = -1; 494 495 (db->close)(db); 496 return error; 497 } 498