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  */
43d2117003Sdp #include <stdio.h>
44d2117003Sdp #include <sys/param.h>
45d2117003Sdp #include <sys/types.h>
46d2117003Sdp #include <sys/stat.h>
47d2117003Sdp #include <utmpx.h>
48d2117003Sdp #include <errno.h>
49d2117003Sdp #include <fcntl.h>
50d2117003Sdp #include <string.h>
51d2117003Sdp #include <stdlib.h>
52d2117003Sdp #include <unistd.h>
53d2117003Sdp #include <pwd.h>
54d2117003Sdp #include <ctype.h>
55d2117003Sdp #include <stropts.h>
56d2117003Sdp #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
main(int argc,char * argv[])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;
181*76e222fdSSumanth Naropanth 
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);
189*76e222fdSSumanth Naropanth 			break;
1907c478bd9Sstevel@tonic-gate 		}
1917c478bd9Sstevel@tonic-gate 	}
192*76e222fdSSumanth Naropanth 	(void) close(devfd);
193*76e222fdSSumanth Naropanth 	if (err) {
194*76e222fdSSumanth Naropanth 		usage();
195*76e222fdSSumanth Naropanth 		return (ILLEGAL_ARGUMENT);
196*76e222fdSSumanth Naropanth 	}
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
hex2bin(unsigned char c)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 
214d2117003Sdp 	dprintf("Bad hex character: 0x%x\n", c);
215d2117003Sdp 	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
load_utmpx_struct(struct utmpx * entryx,char * argv[])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
usage()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
check_utmpx(struct utmpx * entryx)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",
341d2117003Sdp 			    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 ||
354d2117003Sdp 	    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
bad_hostname(char * name,int len)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
check_id(char * id,char * line)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
invalid_utmpx(struct utmpx * eutmpx,struct utmpx * rutmpx)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) ||
450d2117003Sdp 	    strncmp(eutmpx->ut_id, rutmpx->ut_id, SUTMPX_ID) != 0 ||
451d2117003Sdp 	    strncmp(eutmpx->ut_user, rutmpx->ut_user, SUTMPX_USER) != 0);
4527c478bd9Sstevel@tonic-gate }
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate static int
bad_line(char * line)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
display_args(argc,argv)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 
fputmpx(struct utmpx * rutmpx)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",
578d2117003Sdp 	    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