17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*76e222fdSSumanth Naropanth * Common Development and Distribution License (the "License"). 6*76e222fdSSumanth Naropanth * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*76e222fdSSumanth Naropanth * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * utmp_update - Update the /var/adm/utmpx file 287c478bd9Sstevel@tonic-gate * 297c478bd9Sstevel@tonic-gate * As of on28, the utmp interface is obsolete, 307c478bd9Sstevel@tonic-gate * so we only handle updating the utmpx file now. 317c478bd9Sstevel@tonic-gate * The utmpx routines in libc "simulate" calls 327c478bd9Sstevel@tonic-gate * to manipulate utmp entries. 337c478bd9Sstevel@tonic-gate * 347c478bd9Sstevel@tonic-gate * This program runs set uid root on behalf of 357c478bd9Sstevel@tonic-gate * non-privileged user programs. Normal programs cannot 367c478bd9Sstevel@tonic-gate * write to /var/adm/utmpx. Non-root callers of pututxline 377c478bd9Sstevel@tonic-gate * will invoke this program to write the utmpx entry. 387c478bd9Sstevel@tonic-gate */ 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate /* 417c478bd9Sstevel@tonic-gate * Header files 427c478bd9Sstevel@tonic-gate */ 437c478bd9Sstevel@tonic-gate #include <stdio.h> 447c478bd9Sstevel@tonic-gate #include <sys/param.h> 457c478bd9Sstevel@tonic-gate #include <sys/types.h> 467c478bd9Sstevel@tonic-gate #include <sys/stat.h> 477c478bd9Sstevel@tonic-gate #include <utmpx.h> 487c478bd9Sstevel@tonic-gate #include <errno.h> 497c478bd9Sstevel@tonic-gate #include <fcntl.h> 507c478bd9Sstevel@tonic-gate #include <string.h> 517c478bd9Sstevel@tonic-gate #include <stdlib.h> 527c478bd9Sstevel@tonic-gate #include <unistd.h> 537c478bd9Sstevel@tonic-gate #include <pwd.h> 547c478bd9Sstevel@tonic-gate #include <ctype.h> 557c478bd9Sstevel@tonic-gate #include <stropts.h> 567c478bd9Sstevel@tonic-gate #include <syslog.h> 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate /* 597c478bd9Sstevel@tonic-gate * Invocation argument definitions 607c478bd9Sstevel@tonic-gate */ 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate #define UTMPX_NARGS 14 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate /* 657c478bd9Sstevel@tonic-gate * Return codes 667c478bd9Sstevel@tonic-gate */ 677c478bd9Sstevel@tonic-gate #define NORMAL_EXIT 0 687c478bd9Sstevel@tonic-gate #define BAD_ARGS 1 697c478bd9Sstevel@tonic-gate #define PUTUTXLINE_FAILURE 2 707c478bd9Sstevel@tonic-gate #define FORK_FAILURE 3 717c478bd9Sstevel@tonic-gate #define SETSID_FAILURE 4 727c478bd9Sstevel@tonic-gate #define ALREADY_DEAD 5 737c478bd9Sstevel@tonic-gate #define ENTRY_NOTFOUND 6 747c478bd9Sstevel@tonic-gate #define ILLEGAL_ARGUMENT 7 75*76e222fdSSumanth Naropanth #define DEVICE_ERROR 8 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate /* 787c478bd9Sstevel@tonic-gate * Sizes 797c478bd9Sstevel@tonic-gate */ 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate #define MAX_SYSLEN 257 /* From utmpx.h host length + nul */ 827c478bd9Sstevel@tonic-gate #define BUF_SIZE 256 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate /* 857c478bd9Sstevel@tonic-gate * Other defines 867c478bd9Sstevel@tonic-gate */ 877c478bd9Sstevel@tonic-gate #define ROOT_UID 0 887c478bd9Sstevel@tonic-gate /* 897c478bd9Sstevel@tonic-gate * Debugging support 907c478bd9Sstevel@tonic-gate */ 917c478bd9Sstevel@tonic-gate #ifdef DEBUG 927c478bd9Sstevel@tonic-gate #define dprintf printf 937c478bd9Sstevel@tonic-gate #define dprintf3 printf 947c478bd9Sstevel@tonic-gate static void display_args(); 957c478bd9Sstevel@tonic-gate #else /* DEBUG */ 967c478bd9Sstevel@tonic-gate #define dprintf(x, y) 977c478bd9Sstevel@tonic-gate #define dprintf3(w, x, y, z) 987c478bd9Sstevel@tonic-gate #endif 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate /* 1017c478bd9Sstevel@tonic-gate * Local functions 1027c478bd9Sstevel@tonic-gate */ 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate static void load_utmpx_struct(struct utmpx *, char **); 1057c478bd9Sstevel@tonic-gate static void usage(void); 1067c478bd9Sstevel@tonic-gate static void check_utmpx(struct utmpx *); 1077c478bd9Sstevel@tonic-gate static int bad_hostname(char *, int); 1087c478bd9Sstevel@tonic-gate static int hex2bin(unsigned char); 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gate static int invalid_utmpx(struct utmpx *, struct utmpx *); 1117c478bd9Sstevel@tonic-gate static int bad_line(char *); 1127c478bd9Sstevel@tonic-gate static void check_id(char *, char *); 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate int 1157c478bd9Sstevel@tonic-gate main(int argc, char *argv[]) 1167c478bd9Sstevel@tonic-gate { 117*76e222fdSSumanth Naropanth int devfd, err; 1187c478bd9Sstevel@tonic-gate struct utmpx *rutmpx; 1197c478bd9Sstevel@tonic-gate struct utmpx entryx; 120*76e222fdSSumanth Naropanth struct stat stat_arg, stat_db; 1217c478bd9Sstevel@tonic-gate #ifdef DEBUG 1227c478bd9Sstevel@tonic-gate int debugger = 1; 1237c478bd9Sstevel@tonic-gate printf("%d\n", getpid()); 1247c478bd9Sstevel@tonic-gate /* Uncomment the following for attaching with dbx(1) */ 1257c478bd9Sstevel@tonic-gate /* while (debugger) ; */ 1267c478bd9Sstevel@tonic-gate display_args(argc, argv); 1277c478bd9Sstevel@tonic-gate #endif /* DEBUG */ 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate /* 1307c478bd9Sstevel@tonic-gate * We will always be called by pututxline, so simply 1317c478bd9Sstevel@tonic-gate * verify the correct number of args 1327c478bd9Sstevel@tonic-gate */ 1337c478bd9Sstevel@tonic-gate 1347c478bd9Sstevel@tonic-gate if (argc != UTMPX_NARGS) { 1357c478bd9Sstevel@tonic-gate usage(); 136d2117003Sdp return (BAD_ARGS); 1377c478bd9Sstevel@tonic-gate } 1387c478bd9Sstevel@tonic-gate /* 1397c478bd9Sstevel@tonic-gate * we should never be called by root the code in libc already 1407c478bd9Sstevel@tonic-gate * updates the file for root so no need to do it here. This 1417c478bd9Sstevel@tonic-gate * assumption simpilfies the rest of code since we nolonger 1427c478bd9Sstevel@tonic-gate * have to do special processing for the case when we are called 1437c478bd9Sstevel@tonic-gate * by root 1447c478bd9Sstevel@tonic-gate * 1457c478bd9Sstevel@tonic-gate */ 1467c478bd9Sstevel@tonic-gate if (getuid() == ROOT_UID) { 1477c478bd9Sstevel@tonic-gate usage(); 148d2117003Sdp return (ILLEGAL_ARGUMENT); 1497c478bd9Sstevel@tonic-gate } 1507c478bd9Sstevel@tonic-gate /* 1517c478bd9Sstevel@tonic-gate * Search for matching entry by line name before put operation 1527c478bd9Sstevel@tonic-gate * (scan over the whole file using getutxent(3C) to ensure 1537c478bd9Sstevel@tonic-gate * that the line name is the same. We can not use getutline(3C) 1547c478bd9Sstevel@tonic-gate * because that will return LOGIN_PROCESS and USER_PROCESS 1557c478bd9Sstevel@tonic-gate * records. Also check that the entry is for either a dead 1567c478bd9Sstevel@tonic-gate * process or a current process that is valid (see 1577c478bd9Sstevel@tonic-gate * invalid_utmpx() for details of validation criteria). 158*76e222fdSSumanth Naropanth * 159*76e222fdSSumanth Naropanth * Match entries using the inode number of the device file. 1607c478bd9Sstevel@tonic-gate */ 1617c478bd9Sstevel@tonic-gate load_utmpx_struct(&entryx, argv); 1627c478bd9Sstevel@tonic-gate check_utmpx(&entryx); 163*76e222fdSSumanth Naropanth if ((devfd = open("/dev", O_RDONLY)) < 0) { 164*76e222fdSSumanth Naropanth usage(); 165*76e222fdSSumanth Naropanth return (DEVICE_ERROR); 166*76e222fdSSumanth Naropanth } 167*76e222fdSSumanth Naropanth 168*76e222fdSSumanth Naropanth if (fstatat(devfd, entryx.ut_line, &stat_arg, 0) < 0) { 169*76e222fdSSumanth Naropanth (void) close(devfd); 170*76e222fdSSumanth Naropanth usage(); 171*76e222fdSSumanth Naropanth return (DEVICE_ERROR); 172*76e222fdSSumanth Naropanth } 173*76e222fdSSumanth Naropanth 174*76e222fdSSumanth Naropanth err = 0; 1757c478bd9Sstevel@tonic-gate for (rutmpx = getutxent(); rutmpx != (struct utmpx *)NULL; 1767c478bd9Sstevel@tonic-gate rutmpx = getutxent()) { 1777c478bd9Sstevel@tonic-gate 178*76e222fdSSumanth Naropanth if ((rutmpx->ut_type != USER_PROCESS) && 179*76e222fdSSumanth Naropanth (rutmpx->ut_type != DEAD_PROCESS)) 180*76e222fdSSumanth Naropanth continue; 1817c478bd9Sstevel@tonic-gate 182*76e222fdSSumanth Naropanth if (fstatat(devfd, rutmpx->ut_line, &stat_db, 0) < 0) 183*76e222fdSSumanth Naropanth continue; 184*76e222fdSSumanth Naropanth 185*76e222fdSSumanth Naropanth if (stat_arg.st_ino == stat_db.st_ino && 186*76e222fdSSumanth Naropanth stat_arg.st_dev == stat_db.st_dev) { 187*76e222fdSSumanth Naropanth if (rutmpx->ut_type == USER_PROCESS) 188*76e222fdSSumanth Naropanth err = invalid_utmpx(&entryx, rutmpx); 1897c478bd9Sstevel@tonic-gate break; 1907c478bd9Sstevel@tonic-gate } 191*76e222fdSSumanth Naropanth } 192*76e222fdSSumanth Naropanth (void) close(devfd); 193*76e222fdSSumanth Naropanth if (err) { 1947c478bd9Sstevel@tonic-gate usage(); 195d2117003Sdp return (ILLEGAL_ARGUMENT); 1967c478bd9Sstevel@tonic-gate } 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate if (pututxline(&entryx) == (struct utmpx *)NULL) { 199d2117003Sdp return (PUTUTXLINE_FAILURE); 2007c478bd9Sstevel@tonic-gate } 201d2117003Sdp return (NORMAL_EXIT); 2027c478bd9Sstevel@tonic-gate } 2037c478bd9Sstevel@tonic-gate 2047c478bd9Sstevel@tonic-gate static int 2057c478bd9Sstevel@tonic-gate hex2bin(unsigned char c) 2067c478bd9Sstevel@tonic-gate { 2077c478bd9Sstevel@tonic-gate if ('0' <= c && c <= '9') 2087c478bd9Sstevel@tonic-gate return (c - '0'); 2097c478bd9Sstevel@tonic-gate else if ('A' <= c && c <= 'F') 2107c478bd9Sstevel@tonic-gate return (10 + c - 'A'); 2117c478bd9Sstevel@tonic-gate else if ('a' <= c && c <= 'f') 2127c478bd9Sstevel@tonic-gate return (10 + c - 'a'); 213d2117003Sdp 2147c478bd9Sstevel@tonic-gate dprintf("Bad hex character: 0x%x\n", c); 2157c478bd9Sstevel@tonic-gate exit(ILLEGAL_ARGUMENT); 216d2117003Sdp /* NOTREACHED */ 2177c478bd9Sstevel@tonic-gate } 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate /* 2217c478bd9Sstevel@tonic-gate * load_utmpx_struct - Load up the utmpx structure with information supplied 2227c478bd9Sstevel@tonic-gate * as arguments in argv. 2237c478bd9Sstevel@tonic-gate */ 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate static void 2267c478bd9Sstevel@tonic-gate load_utmpx_struct(struct utmpx *entryx, char *argv[]) 2277c478bd9Sstevel@tonic-gate { 2287c478bd9Sstevel@tonic-gate char *user, *id, *line, *pid, *type, *term, *time_usec, 2297c478bd9Sstevel@tonic-gate *exitstatus, *xtime, *session, *pad, *syslen, *host; 2307c478bd9Sstevel@tonic-gate int temp, i; 2317c478bd9Sstevel@tonic-gate unsigned char *cp; 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate (void) memset(entryx, 0, sizeof (struct utmpx)); 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate user = argv[1]; 2367c478bd9Sstevel@tonic-gate id = argv[2]; 2377c478bd9Sstevel@tonic-gate line = argv[3]; 2387c478bd9Sstevel@tonic-gate pid = argv[4]; 2397c478bd9Sstevel@tonic-gate type = argv[5]; 2407c478bd9Sstevel@tonic-gate term = argv[6]; 2417c478bd9Sstevel@tonic-gate exitstatus = argv[7]; 2427c478bd9Sstevel@tonic-gate xtime = argv[8]; 2437c478bd9Sstevel@tonic-gate time_usec = argv[9 ]; 2447c478bd9Sstevel@tonic-gate session = argv[10]; 2457c478bd9Sstevel@tonic-gate pad = argv[11]; 2467c478bd9Sstevel@tonic-gate syslen = argv[12]; 2477c478bd9Sstevel@tonic-gate host = argv[13]; 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate (void) strncpy(entryx->ut_user, user, sizeof (entryx->ut_user)); 2507c478bd9Sstevel@tonic-gate (void) strncpy(entryx->ut_id, id, sizeof (entryx->ut_id)); 2517c478bd9Sstevel@tonic-gate (void) strncpy(entryx->ut_line, line, sizeof (entryx->ut_line)); 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate (void) sscanf(pid, "%d", &temp); 2547c478bd9Sstevel@tonic-gate entryx->ut_pid = temp; 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate (void) sscanf(type, "%d", &temp); 2577c478bd9Sstevel@tonic-gate entryx->ut_type = temp; 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate (void) sscanf(term, "%d", &temp); 2607c478bd9Sstevel@tonic-gate entryx->ut_exit.e_termination = temp; 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate (void) sscanf(exitstatus, "%d", &temp); 2637c478bd9Sstevel@tonic-gate entryx->ut_exit.e_exit = temp; 2647c478bd9Sstevel@tonic-gate /* 2657c478bd9Sstevel@tonic-gate * Here's where we stamp the exit field of a USER_PROCESS 2667c478bd9Sstevel@tonic-gate * record so that we know it was written by a normal user. 2677c478bd9Sstevel@tonic-gate */ 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate if (entryx->ut_type == USER_PROCESS) 2707c478bd9Sstevel@tonic-gate setuserx(*entryx); 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate (void) sscanf(xtime, "%d", &temp); 2737c478bd9Sstevel@tonic-gate entryx->ut_tv.tv_sec = temp; 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate (void) sscanf(time_usec, "%d", &temp); 2767c478bd9Sstevel@tonic-gate entryx->ut_tv.tv_usec = temp; 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate (void) sscanf(session, "%d", &temp); 2797c478bd9Sstevel@tonic-gate entryx->ut_session = temp; 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate temp = strlen(pad); 2827c478bd9Sstevel@tonic-gate cp = (unsigned char *)entryx->pad; 2837c478bd9Sstevel@tonic-gate for (i = 0; i < temp && (i>>1) < sizeof (entryx->pad); i += 2) 2847c478bd9Sstevel@tonic-gate cp[i>>1] = hex2bin(pad[i]) << 4 | hex2bin(pad[i+1]); 2857c478bd9Sstevel@tonic-gate 2867c478bd9Sstevel@tonic-gate (void) sscanf(syslen, "%d", &temp); 2877c478bd9Sstevel@tonic-gate entryx->ut_syslen = temp; 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate (void) strlcpy(entryx->ut_host, host, sizeof (entryx->ut_host)); 2907c478bd9Sstevel@tonic-gate } 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate /* 2937c478bd9Sstevel@tonic-gate * usage - There's no need to say more. This program isn't supposed to 2947c478bd9Sstevel@tonic-gate * be executed by normal users directly. 2957c478bd9Sstevel@tonic-gate */ 2967c478bd9Sstevel@tonic-gate 2977c478bd9Sstevel@tonic-gate static void 2987c478bd9Sstevel@tonic-gate usage() 2997c478bd9Sstevel@tonic-gate { 3007c478bd9Sstevel@tonic-gate syslog(LOG_ERR, "Wrong number of arguments or invalid user \n"); 3017c478bd9Sstevel@tonic-gate } 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate /* 3047c478bd9Sstevel@tonic-gate * check_utmpx - Verify the utmpx structure 3057c478bd9Sstevel@tonic-gate */ 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate static void 3087c478bd9Sstevel@tonic-gate check_utmpx(struct utmpx *entryx) 3097c478bd9Sstevel@tonic-gate { 3107c478bd9Sstevel@tonic-gate char buf[BUF_SIZE]; 3117c478bd9Sstevel@tonic-gate char *line = buf; 3127c478bd9Sstevel@tonic-gate struct passwd *pwd; 3137c478bd9Sstevel@tonic-gate int uid; 3147c478bd9Sstevel@tonic-gate int hostlen; 3157c478bd9Sstevel@tonic-gate char *user; 3167c478bd9Sstevel@tonic-gate uid_t ruid = getuid(); 3177c478bd9Sstevel@tonic-gate 3187c478bd9Sstevel@tonic-gate (void) memset(buf, 0, BUF_SIZE); 3197c478bd9Sstevel@tonic-gate user = malloc(sizeof (entryx->ut_user) +1); 3207c478bd9Sstevel@tonic-gate (void) strncpy(user, entryx->ut_user, sizeof (entryx->ut_user)); 3217c478bd9Sstevel@tonic-gate user[sizeof (entryx->ut_user)] = '\0'; 3227c478bd9Sstevel@tonic-gate pwd = getpwnam(user); 3237c478bd9Sstevel@tonic-gate (void) free(user); 3247c478bd9Sstevel@tonic-gate 3257c478bd9Sstevel@tonic-gate (void) strlcat(strcpy(buf, "/dev/"), entryx->ut_line, sizeof (buf)); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate if (pwd != (struct passwd *)NULL) { 3287c478bd9Sstevel@tonic-gate uid = pwd->pw_uid; 3297c478bd9Sstevel@tonic-gate /* 3307c478bd9Sstevel@tonic-gate * We nolonger permit the UID of the caller to be different 3317c478bd9Sstevel@tonic-gate * the UID to be written to the utmp file. This was thought 3327c478bd9Sstevel@tonic-gate * necessary to allow the utmp file to be updated when 3337c478bd9Sstevel@tonic-gate * logging out from an xterm(1) window after running 3347c478bd9Sstevel@tonic-gate * exec login. Instead we now rely upon utmpd(1) to update 3357c478bd9Sstevel@tonic-gate * the utmp file for us. 3367c478bd9Sstevel@tonic-gate * 3377c478bd9Sstevel@tonic-gate */ 3387c478bd9Sstevel@tonic-gate 3397c478bd9Sstevel@tonic-gate if (ruid != uid) { 3407c478bd9Sstevel@tonic-gate dprintf3("Bad uid: user %s = %d uid = %d \n", 3417c478bd9Sstevel@tonic-gate entryx->ut_user, uid, getuid()); 3427c478bd9Sstevel@tonic-gate exit(ILLEGAL_ARGUMENT); 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate } else if (entryx->ut_type != DEAD_PROCESS) { 3467c478bd9Sstevel@tonic-gate dprintf("Bad user name: %s \n", entryx->ut_user); 3477c478bd9Sstevel@tonic-gate exit(ILLEGAL_ARGUMENT); 3487c478bd9Sstevel@tonic-gate } 3497c478bd9Sstevel@tonic-gate 3507c478bd9Sstevel@tonic-gate /* 3517c478bd9Sstevel@tonic-gate * Only USER_PROCESS and DEAD_PROCESS entries may be updated 3527c478bd9Sstevel@tonic-gate */ 3537c478bd9Sstevel@tonic-gate if (!(entryx->ut_type == USER_PROCESS || 3547c478bd9Sstevel@tonic-gate entryx->ut_type == DEAD_PROCESS)) { 3557c478bd9Sstevel@tonic-gate dprintf("Bad type type = %d\n", entryx->ut_type); 3567c478bd9Sstevel@tonic-gate exit(ILLEGAL_ARGUMENT); 3577c478bd9Sstevel@tonic-gate } 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate /* 3607c478bd9Sstevel@tonic-gate * Verify that the pid of the entry field is the same pid as our 3617c478bd9Sstevel@tonic-gate * parent, who should be the guy writing the entry. This is commented 3627c478bd9Sstevel@tonic-gate * out for now because this restriction is overkill. 3637c478bd9Sstevel@tonic-gate */ 3647c478bd9Sstevel@tonic-gate #ifdef VERIFY_PID 3657c478bd9Sstevel@tonic-gate if (entryx->ut_type == USER_PROCESS && entryx->ut_pid != getppid()) { 3667c478bd9Sstevel@tonic-gate dprintf("Bad pid = %d\n", entryx->ut_pid); 3677c478bd9Sstevel@tonic-gate exit(ILLEGAL_ARGUMENT); 3687c478bd9Sstevel@tonic-gate } 3697c478bd9Sstevel@tonic-gate #endif /* VERIFY_PID */ 3707c478bd9Sstevel@tonic-gate 3717c478bd9Sstevel@tonic-gate if (bad_line(line) == 1) { 3727c478bd9Sstevel@tonic-gate dprintf("Bad line = %s\n", line); 3737c478bd9Sstevel@tonic-gate exit(ILLEGAL_ARGUMENT); 3747c478bd9Sstevel@tonic-gate } 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate hostlen = strlen(entryx->ut_host) + 1; 3777c478bd9Sstevel@tonic-gate if (entryx->ut_syslen != hostlen) { 3787c478bd9Sstevel@tonic-gate dprintf3("Bad syslen of \"%s\" = %d - correcting to %d\n", 3797c478bd9Sstevel@tonic-gate entryx->ut_host, entryx->ut_syslen, hostlen); 3807c478bd9Sstevel@tonic-gate entryx->ut_syslen = hostlen; 3817c478bd9Sstevel@tonic-gate } 3827c478bd9Sstevel@tonic-gate 3837c478bd9Sstevel@tonic-gate if (bad_hostname(entryx->ut_host, entryx->ut_syslen) == 1) { 3847c478bd9Sstevel@tonic-gate dprintf("Bad hostname name = %s\n", entryx->ut_host); 3857c478bd9Sstevel@tonic-gate exit(ILLEGAL_ARGUMENT); 3867c478bd9Sstevel@tonic-gate } 3877c478bd9Sstevel@tonic-gate check_id(entryx->ut_id, entryx->ut_line); 3887c478bd9Sstevel@tonic-gate } 3897c478bd9Sstevel@tonic-gate 3907c478bd9Sstevel@tonic-gate /* 3917c478bd9Sstevel@tonic-gate * bad_hostname - Previously returned an error if a non alpha numeric 3927c478bd9Sstevel@tonic-gate * was in the host field, but now just clears those so 3937c478bd9Sstevel@tonic-gate * cmdtool entries will work. 3947c478bd9Sstevel@tonic-gate */ 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate static int 3977c478bd9Sstevel@tonic-gate bad_hostname(char *name, int len) 3987c478bd9Sstevel@tonic-gate { 3997c478bd9Sstevel@tonic-gate int i; 4007c478bd9Sstevel@tonic-gate 4017c478bd9Sstevel@tonic-gate if (len < 0 || len > MAX_SYSLEN) 4027c478bd9Sstevel@tonic-gate return (1); 4037c478bd9Sstevel@tonic-gate /* 4047c478bd9Sstevel@tonic-gate * Scan for non-alpha numerics 4057c478bd9Sstevel@tonic-gate * Per utmpx.h, len includes the nul character. 4067c478bd9Sstevel@tonic-gate */ 4077c478bd9Sstevel@tonic-gate for (i = 0; i < len; i++) 4087c478bd9Sstevel@tonic-gate if (name[i] != '\0' && isprint(name[i]) == 0) 4097c478bd9Sstevel@tonic-gate name[i] = ' '; 4107c478bd9Sstevel@tonic-gate return (0); 4117c478bd9Sstevel@tonic-gate } 4127c478bd9Sstevel@tonic-gate 4137c478bd9Sstevel@tonic-gate /* 4147c478bd9Sstevel@tonic-gate * Workaround until the window system gets fixed. Look for id's with 4157c478bd9Sstevel@tonic-gate * a '/' in them. That means they are probably from libxview. 4167c478bd9Sstevel@tonic-gate * Then create a new id that is unique using the last 4 chars in the line. 4177c478bd9Sstevel@tonic-gate */ 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate static void 4207c478bd9Sstevel@tonic-gate check_id(char *id, char *line) 4217c478bd9Sstevel@tonic-gate { 4227c478bd9Sstevel@tonic-gate int i, len; 4237c478bd9Sstevel@tonic-gate 4247c478bd9Sstevel@tonic-gate if (id[1] == '/' && id[2] == 's' && id[3] == 't') { 4257c478bd9Sstevel@tonic-gate len = strlen(line); 4267c478bd9Sstevel@tonic-gate if (len > 0) 4277c478bd9Sstevel@tonic-gate len--; 4287c478bd9Sstevel@tonic-gate for (i = 0; i < 4; i++) 4297c478bd9Sstevel@tonic-gate id[i] = len - i < 0 ? 0 : line[len-i]; 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate } 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate 4347c478bd9Sstevel@tonic-gate /* 4357c478bd9Sstevel@tonic-gate * The function invalid_utmpx() enforces the requirement that the record 4367c478bd9Sstevel@tonic-gate * being updating in the utmpx file can not have been created by login(1) 4377c478bd9Sstevel@tonic-gate * or friends. Also that the id and username of the record to be written match 4387c478bd9Sstevel@tonic-gate * those found in the utmpx file. We need this both for security and to ensure 4397c478bd9Sstevel@tonic-gate * that pututxline(3C) will NOT reposition the file pointer in the utmpx file, 4407c478bd9Sstevel@tonic-gate * so that the record is updated in place. 4417c478bd9Sstevel@tonic-gate * 4427c478bd9Sstevel@tonic-gate */ 4437c478bd9Sstevel@tonic-gate static int 4447c478bd9Sstevel@tonic-gate invalid_utmpx(struct utmpx *eutmpx, struct utmpx *rutmpx) 4457c478bd9Sstevel@tonic-gate { 4467c478bd9Sstevel@tonic-gate #define SUTMPX_ID (sizeof (eutmpx->ut_id)) 4477c478bd9Sstevel@tonic-gate #define SUTMPX_USER (sizeof (eutmpx->ut_user)) 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate return (!nonuserx(*rutmpx) || 4507c478bd9Sstevel@tonic-gate strncmp(eutmpx->ut_id, rutmpx->ut_id, SUTMPX_ID) != 0 || 4517c478bd9Sstevel@tonic-gate strncmp(eutmpx->ut_user, rutmpx->ut_user, SUTMPX_USER) != 0); 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate static int 4557c478bd9Sstevel@tonic-gate bad_line(char *line) 4567c478bd9Sstevel@tonic-gate { 4577c478bd9Sstevel@tonic-gate struct stat statbuf; 4587c478bd9Sstevel@tonic-gate int fd; 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate /* 4617c478bd9Sstevel@tonic-gate * The line field must be a device file that we can write to, 4627c478bd9Sstevel@tonic-gate * it should live under /dev which is enforced by requiring 4637c478bd9Sstevel@tonic-gate * its name not to contain "../" and opening it as the user for 4647c478bd9Sstevel@tonic-gate * writing. 4657c478bd9Sstevel@tonic-gate */ 4667c478bd9Sstevel@tonic-gate if (strstr(line, "../") != 0) { 4677c478bd9Sstevel@tonic-gate dprintf("Bad line = %s\n", line); 4687c478bd9Sstevel@tonic-gate return (1); 4697c478bd9Sstevel@tonic-gate } 4707c478bd9Sstevel@tonic-gate 4717c478bd9Sstevel@tonic-gate /* 4727c478bd9Sstevel@tonic-gate * It has to be a tty. It can't be a bogus file, e.g. ../tmp/bogus. 4737c478bd9Sstevel@tonic-gate */ 4747c478bd9Sstevel@tonic-gate if (seteuid(getuid()) != 0) 4757c478bd9Sstevel@tonic-gate return (1); 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate /* 4787c478bd9Sstevel@tonic-gate * We need to open the line without blocking so that it does not hang 4797c478bd9Sstevel@tonic-gate */ 4807c478bd9Sstevel@tonic-gate if ((fd = open(line, O_WRONLY|O_NOCTTY|O_NONBLOCK)) == -1) { 4817c478bd9Sstevel@tonic-gate dprintf("Bad line (Can't open/write) = %s\n", line); 4827c478bd9Sstevel@tonic-gate return (1); 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /* 4867c478bd9Sstevel@tonic-gate * Check that fd is a tty, if this fails all is not lost see below 4877c478bd9Sstevel@tonic-gate */ 4887c478bd9Sstevel@tonic-gate if (isatty(fd) == 1) { 4897c478bd9Sstevel@tonic-gate /* 4907c478bd9Sstevel@tonic-gate * It really is a tty, so return success 4917c478bd9Sstevel@tonic-gate */ 492d2117003Sdp (void) close(fd); 4937c478bd9Sstevel@tonic-gate if (seteuid(ROOT_UID) != 0) 4947c478bd9Sstevel@tonic-gate return (1); 4957c478bd9Sstevel@tonic-gate return (0); 4967c478bd9Sstevel@tonic-gate } 4977c478bd9Sstevel@tonic-gate 4987c478bd9Sstevel@tonic-gate /* 4997c478bd9Sstevel@tonic-gate * Check that the line refers to a character 5004bc0a2efScasper * special device. 5017c478bd9Sstevel@tonic-gate */ 5024bc0a2efScasper if ((fstat(fd, &statbuf) < 0) || !S_ISCHR(statbuf.st_mode)) { 5037c478bd9Sstevel@tonic-gate dprintf("Bad line (fstat failed) (Not S_IFCHR) = %s\n", line); 504d2117003Sdp (void) close(fd); 5057c478bd9Sstevel@tonic-gate return (1); 5067c478bd9Sstevel@tonic-gate } 5077c478bd9Sstevel@tonic-gate 5087c478bd9Sstevel@tonic-gate /* 5097c478bd9Sstevel@tonic-gate * Check that the line refers to a streams device 5107c478bd9Sstevel@tonic-gate */ 5117c478bd9Sstevel@tonic-gate if (isastream(fd) != 1) { 5127c478bd9Sstevel@tonic-gate dprintf("Bad line (isastream failed) = %s\n", line); 513d2117003Sdp (void) close(fd); 5147c478bd9Sstevel@tonic-gate return (1); 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate /* 5187c478bd9Sstevel@tonic-gate * if isatty(3C) failed above we assume that the ptem module has 5197c478bd9Sstevel@tonic-gate * been popped already and that caused the failure, so we push it 5207c478bd9Sstevel@tonic-gate * and try again 5217c478bd9Sstevel@tonic-gate */ 5227c478bd9Sstevel@tonic-gate if (ioctl(fd, I_PUSH, "ptem") == -1) { 5237c478bd9Sstevel@tonic-gate dprintf("Bad line (I_PUSH of \"ptem\" failed) = %s\n", line); 524d2117003Sdp (void) close(fd); 5257c478bd9Sstevel@tonic-gate return (1); 5267c478bd9Sstevel@tonic-gate } 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate if (isatty(fd) != 1) { 5297c478bd9Sstevel@tonic-gate dprintf("Bad line (isatty failed) = %s\n", line); 530d2117003Sdp (void) close(fd); 5317c478bd9Sstevel@tonic-gate return (1); 5327c478bd9Sstevel@tonic-gate } 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate if (ioctl(fd, I_POP, 0) == -1) { 5357c478bd9Sstevel@tonic-gate dprintf("Bad line (I_POP of \"ptem\" failed) = %s\n", line); 536d2117003Sdp (void) close(fd); 5377c478bd9Sstevel@tonic-gate return (1); 5387c478bd9Sstevel@tonic-gate } 5397c478bd9Sstevel@tonic-gate 540d2117003Sdp (void) close(fd); 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate if (seteuid(ROOT_UID) != 0) 5437c478bd9Sstevel@tonic-gate return (1); 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate return (0); 5467c478bd9Sstevel@tonic-gate 5477c478bd9Sstevel@tonic-gate } 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate #ifdef DEBUG 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate /* 5527c478bd9Sstevel@tonic-gate * display_args - This code prints out invocation arguments 5537c478bd9Sstevel@tonic-gate * This is helpful since the program is called with 5547c478bd9Sstevel@tonic-gate * up to 15 argumments. 5557c478bd9Sstevel@tonic-gate */ 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate static void 5587c478bd9Sstevel@tonic-gate display_args(argc, argv) 5597c478bd9Sstevel@tonic-gate int argc; 5607c478bd9Sstevel@tonic-gate char **argv; 5617c478bd9Sstevel@tonic-gate { 5627c478bd9Sstevel@tonic-gate int i = 0; 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate while (argc--) { 5657c478bd9Sstevel@tonic-gate printf("Argument #%d = %s\n", i, argv[i]); 5667c478bd9Sstevel@tonic-gate i++; 5677c478bd9Sstevel@tonic-gate } 5687c478bd9Sstevel@tonic-gate } 5697c478bd9Sstevel@tonic-gate 5707c478bd9Sstevel@tonic-gate fputmpx(struct utmpx *rutmpx) 5717c478bd9Sstevel@tonic-gate { 5727c478bd9Sstevel@tonic-gate printf("ut_user = \"%-32.32s\" \n", rutmpx->ut_user); 5737c478bd9Sstevel@tonic-gate printf("ut_id = \"%-4.4s\" \n", rutmpx->ut_id); 5747c478bd9Sstevel@tonic-gate printf("ut_line = \"%-32.32s\" \n", rutmpx->ut_line); 5757c478bd9Sstevel@tonic-gate printf("ut_pid = \"%d\" \n", rutmpx->ut_pid); 5767c478bd9Sstevel@tonic-gate printf("ut_type = \"%d\" \n", rutmpx->ut_type); 5777c478bd9Sstevel@tonic-gate printf("ut_exit.e_termination = \"%d\" \n", 5787c478bd9Sstevel@tonic-gate rutmpx->ut_exit.e_termination); 5797c478bd9Sstevel@tonic-gate printf("ut_exit.e_exit = \"%d\" \n", rutmpx->ut_exit.e_exit); 5807c478bd9Sstevel@tonic-gate } 5817c478bd9Sstevel@tonic-gate 582d2117003Sdp #endif /* DEBUG */ 583