utmp_update.c revision d2117003c7d0588abeea5ed1b925b77f025e2c96
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * CDDL HEADER START
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * The contents of this file are subject to the terms of the
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Common Development and Distribution License, Version 1.0 only
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * (the "License"). You may not use this file except in compliance
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * with the License.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * or http://www.opensolaris.org/os/licensing.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * See the License for the specific language governing permissions
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * and limitations under the License.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * When distributing Covered Code, include this CDDL HEADER in each
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * If applicable, add the following below this CDDL HEADER, with the
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * fields enclosed by brackets "[]" replaced with your own identifying
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * information: Portions Copyright [yyyy] [name of copyright owner]
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * CDDL HEADER END
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Use is subject to license terms.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#pragma ident "%Z%%M% %I% %E% SMI"
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * utmp_update - Update the /var/adm/utmpx file
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * As of on28, the utmp interface is obsolete,
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * so we only handle updating the utmpx file now.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * The utmpx routines in libc "simulate" calls
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * to manipulate utmp entries.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * This program runs set uid root on behalf of
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * non-privileged user programs. Normal programs cannot
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * write to /var/adm/utmpx. Non-root callers of pututxline
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * will invoke this program to write the utmpx entry.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Header files
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <stdio.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <sys/param.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <sys/types.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <sys/stat.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <utmpx.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <errno.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <fcntl.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <string.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <stdlib.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <unistd.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <pwd.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <ctype.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <stropts.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#include <syslog.h>
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Invocation argument definitions
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define UTMPX_NARGS 14
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Return codes
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define NORMAL_EXIT 0
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define BAD_ARGS 1
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define PUTUTXLINE_FAILURE 2
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define FORK_FAILURE 3
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define SETSID_FAILURE 4
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define ALREADY_DEAD 5
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define ENTRY_NOTFOUND 6
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define ILLEGAL_ARGUMENT 7
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Sizes
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define MAX_SYSLEN 257 /* From utmpx.h host length + nul */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define BUF_SIZE 256
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Other defines
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define ROOT_UID 0
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Debugging support
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#ifdef DEBUG
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define dprintf printf
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define dprintf3 printf
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic void display_args();
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#else /* DEBUG */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define dprintf(x, y)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#define dprintf3(w, x, y, z)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#endif
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Local functions
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic void load_utmpx_struct(struct utmpx *, char **);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic void usage(void);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic void check_utmpx(struct utmpx *);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic int bad_hostname(char *, int);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic int hex2bin(unsigned char);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic int invalid_utmpx(struct utmpx *, struct utmpx *);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic int bad_line(char *);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic void check_id(char *, char *);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinint
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinmain(int argc, char *argv[])
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin{
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin struct utmpx *rutmpx;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin struct utmpx entryx;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#ifdef DEBUG
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin int debugger = 1;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin printf("%d\n", getpid());
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* Uncomment the following for attaching with dbx(1) */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* while (debugger) ; */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin display_args(argc, argv);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin#endif /* DEBUG */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * We will always be called by pututxline, so simply
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * verify the correct number of args
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if (argc != UTMPX_NARGS) {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin usage();
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin return (BAD_ARGS);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin }
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * we should never be called by root the code in libc already
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * updates the file for root so no need to do it here. This
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * assumption simpilfies the rest of code since we nolonger
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * have to do special processing for the case when we are called
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * by root
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin *
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if (getuid() == ROOT_UID) {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin usage();
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin return (ILLEGAL_ARGUMENT);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin }
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * Search for matching entry by line name before put operation
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * (scan over the whole file using getutxent(3C) to ensure
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * that the line name is the same. We can not use getutline(3C)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * because that will return LOGIN_PROCESS and USER_PROCESS
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * records. Also check that the entry is for either a dead
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * process or a current process that is valid (see
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * invalid_utmpx() for details of validation criteria).
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin load_utmpx_struct(&entryx, argv);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin check_utmpx(&entryx);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin for (rutmpx = getutxent(); rutmpx != (struct utmpx *)NULL;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin rutmpx = getutxent()) {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if (strncmp(entryx.ut_line, rutmpx->ut_line,
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin sizeof (entryx.ut_line)) == 0) {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if (rutmpx->ut_type == DEAD_PROCESS) {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin break;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin }
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if (rutmpx->ut_type == USER_PROCESS) {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if (invalid_utmpx(&entryx, rutmpx)) {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin usage();
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin return (ILLEGAL_ARGUMENT);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin } else {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin break;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin }
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin }
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin }
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin }
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if (pututxline(&entryx) == (struct utmpx *)NULL) {
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin return (PUTUTXLINE_FAILURE);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin }
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin return (NORMAL_EXIT);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin}
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic int
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinhex2bin(unsigned char c)
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin{
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin if ('0' <= c && c <= '9')
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin return (c - '0');
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if ('A' <= c && c <= 'F')
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin return (10 + c - 'A');
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin else if ('a' <= c && c <= 'f')
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin return (10 + c - 'a');
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin dprintf("Bad hex character: 0x%x\n", c);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin exit(ILLEGAL_ARGUMENT);
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin /* NOTREACHED */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin}
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin/*
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * load_utmpx_struct - Load up the utmpx structure with information supplied
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin * as arguments in argv.
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin */
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinstatic void
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chinload_utmpx_struct(struct utmpx *entryx, char *argv[])
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin{
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin char *user, *id, *line, *pid, *type, *term, *time_usec,
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin *exitstatus, *xtime, *session, *pad, *syslen, *host;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin int temp, i;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin unsigned char *cp;
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin (void) memset(entryx, 0, sizeof (struct utmpx));
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin user = argv[1];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin id = argv[2];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin line = argv[3];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin pid = argv[4];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin type = argv[5];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin term = argv[6];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin exitstatus = argv[7];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin xtime = argv[8];
da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968chin time_usec = argv[9 ];
session = argv[10];
pad = argv[11];
syslen = argv[12];
host = argv[13];
(void) strncpy(entryx->ut_user, user, sizeof (entryx->ut_user));
(void) strncpy(entryx->ut_id, id, sizeof (entryx->ut_id));
(void) strncpy(entryx->ut_line, line, sizeof (entryx->ut_line));
(void) sscanf(pid, "%d", &temp);
entryx->ut_pid = temp;
(void) sscanf(type, "%d", &temp);
entryx->ut_type = temp;
(void) sscanf(term, "%d", &temp);
entryx->ut_exit.e_termination = temp;
(void) sscanf(exitstatus, "%d", &temp);
entryx->ut_exit.e_exit = temp;
/*
* Here's where we stamp the exit field of a USER_PROCESS
* record so that we know it was written by a normal user.
*/
if (entryx->ut_type == USER_PROCESS)
setuserx(*entryx);
(void) sscanf(xtime, "%d", &temp);
entryx->ut_tv.tv_sec = temp;
(void) sscanf(time_usec, "%d", &temp);
entryx->ut_tv.tv_usec = temp;
(void) sscanf(session, "%d", &temp);
entryx->ut_session = temp;
temp = strlen(pad);
cp = (unsigned char *)entryx->pad;
for (i = 0; i < temp && (i>>1) < sizeof (entryx->pad); i += 2)
cp[i>>1] = hex2bin(pad[i]) << 4 | hex2bin(pad[i+1]);
(void) sscanf(syslen, "%d", &temp);
entryx->ut_syslen = temp;
(void) strlcpy(entryx->ut_host, host, sizeof (entryx->ut_host));
}
/*
* usage - There's no need to say more. This program isn't supposed to
* be executed by normal users directly.
*/
static void
usage()
{
syslog(LOG_ERR, "Wrong number of arguments or invalid user \n");
}
/*
* check_utmpx - Verify the utmpx structure
*/
static void
check_utmpx(struct utmpx *entryx)
{
char buf[BUF_SIZE];
char *line = buf;
struct passwd *pwd;
int uid;
int hostlen;
char *user;
uid_t ruid = getuid();
(void) memset(buf, 0, BUF_SIZE);
user = malloc(sizeof (entryx->ut_user) +1);
(void) strncpy(user, entryx->ut_user, sizeof (entryx->ut_user));
user[sizeof (entryx->ut_user)] = '\0';
pwd = getpwnam(user);
(void) free(user);
(void) strlcat(strcpy(buf, "/dev/"), entryx->ut_line, sizeof (buf));
if (pwd != (struct passwd *)NULL) {
uid = pwd->pw_uid;
/*
* We nolonger permit the UID of the caller to be different
* the UID to be written to the utmp file. This was thought
* necessary to allow the utmp file to be updated when
* logging out from an xterm(1) window after running
* exec login. Instead we now rely upon utmpd(1) to update
* the utmp file for us.
*
*/
if (ruid != uid) {
dprintf3("Bad uid: user %s = %d uid = %d \n",
entryx->ut_user, uid, getuid());
exit(ILLEGAL_ARGUMENT);
}
} else if (entryx->ut_type != DEAD_PROCESS) {
dprintf("Bad user name: %s \n", entryx->ut_user);
exit(ILLEGAL_ARGUMENT);
}
/*
* Only USER_PROCESS and DEAD_PROCESS entries may be updated
*/
if (!(entryx->ut_type == USER_PROCESS ||
entryx->ut_type == DEAD_PROCESS)) {
dprintf("Bad type type = %d\n", entryx->ut_type);
exit(ILLEGAL_ARGUMENT);
}
/*
* Verify that the pid of the entry field is the same pid as our
* parent, who should be the guy writing the entry. This is commented
* out for now because this restriction is overkill.
*/
#ifdef VERIFY_PID
if (entryx->ut_type == USER_PROCESS && entryx->ut_pid != getppid()) {
dprintf("Bad pid = %d\n", entryx->ut_pid);
exit(ILLEGAL_ARGUMENT);
}
#endif /* VERIFY_PID */
if (bad_line(line) == 1) {
dprintf("Bad line = %s\n", line);
exit(ILLEGAL_ARGUMENT);
}
hostlen = strlen(entryx->ut_host) + 1;
if (entryx->ut_syslen != hostlen) {
dprintf3("Bad syslen of \"%s\" = %d - correcting to %d\n",
entryx->ut_host, entryx->ut_syslen, hostlen);
entryx->ut_syslen = hostlen;
}
if (bad_hostname(entryx->ut_host, entryx->ut_syslen) == 1) {
dprintf("Bad hostname name = %s\n", entryx->ut_host);
exit(ILLEGAL_ARGUMENT);
}
check_id(entryx->ut_id, entryx->ut_line);
}
/*
* bad_hostname - Previously returned an error if a non alpha numeric
* was in the host field, but now just clears those so
* cmdtool entries will work.
*/
static int
bad_hostname(char *name, int len)
{
int i;
if (len < 0 || len > MAX_SYSLEN)
return (1);
/*
* Scan for non-alpha numerics
* Per utmpx.h, len includes the nul character.
*/
for (i = 0; i < len; i++)
if (name[i] != '\0' && isprint(name[i]) == 0)
name[i] = ' ';
return (0);
}
/*
* Workaround until the window system gets fixed. Look for id's with
* a '/' in them. That means they are probably from libxview.
* Then create a new id that is unique using the last 4 chars in the line.
*/
static void
check_id(char *id, char *line)
{
int i, len;
if (id[1] == '/' && id[2] == 's' && id[3] == 't') {
len = strlen(line);
if (len > 0)
len--;
for (i = 0; i < 4; i++)
id[i] = len - i < 0 ? 0 : line[len-i];
}
}
/*
* The function invalid_utmpx() enforces the requirement that the record
* being updating in the utmpx file can not have been created by login(1)
* or friends. Also that the id and username of the record to be written match
* those found in the utmpx file. We need this both for security and to ensure
* that pututxline(3C) will NOT reposition the file pointer in the utmpx file,
* so that the record is updated in place.
*
*/
static int
invalid_utmpx(struct utmpx *eutmpx, struct utmpx *rutmpx)
{
#define SUTMPX_ID (sizeof (eutmpx->ut_id))
#define SUTMPX_USER (sizeof (eutmpx->ut_user))
return (!nonuserx(*rutmpx) ||
strncmp(eutmpx->ut_id, rutmpx->ut_id, SUTMPX_ID) != 0 ||
strncmp(eutmpx->ut_user, rutmpx->ut_user, SUTMPX_USER) != 0);
}
static int
bad_line(char *line)
{
struct stat statbuf;
int fd;
/*
* The line field must be a device file that we can write to,
* it should live under /dev which is enforced by requiring
* its name not to contain "../" and opening it as the user for
* writing.
*/
if (strstr(line, "../") != 0) {
dprintf("Bad line = %s\n", line);
return (1);
}
/*
* It has to be a tty. It can't be a bogus file, e.g. ../tmp/bogus.
*/
if (seteuid(getuid()) != 0)
return (1);
/*
* Check that the line refers to a character
* special device see bugid: 1136978
*/
if ((stat(line, &statbuf) < 0) || (statbuf.st_mode & S_IFCHR) == 0) {
dprintf("Bad line (stat failed) (Not S_IFCHR) = %s\n", line);
return (1);
}
/*
* We need to open the line without blocking so that it does not hang
*/
if ((fd = open(line, O_WRONLY|O_NOCTTY|O_NONBLOCK)) == -1) {
dprintf("Bad line (Can't open/write) = %s\n", line);
return (1);
}
/*
* Check that fd is a tty, if this fails all is not lost see below
*/
if (isatty(fd) == 1) {
/*
* It really is a tty, so return success
*/
(void) close(fd);
if (seteuid(ROOT_UID) != 0)
return (1);
return (0);
}
/*
* Check that the line refers to a character
* special device see bugid: 1136978
*/
if ((fstat(fd, &statbuf) < 0) || (statbuf.st_mode & S_IFCHR) == 0) {
dprintf("Bad line (fstat failed) (Not S_IFCHR) = %s\n", line);
(void) close(fd);
return (1);
}
/*
* Check that the line refers to a streams device
*/
if (isastream(fd) != 1) {
dprintf("Bad line (isastream failed) = %s\n", line);
(void) close(fd);
return (1);
}
/*
* if isatty(3C) failed above we assume that the ptem module has
* been popped already and that caused the failure, so we push it
* and try again
*/
if (ioctl(fd, I_PUSH, "ptem") == -1) {
dprintf("Bad line (I_PUSH of \"ptem\" failed) = %s\n", line);
(void) close(fd);
return (1);
}
if (isatty(fd) != 1) {
dprintf("Bad line (isatty failed) = %s\n", line);
(void) close(fd);
return (1);
}
if (ioctl(fd, I_POP, 0) == -1) {
dprintf("Bad line (I_POP of \"ptem\" failed) = %s\n", line);
(void) close(fd);
return (1);
}
(void) close(fd);
if (seteuid(ROOT_UID) != 0)
return (1);
return (0);
}
#ifdef DEBUG
/*
* display_args - This code prints out invocation arguments
* This is helpful since the program is called with
* up to 15 argumments.
*/
static void
display_args(argc, argv)
int argc;
char **argv;
{
int i = 0;
while (argc--) {
printf("Argument #%d = %s\n", i, argv[i]);
i++;
}
}
fputmpx(struct utmpx *rutmpx)
{
printf("ut_user = \"%-32.32s\" \n", rutmpx->ut_user);
printf("ut_id = \"%-4.4s\" \n", rutmpx->ut_id);
printf("ut_line = \"%-32.32s\" \n", rutmpx->ut_line);
printf("ut_pid = \"%d\" \n", rutmpx->ut_pid);
printf("ut_type = \"%d\" \n", rutmpx->ut_type);
printf("ut_exit.e_termination = \"%d\" \n",
rutmpx->ut_exit.e_termination);
printf("ut_exit.e_exit = \"%d\" \n", rutmpx->ut_exit.e_exit);
}
#endif /* DEBUG */