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
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;
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
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
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
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",
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
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) ||
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
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",
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