1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1999-2002 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * utmp_update - Update the /var/adm/utmpx file 31 * 32 * As of on28, the utmp interface is obsolete, 33 * so we only handle updating the utmpx file now. 34 * The utmpx routines in libc "simulate" calls 35 * to manipulate utmp entries. 36 * 37 * This program runs set uid root on behalf of 38 * non-privileged user programs. Normal programs cannot 39 * write to /var/adm/utmpx. Non-root callers of pututxline 40 * will invoke this program to write the utmpx entry. 41 */ 42 43 /* 44 * Header files 45 */ 46 #include <stdio.h> 47 #include <sys/param.h> 48 #include <sys/types.h> 49 #include <sys/stat.h> 50 #include <utmpx.h> 51 #include <errno.h> 52 #include <fcntl.h> 53 #include <string.h> 54 #include <stdlib.h> 55 #include <unistd.h> 56 #include <pwd.h> 57 #include <ctype.h> 58 #include <stropts.h> 59 #include <syslog.h> 60 61 /* 62 * Invocation argument definitions 63 */ 64 65 #define UTMPX_NARGS 14 66 67 /* 68 * Return codes 69 */ 70 #define NORMAL_EXIT 0 71 #define BAD_ARGS 1 72 #define PUTUTXLINE_FAILURE 2 73 #define FORK_FAILURE 3 74 #define SETSID_FAILURE 4 75 #define ALREADY_DEAD 5 76 #define ENTRY_NOTFOUND 6 77 #define ILLEGAL_ARGUMENT 7 78 79 /* 80 * Sizes 81 */ 82 83 #define MAX_SYSLEN 257 /* From utmpx.h host length + nul */ 84 #define BUF_SIZE 256 85 86 /* 87 * Other defines 88 */ 89 #define ROOT_UID 0 90 /* 91 * Debugging support 92 */ 93 #ifdef DEBUG 94 #define dprintf printf 95 #define dprintf3 printf 96 static void display_args(); 97 #else /* DEBUG */ 98 #define dprintf(x, y) 99 #define dprintf3(w, x, y, z) 100 #endif 101 102 /* 103 * Local functions 104 */ 105 106 static void load_utmpx_struct(struct utmpx *, char **); 107 static void usage(void); 108 static void check_utmpx(struct utmpx *); 109 static int bad_hostname(char *, int); 110 static int hex2bin(unsigned char); 111 112 static int invalid_utmpx(struct utmpx *, struct utmpx *); 113 static int bad_line(char *); 114 static void check_id(char *, char *); 115 116 int 117 main(int argc, char *argv[]) 118 { 119 struct utmpx *rutmpx; 120 struct utmpx entryx; 121 int error = 0; 122 int num_args; 123 #ifdef DEBUG 124 int debugger = 1; 125 printf("%d\n", getpid()); 126 /* Uncomment the following for attaching with dbx(1) */ 127 /* while (debugger) ; */ 128 display_args(argc, argv); 129 #endif /* DEBUG */ 130 131 /* 132 * We will always be called by pututxline, so simply 133 * verify the correct number of args 134 */ 135 136 if (argc != UTMPX_NARGS) { 137 usage(); 138 exit(BAD_ARGS); 139 } 140 /* 141 * we should never be called by root the code in libc already 142 * updates the file for root so no need to do it here. This 143 * assumption simpilfies the rest of code since we nolonger 144 * have to do special processing for the case when we are called 145 * by root 146 * 147 */ 148 if (getuid() == ROOT_UID) { 149 usage(); 150 exit(ILLEGAL_ARGUMENT); 151 } 152 /* 153 * Search for matching entry by line name before put operation 154 * (scan over the whole file using getutxent(3C) to ensure 155 * that the line name is the same. We can not use getutline(3C) 156 * because that will return LOGIN_PROCESS and USER_PROCESS 157 * records. Also check that the entry is for either a dead 158 * process or a current process that is valid (see 159 * invalid_utmpx() for details of validation criteria). 160 */ 161 load_utmpx_struct(&entryx, argv); 162 check_utmpx(&entryx); 163 for (rutmpx = getutxent(); rutmpx != (struct utmpx *)NULL; 164 rutmpx = getutxent()) { 165 166 if (strncmp(entryx.ut_line, rutmpx->ut_line, 167 sizeof (entryx.ut_line)) == 0) { 168 169 if (rutmpx->ut_type == DEAD_PROCESS) { 170 break; 171 } 172 173 if (rutmpx->ut_type == USER_PROCESS) { 174 if (invalid_utmpx(&entryx, rutmpx)) { 175 usage(); 176 exit(ILLEGAL_ARGUMENT); 177 } else { 178 break; 179 } 180 } 181 } 182 } 183 184 if (pututxline(&entryx) == (struct utmpx *)NULL) { 185 exit(PUTUTXLINE_FAILURE); 186 } 187 exit(NORMAL_EXIT); 188 } 189 190 static int 191 hex2bin(unsigned char c) 192 { 193 if ('0' <= c && c <= '9') 194 return (c - '0'); 195 else if ('A' <= c && c <= 'F') 196 return (10 + c - 'A'); 197 else if ('a' <= c && c <= 'f') 198 return (10 + c - 'a'); 199 else { 200 dprintf("Bad hex character: 0x%x\n", c); 201 exit(ILLEGAL_ARGUMENT); 202 } 203 } 204 205 206 /* 207 * load_utmpx_struct - Load up the utmpx structure with information supplied 208 * as arguments in argv. 209 */ 210 211 static void 212 load_utmpx_struct(struct utmpx *entryx, char *argv[]) 213 { 214 char *user, *id, *line, *pid, *type, *term, *time_usec, 215 *exitstatus, *xtime, *session, *pad, *syslen, *host; 216 int temp, i; 217 unsigned char *cp; 218 219 (void) memset(entryx, 0, sizeof (struct utmpx)); 220 221 user = argv[1]; 222 id = argv[2]; 223 line = argv[3]; 224 pid = argv[4]; 225 type = argv[5]; 226 term = argv[6]; 227 exitstatus = argv[7]; 228 xtime = argv[8]; 229 time_usec = argv[9 ]; 230 session = argv[10]; 231 pad = argv[11]; 232 syslen = argv[12]; 233 host = argv[13]; 234 235 (void) strncpy(entryx->ut_user, user, sizeof (entryx->ut_user)); 236 (void) strncpy(entryx->ut_id, id, sizeof (entryx->ut_id)); 237 (void) strncpy(entryx->ut_line, line, sizeof (entryx->ut_line)); 238 239 (void) sscanf(pid, "%d", &temp); 240 entryx->ut_pid = temp; 241 242 (void) sscanf(type, "%d", &temp); 243 entryx->ut_type = temp; 244 245 (void) sscanf(term, "%d", &temp); 246 entryx->ut_exit.e_termination = temp; 247 248 (void) sscanf(exitstatus, "%d", &temp); 249 entryx->ut_exit.e_exit = temp; 250 /* 251 * Here's where we stamp the exit field of a USER_PROCESS 252 * record so that we know it was written by a normal user. 253 */ 254 255 if (entryx->ut_type == USER_PROCESS) 256 setuserx(*entryx); 257 258 (void) sscanf(xtime, "%d", &temp); 259 entryx->ut_tv.tv_sec = temp; 260 261 (void) sscanf(time_usec, "%d", &temp); 262 entryx->ut_tv.tv_usec = temp; 263 264 (void) sscanf(session, "%d", &temp); 265 entryx->ut_session = temp; 266 267 temp = strlen(pad); 268 cp = (unsigned char *)entryx->pad; 269 for (i = 0; i < temp && (i>>1) < sizeof (entryx->pad); i += 2) 270 cp[i>>1] = hex2bin(pad[i]) << 4 | hex2bin(pad[i+1]); 271 272 (void) sscanf(syslen, "%d", &temp); 273 entryx->ut_syslen = temp; 274 275 (void) strlcpy(entryx->ut_host, host, sizeof (entryx->ut_host)); 276 } 277 278 /* 279 * usage - There's no need to say more. This program isn't supposed to 280 * be executed by normal users directly. 281 */ 282 283 static void 284 usage() 285 { 286 syslog(LOG_ERR, "Wrong number of arguments or invalid user \n"); 287 } 288 289 /* 290 * check_utmpx - Verify the utmpx structure 291 */ 292 293 static void 294 check_utmpx(struct utmpx *entryx) 295 { 296 char buf[BUF_SIZE]; 297 char *line = buf; 298 struct passwd *pwd; 299 int uid; 300 int fd; 301 int hostlen; 302 char *user; 303 uid_t ruid = getuid(); 304 305 (void) memset(buf, 0, BUF_SIZE); 306 user = malloc(sizeof (entryx->ut_user) +1); 307 (void) strncpy(user, entryx->ut_user, sizeof (entryx->ut_user)); 308 user[sizeof (entryx->ut_user)] = '\0'; 309 pwd = getpwnam(user); 310 (void) free(user); 311 312 (void) strlcat(strcpy(buf, "/dev/"), entryx->ut_line, sizeof (buf)); 313 314 if (pwd != (struct passwd *)NULL) { 315 uid = pwd->pw_uid; 316 /* 317 * We nolonger permit the UID of the caller to be different 318 * the UID to be written to the utmp file. This was thought 319 * necessary to allow the utmp file to be updated when 320 * logging out from an xterm(1) window after running 321 * exec login. Instead we now rely upon utmpd(1) to update 322 * the utmp file for us. 323 * 324 */ 325 326 if (ruid != uid) { 327 dprintf3("Bad uid: user %s = %d uid = %d \n", 328 entryx->ut_user, uid, getuid()); 329 exit(ILLEGAL_ARGUMENT); 330 } 331 332 } else if (entryx->ut_type != DEAD_PROCESS) { 333 dprintf("Bad user name: %s \n", entryx->ut_user); 334 exit(ILLEGAL_ARGUMENT); 335 } 336 337 /* 338 * Only USER_PROCESS and DEAD_PROCESS entries may be updated 339 */ 340 if (!(entryx->ut_type == USER_PROCESS || 341 entryx->ut_type == DEAD_PROCESS)) { 342 dprintf("Bad type type = %d\n", entryx->ut_type); 343 exit(ILLEGAL_ARGUMENT); 344 } 345 346 /* 347 * Verify that the pid of the entry field is the same pid as our 348 * parent, who should be the guy writing the entry. This is commented 349 * out for now because this restriction is overkill. 350 */ 351 #ifdef VERIFY_PID 352 if (entryx->ut_type == USER_PROCESS && entryx->ut_pid != getppid()) { 353 dprintf("Bad pid = %d\n", entryx->ut_pid); 354 exit(ILLEGAL_ARGUMENT); 355 } 356 #endif /* VERIFY_PID */ 357 358 if (bad_line(line) == 1) { 359 dprintf("Bad line = %s\n", line); 360 exit(ILLEGAL_ARGUMENT); 361 } 362 363 hostlen = strlen(entryx->ut_host) + 1; 364 if (entryx->ut_syslen != hostlen) { 365 dprintf3("Bad syslen of \"%s\" = %d - correcting to %d\n", 366 entryx->ut_host, entryx->ut_syslen, hostlen); 367 entryx->ut_syslen = hostlen; 368 } 369 370 if (bad_hostname(entryx->ut_host, entryx->ut_syslen) == 1) { 371 dprintf("Bad hostname name = %s\n", entryx->ut_host); 372 exit(ILLEGAL_ARGUMENT); 373 } 374 check_id(entryx->ut_id, entryx->ut_line); 375 } 376 377 /* 378 * bad_hostname - Previously returned an error if a non alpha numeric 379 * was in the host field, but now just clears those so 380 * cmdtool entries will work. 381 */ 382 383 static int 384 bad_hostname(char *name, int len) 385 { 386 int i; 387 388 if (len < 0 || len > MAX_SYSLEN) 389 return (1); 390 /* 391 * Scan for non-alpha numerics 392 * Per utmpx.h, len includes the nul character. 393 */ 394 for (i = 0; i < len; i++) 395 if (name[i] != '\0' && isprint(name[i]) == 0) 396 name[i] = ' '; 397 return (0); 398 } 399 400 /* 401 * Workaround until the window system gets fixed. Look for id's with 402 * a '/' in them. That means they are probably from libxview. 403 * Then create a new id that is unique using the last 4 chars in the line. 404 */ 405 406 static void 407 check_id(char *id, char *line) 408 { 409 char temp[4]; 410 int i, len; 411 412 if (id[1] == '/' && id[2] == 's' && id[3] == 't') { 413 len = strlen(line); 414 if (len > 0) 415 len--; 416 for (i = 0; i < 4; i++) 417 id[i] = len - i < 0 ? 0 : line[len-i]; 418 } 419 } 420 421 422 /* 423 * The function invalid_utmpx() enforces the requirement that the record 424 * being updating in the utmpx file can not have been created by login(1) 425 * or friends. Also that the id and username of the record to be written match 426 * those found in the utmpx file. We need this both for security and to ensure 427 * that pututxline(3C) will NOT reposition the file pointer in the utmpx file, 428 * so that the record is updated in place. 429 * 430 */ 431 static int 432 invalid_utmpx(struct utmpx *eutmpx, struct utmpx *rutmpx) 433 { 434 #define SUTMPX_ID (sizeof (eutmpx->ut_id)) 435 #define SUTMPX_USER (sizeof (eutmpx->ut_user)) 436 437 return (!nonuserx(*rutmpx) || 438 strncmp(eutmpx->ut_id, rutmpx->ut_id, SUTMPX_ID) != 0 || 439 strncmp(eutmpx->ut_user, rutmpx->ut_user, SUTMPX_USER) != 0); 440 } 441 442 static int 443 bad_line(char *line) 444 { 445 struct stat statbuf; 446 int fd; 447 448 /* 449 * The line field must be a device file that we can write to, 450 * it should live under /dev which is enforced by requiring 451 * its name not to contain "../" and opening it as the user for 452 * writing. 453 */ 454 if (strstr(line, "../") != 0) { 455 dprintf("Bad line = %s\n", line); 456 return (1); 457 } 458 459 /* 460 * It has to be a tty. It can't be a bogus file, e.g. ../tmp/bogus. 461 */ 462 if (seteuid(getuid()) != 0) 463 return (1); 464 465 /* 466 * Check that the line refers to a character 467 * special device see bugid: 1136978 468 */ 469 if ((stat(line, &statbuf) < 0) || (statbuf.st_mode & S_IFCHR) == 0) { 470 dprintf("Bad line (stat failed) (Not S_IFCHR) = %s\n", line); 471 return (1); 472 } 473 474 /* 475 * We need to open the line without blocking so that it does not hang 476 */ 477 if ((fd = open(line, O_WRONLY|O_NOCTTY|O_NONBLOCK)) == -1) { 478 dprintf("Bad line (Can't open/write) = %s\n", line); 479 return (1); 480 } 481 482 /* 483 * Check that fd is a tty, if this fails all is not lost see below 484 */ 485 if (isatty(fd) == 1) { 486 /* 487 * It really is a tty, so return success 488 */ 489 close(fd); 490 if (seteuid(ROOT_UID) != 0) 491 return (1); 492 return (0); 493 } 494 495 /* 496 * Check that the line refers to a character 497 * special device see bugid: 1136978 498 */ 499 if ((fstat(fd, &statbuf) < 0) || (statbuf.st_mode & S_IFCHR) == 0) { 500 dprintf("Bad line (fstat failed) (Not S_IFCHR) = %s\n", line); 501 close(fd); 502 return (1); 503 } 504 505 /* 506 * Check that the line refers to a streams device 507 */ 508 if (isastream(fd) != 1) { 509 dprintf("Bad line (isastream failed) = %s\n", line); 510 close(fd); 511 return (1); 512 } 513 514 /* 515 * if isatty(3C) failed above we assume that the ptem module has 516 * been popped already and that caused the failure, so we push it 517 * and try again 518 */ 519 if (ioctl(fd, I_PUSH, "ptem") == -1) { 520 dprintf("Bad line (I_PUSH of \"ptem\" failed) = %s\n", line); 521 close(fd); 522 return (1); 523 } 524 525 if (isatty(fd) != 1) { 526 dprintf("Bad line (isatty failed) = %s\n", line); 527 close(fd); 528 return (1); 529 } 530 531 if (ioctl(fd, I_POP, 0) == -1) { 532 dprintf("Bad line (I_POP of \"ptem\" failed) = %s\n", line); 533 close(fd); 534 return (1); 535 } 536 537 close(fd); 538 539 if (seteuid(ROOT_UID) != 0) 540 return (1); 541 542 return (0); 543 544 } 545 546 #ifdef DEBUG 547 548 /* 549 * display_args - This code prints out invocation arguments 550 * This is helpful since the program is called with 551 * up to 15 argumments. 552 */ 553 554 static void 555 display_args(argc, argv) 556 int argc; 557 char **argv; 558 { 559 int i = 0; 560 561 while (argc--) { 562 printf("Argument #%d = %s\n", i, argv[i]); 563 i++; 564 } 565 } 566 567 fputmpx(struct utmpx *rutmpx) 568 { 569 printf("ut_user = \"%-32.32s\" \n", rutmpx->ut_user); 570 printf("ut_id = \"%-4.4s\" \n", rutmpx->ut_id); 571 printf("ut_line = \"%-32.32s\" \n", rutmpx->ut_line); 572 printf("ut_pid = \"%d\" \n", rutmpx->ut_pid); 573 printf("ut_type = \"%d\" \n", rutmpx->ut_type); 574 printf("ut_exit.e_termination = \"%d\" \n", 575 rutmpx->ut_exit.e_termination); 576 printf("ut_exit.e_exit = \"%d\" \n", rutmpx->ut_exit.e_exit); 577 } 578 579 #endif DEBUG 580