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 2005 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 #ifdef DEBUG 122 int debugger = 1; 123 printf("%d\n", getpid()); 124 /* Uncomment the following for attaching with dbx(1) */ 125 /* while (debugger) ; */ 126 display_args(argc, argv); 127 #endif /* DEBUG */ 128 129 /* 130 * We will always be called by pututxline, so simply 131 * verify the correct number of args 132 */ 133 134 if (argc != UTMPX_NARGS) { 135 usage(); 136 return (BAD_ARGS); 137 } 138 /* 139 * we should never be called by root the code in libc already 140 * updates the file for root so no need to do it here. This 141 * assumption simpilfies the rest of code since we nolonger 142 * have to do special processing for the case when we are called 143 * by root 144 * 145 */ 146 if (getuid() == ROOT_UID) { 147 usage(); 148 return (ILLEGAL_ARGUMENT); 149 } 150 /* 151 * Search for matching entry by line name before put operation 152 * (scan over the whole file using getutxent(3C) to ensure 153 * that the line name is the same. We can not use getutline(3C) 154 * because that will return LOGIN_PROCESS and USER_PROCESS 155 * records. Also check that the entry is for either a dead 156 * process or a current process that is valid (see 157 * invalid_utmpx() for details of validation criteria). 158 */ 159 load_utmpx_struct(&entryx, argv); 160 check_utmpx(&entryx); 161 for (rutmpx = getutxent(); rutmpx != (struct utmpx *)NULL; 162 rutmpx = getutxent()) { 163 164 if (strncmp(entryx.ut_line, rutmpx->ut_line, 165 sizeof (entryx.ut_line)) == 0) { 166 167 if (rutmpx->ut_type == DEAD_PROCESS) { 168 break; 169 } 170 171 if (rutmpx->ut_type == USER_PROCESS) { 172 if (invalid_utmpx(&entryx, rutmpx)) { 173 usage(); 174 return (ILLEGAL_ARGUMENT); 175 } else { 176 break; 177 } 178 } 179 } 180 } 181 182 if (pututxline(&entryx) == (struct utmpx *)NULL) { 183 return (PUTUTXLINE_FAILURE); 184 } 185 return (NORMAL_EXIT); 186 } 187 188 static int 189 hex2bin(unsigned char c) 190 { 191 if ('0' <= c && c <= '9') 192 return (c - '0'); 193 else if ('A' <= c && c <= 'F') 194 return (10 + c - 'A'); 195 else if ('a' <= c && c <= 'f') 196 return (10 + c - 'a'); 197 198 dprintf("Bad hex character: 0x%x\n", c); 199 exit(ILLEGAL_ARGUMENT); 200 /* NOTREACHED */ 201 } 202 203 204 /* 205 * load_utmpx_struct - Load up the utmpx structure with information supplied 206 * as arguments in argv. 207 */ 208 209 static void 210 load_utmpx_struct(struct utmpx *entryx, char *argv[]) 211 { 212 char *user, *id, *line, *pid, *type, *term, *time_usec, 213 *exitstatus, *xtime, *session, *pad, *syslen, *host; 214 int temp, i; 215 unsigned char *cp; 216 217 (void) memset(entryx, 0, sizeof (struct utmpx)); 218 219 user = argv[1]; 220 id = argv[2]; 221 line = argv[3]; 222 pid = argv[4]; 223 type = argv[5]; 224 term = argv[6]; 225 exitstatus = argv[7]; 226 xtime = argv[8]; 227 time_usec = argv[9 ]; 228 session = argv[10]; 229 pad = argv[11]; 230 syslen = argv[12]; 231 host = argv[13]; 232 233 (void) strncpy(entryx->ut_user, user, sizeof (entryx->ut_user)); 234 (void) strncpy(entryx->ut_id, id, sizeof (entryx->ut_id)); 235 (void) strncpy(entryx->ut_line, line, sizeof (entryx->ut_line)); 236 237 (void) sscanf(pid, "%d", &temp); 238 entryx->ut_pid = temp; 239 240 (void) sscanf(type, "%d", &temp); 241 entryx->ut_type = temp; 242 243 (void) sscanf(term, "%d", &temp); 244 entryx->ut_exit.e_termination = temp; 245 246 (void) sscanf(exitstatus, "%d", &temp); 247 entryx->ut_exit.e_exit = temp; 248 /* 249 * Here's where we stamp the exit field of a USER_PROCESS 250 * record so that we know it was written by a normal user. 251 */ 252 253 if (entryx->ut_type == USER_PROCESS) 254 setuserx(*entryx); 255 256 (void) sscanf(xtime, "%d", &temp); 257 entryx->ut_tv.tv_sec = temp; 258 259 (void) sscanf(time_usec, "%d", &temp); 260 entryx->ut_tv.tv_usec = temp; 261 262 (void) sscanf(session, "%d", &temp); 263 entryx->ut_session = temp; 264 265 temp = strlen(pad); 266 cp = (unsigned char *)entryx->pad; 267 for (i = 0; i < temp && (i>>1) < sizeof (entryx->pad); i += 2) 268 cp[i>>1] = hex2bin(pad[i]) << 4 | hex2bin(pad[i+1]); 269 270 (void) sscanf(syslen, "%d", &temp); 271 entryx->ut_syslen = temp; 272 273 (void) strlcpy(entryx->ut_host, host, sizeof (entryx->ut_host)); 274 } 275 276 /* 277 * usage - There's no need to say more. This program isn't supposed to 278 * be executed by normal users directly. 279 */ 280 281 static void 282 usage() 283 { 284 syslog(LOG_ERR, "Wrong number of arguments or invalid user \n"); 285 } 286 287 /* 288 * check_utmpx - Verify the utmpx structure 289 */ 290 291 static void 292 check_utmpx(struct utmpx *entryx) 293 { 294 char buf[BUF_SIZE]; 295 char *line = buf; 296 struct passwd *pwd; 297 int uid; 298 int hostlen; 299 char *user; 300 uid_t ruid = getuid(); 301 302 (void) memset(buf, 0, BUF_SIZE); 303 user = malloc(sizeof (entryx->ut_user) +1); 304 (void) strncpy(user, entryx->ut_user, sizeof (entryx->ut_user)); 305 user[sizeof (entryx->ut_user)] = '\0'; 306 pwd = getpwnam(user); 307 (void) free(user); 308 309 (void) strlcat(strcpy(buf, "/dev/"), entryx->ut_line, sizeof (buf)); 310 311 if (pwd != (struct passwd *)NULL) { 312 uid = pwd->pw_uid; 313 /* 314 * We nolonger permit the UID of the caller to be different 315 * the UID to be written to the utmp file. This was thought 316 * necessary to allow the utmp file to be updated when 317 * logging out from an xterm(1) window after running 318 * exec login. Instead we now rely upon utmpd(1) to update 319 * the utmp file for us. 320 * 321 */ 322 323 if (ruid != uid) { 324 dprintf3("Bad uid: user %s = %d uid = %d \n", 325 entryx->ut_user, uid, getuid()); 326 exit(ILLEGAL_ARGUMENT); 327 } 328 329 } else if (entryx->ut_type != DEAD_PROCESS) { 330 dprintf("Bad user name: %s \n", entryx->ut_user); 331 exit(ILLEGAL_ARGUMENT); 332 } 333 334 /* 335 * Only USER_PROCESS and DEAD_PROCESS entries may be updated 336 */ 337 if (!(entryx->ut_type == USER_PROCESS || 338 entryx->ut_type == DEAD_PROCESS)) { 339 dprintf("Bad type type = %d\n", entryx->ut_type); 340 exit(ILLEGAL_ARGUMENT); 341 } 342 343 /* 344 * Verify that the pid of the entry field is the same pid as our 345 * parent, who should be the guy writing the entry. This is commented 346 * out for now because this restriction is overkill. 347 */ 348 #ifdef VERIFY_PID 349 if (entryx->ut_type == USER_PROCESS && entryx->ut_pid != getppid()) { 350 dprintf("Bad pid = %d\n", entryx->ut_pid); 351 exit(ILLEGAL_ARGUMENT); 352 } 353 #endif /* VERIFY_PID */ 354 355 if (bad_line(line) == 1) { 356 dprintf("Bad line = %s\n", line); 357 exit(ILLEGAL_ARGUMENT); 358 } 359 360 hostlen = strlen(entryx->ut_host) + 1; 361 if (entryx->ut_syslen != hostlen) { 362 dprintf3("Bad syslen of \"%s\" = %d - correcting to %d\n", 363 entryx->ut_host, entryx->ut_syslen, hostlen); 364 entryx->ut_syslen = hostlen; 365 } 366 367 if (bad_hostname(entryx->ut_host, entryx->ut_syslen) == 1) { 368 dprintf("Bad hostname name = %s\n", entryx->ut_host); 369 exit(ILLEGAL_ARGUMENT); 370 } 371 check_id(entryx->ut_id, entryx->ut_line); 372 } 373 374 /* 375 * bad_hostname - Previously returned an error if a non alpha numeric 376 * was in the host field, but now just clears those so 377 * cmdtool entries will work. 378 */ 379 380 static int 381 bad_hostname(char *name, int len) 382 { 383 int i; 384 385 if (len < 0 || len > MAX_SYSLEN) 386 return (1); 387 /* 388 * Scan for non-alpha numerics 389 * Per utmpx.h, len includes the nul character. 390 */ 391 for (i = 0; i < len; i++) 392 if (name[i] != '\0' && isprint(name[i]) == 0) 393 name[i] = ' '; 394 return (0); 395 } 396 397 /* 398 * Workaround until the window system gets fixed. Look for id's with 399 * a '/' in them. That means they are probably from libxview. 400 * Then create a new id that is unique using the last 4 chars in the line. 401 */ 402 403 static void 404 check_id(char *id, char *line) 405 { 406 int i, len; 407 408 if (id[1] == '/' && id[2] == 's' && id[3] == 't') { 409 len = strlen(line); 410 if (len > 0) 411 len--; 412 for (i = 0; i < 4; i++) 413 id[i] = len - i < 0 ? 0 : line[len-i]; 414 } 415 } 416 417 418 /* 419 * The function invalid_utmpx() enforces the requirement that the record 420 * being updating in the utmpx file can not have been created by login(1) 421 * or friends. Also that the id and username of the record to be written match 422 * those found in the utmpx file. We need this both for security and to ensure 423 * that pututxline(3C) will NOT reposition the file pointer in the utmpx file, 424 * so that the record is updated in place. 425 * 426 */ 427 static int 428 invalid_utmpx(struct utmpx *eutmpx, struct utmpx *rutmpx) 429 { 430 #define SUTMPX_ID (sizeof (eutmpx->ut_id)) 431 #define SUTMPX_USER (sizeof (eutmpx->ut_user)) 432 433 return (!nonuserx(*rutmpx) || 434 strncmp(eutmpx->ut_id, rutmpx->ut_id, SUTMPX_ID) != 0 || 435 strncmp(eutmpx->ut_user, rutmpx->ut_user, SUTMPX_USER) != 0); 436 } 437 438 static int 439 bad_line(char *line) 440 { 441 struct stat statbuf; 442 int fd; 443 444 /* 445 * The line field must be a device file that we can write to, 446 * it should live under /dev which is enforced by requiring 447 * its name not to contain "../" and opening it as the user for 448 * writing. 449 */ 450 if (strstr(line, "../") != 0) { 451 dprintf("Bad line = %s\n", line); 452 return (1); 453 } 454 455 /* 456 * It has to be a tty. It can't be a bogus file, e.g. ../tmp/bogus. 457 */ 458 if (seteuid(getuid()) != 0) 459 return (1); 460 461 /* 462 * Check that the line refers to a character 463 * special device. 464 */ 465 if ((stat(line, &statbuf) < 0) || !S_ISCHR(statbuf.st_mode)) { 466 dprintf("Bad line (stat failed) (Not S_IFCHR) = %s\n", line); 467 return (1); 468 } 469 470 /* 471 * We need to open the line without blocking so that it does not hang 472 */ 473 if ((fd = open(line, O_WRONLY|O_NOCTTY|O_NONBLOCK)) == -1) { 474 dprintf("Bad line (Can't open/write) = %s\n", line); 475 return (1); 476 } 477 478 /* 479 * Check that fd is a tty, if this fails all is not lost see below 480 */ 481 if (isatty(fd) == 1) { 482 /* 483 * It really is a tty, so return success 484 */ 485 (void) close(fd); 486 if (seteuid(ROOT_UID) != 0) 487 return (1); 488 return (0); 489 } 490 491 /* 492 * Check that the line refers to a character 493 * special device. 494 */ 495 if ((fstat(fd, &statbuf) < 0) || !S_ISCHR(statbuf.st_mode)) { 496 dprintf("Bad line (fstat failed) (Not S_IFCHR) = %s\n", line); 497 (void) close(fd); 498 return (1); 499 } 500 501 /* 502 * Check that the line refers to a streams device 503 */ 504 if (isastream(fd) != 1) { 505 dprintf("Bad line (isastream failed) = %s\n", line); 506 (void) close(fd); 507 return (1); 508 } 509 510 /* 511 * if isatty(3C) failed above we assume that the ptem module has 512 * been popped already and that caused the failure, so we push it 513 * and try again 514 */ 515 if (ioctl(fd, I_PUSH, "ptem") == -1) { 516 dprintf("Bad line (I_PUSH of \"ptem\" failed) = %s\n", line); 517 (void) close(fd); 518 return (1); 519 } 520 521 if (isatty(fd) != 1) { 522 dprintf("Bad line (isatty failed) = %s\n", line); 523 (void) close(fd); 524 return (1); 525 } 526 527 if (ioctl(fd, I_POP, 0) == -1) { 528 dprintf("Bad line (I_POP of \"ptem\" failed) = %s\n", line); 529 (void) close(fd); 530 return (1); 531 } 532 533 (void) close(fd); 534 535 if (seteuid(ROOT_UID) != 0) 536 return (1); 537 538 return (0); 539 540 } 541 542 #ifdef DEBUG 543 544 /* 545 * display_args - This code prints out invocation arguments 546 * This is helpful since the program is called with 547 * up to 15 argumments. 548 */ 549 550 static void 551 display_args(argc, argv) 552 int argc; 553 char **argv; 554 { 555 int i = 0; 556 557 while (argc--) { 558 printf("Argument #%d = %s\n", i, argv[i]); 559 i++; 560 } 561 } 562 563 fputmpx(struct utmpx *rutmpx) 564 { 565 printf("ut_user = \"%-32.32s\" \n", rutmpx->ut_user); 566 printf("ut_id = \"%-4.4s\" \n", rutmpx->ut_id); 567 printf("ut_line = \"%-32.32s\" \n", rutmpx->ut_line); 568 printf("ut_pid = \"%d\" \n", rutmpx->ut_pid); 569 printf("ut_type = \"%d\" \n", rutmpx->ut_type); 570 printf("ut_exit.e_termination = \"%d\" \n", 571 rutmpx->ut_exit.e_termination); 572 printf("ut_exit.e_exit = \"%d\" \n", rutmpx->ut_exit.e_exit); 573 } 574 575 #endif /* DEBUG */ 576