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