[alt.sources] binst - Easy installation of user programs

chris@attron.ruhr.sub.org (Christian Schlichtherle) (01/26/91)

Please excuse my bad English...

Hmm, it seems to me that this program is a little bit like BSD's
"install", although I've never seen this...

	"binst" is a small program used to make user written programs
	public available without having super user permissions in
	order to install it. To avoid system security gaps "binst"
	performs a number of (configurable) checks when installing
	the program.
	To install a program you normally need write permission to the
	directory where the program should go, but giving write
	permission to the user means a lack of system security because
	the user could remove any file in the directory and for example
	substitute it with a trojanic horse.
	With "binst" the user does not need write permission to the
	directory. Instead "binst" is a setugid-root program which
	performs a number of (configurable) checks in order to maintain
	system security.

#!/bin/sh
# This is a shell archive (shar 3.24)
# made 01/26/1991 15:22 UTC by chris@attron.ruhr.sub.org
# Source directory /u/chris/src/binst
#
# existing files WILL be overwritten
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#    783 -r--r--r-- Makefile
#   1774 -r--r--r-- README
#   9467 -r--r--r-- binst.c
#   2146 -r--r--r-- config.h
#
if touch 2>&1 | fgrep '[-amc]' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= Makefile ==============
echo "x - extracting Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X#
X#	@(#) Makefile	1.1 91/01/26 
X#
X#	Copyright (C) 1991 by Christian Schlichtherle
X#			      (chris@attron.ruhr.sub.org)
X#	
X#	Permission is hereby granted to use, copy, modify or distribute
X#	this file at will unless this copyright notice is removed.
X#	The author disclaims any kind of warranty.
X#
X#	Makefile - Makefile for binst(C).
X#
X
X#
X# Compiler and linker flags.
X#
XCC = cc
XCFLAGS = -Ms2 -i
XLDFLAGS = -s
XLIBS =
X
X#
X# Owner, group and mode of the installed program.
X#
XOWNER = root
XGROUP = root
XMODE = 6711
X# Destination directory
XBIN = /u/bin
X
Xbinst: binst.c
X	$(CC) $(CFLAGS) $(LDFLAGS) $? -o $@ $(LIBS)
X
Xinstall: $(BIN)/binst
X
X$(BIN)/binst: binst
X	cp $? $@
X	chown $(OWNER) $@
X	chgrp $(GROUP) $@
X	chmod $(MODE) $@
X
Xuninstall:
X	rm -f $(BIN)/binst
X
Xclean:
X	rm -f a.out core *.o binst
SHAR_EOF
$TOUCH -am 0126162191 Makefile &&
chmod 0444 Makefile ||
echo "restore of Makefile failed"
set `wc -c Makefile`;Wc_c=$1
if test "$Wc_c" != "783"; then
	echo original size 783, current size $Wc_c
fi
# ============= README ==============
echo "x - extracting README (Text)"
sed 's/^X//' << 'SHAR_EOF' > README &&
X
X	@(#) README	1.1 91/01/26 
X
X	Copyright (C) 1991 by Christian Schlichtherle
X			      (chris@attron.ruhr.sub.org)
X	
X	Permission is hereby granted to use, copy, modify or distribute
X	this file at will unless this copyright notice is removed.
X	The author disclaims any kind of warranty.
X
X	README - Documentation file for binst(C).
X
X
XSYNTAX
X
X	binst <program> ...
X
X
XDESCRIPTION
X
X	"binst" is a small program used to make user written programs
X	public available without having super user permissions in
X	order to install it. To avoid system security gaps "binst"
X	performs a number of (configurable) checks when installing
X	the program.
X	To install a program you normally need write permission to the
X	directory where the program should go, but giving write
X	permission to the user means a lack of system security because
X	the user could remove any file in the directory and for example
X	substitute it with a trojanic horse.
X	With "binst" the user does not need write permission to the
X	directory. Instead "binst" is a setugid-root program which
X	performs a number of (configurable) checks in order to maintain
X	system security.
X
X	All installed programs have at least 0755 permission (read
X	permission is needed for shell scripts).
X	All programs are installed in the directory PUBBIN (configurable
X	parameter in "config.h").
X	For a detailed description of the permission checkings performed
X	by "binst" see the file "config.h". The behaviour of "binst"
X	deeply depends on this file.
X
X
XNOTES
X
X	To install "binst" you should edit "config.h" in order to adapt
X	it to your system's needs and edit "Makefile" to satisfact your
X	compiler. Installation is quite simple.
X
X
XRETURN VALUE
X
X	"binst" always returns the status of the last installation,
X	which is 0 for success and 1 otherwise.
SHAR_EOF
$TOUCH -am 0126162191 README &&
chmod 0444 README ||
echo "restore of README failed"
set `wc -c README`;Wc_c=$1
if test "$Wc_c" != "1774"; then
	echo original size 1774, current size $Wc_c
fi
# ============= binst.c ==============
echo "x - extracting binst.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > binst.c &&
X/*
X *	@(#) binst.c	1.1 91/01/26 
X *
X *	Copyright (C) 1991 by Christian Schlichtherle
X *			      (chris@attron.ruhr.sub.org)
X *	
X *	Permission is hereby granted to use, copy, modify or distribute
X *	this file at will unless this copyright notice is removed.
X *	The author disclaims any kind of warranty.
X *
X *	binst.c - C module for binst(C).
X */
X
X#if	!defined(lint) && !defined(library)
Xstatic char	sccsid[] = "@(#) binst.c 1.1 91/01/26 ";
X#endif /* not lint and not library */
X
X#include <string.h>
X#include <stdio.h>
X#include <fcntl.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <errno.h>
X#include "config.h"
X
X#define MAXPATHLEN	80
X#define ERRBUFSIZ	255
X
Xextern char	*sys_errlist[];
Xextern int	sys_nerr;
X
Xextern int	setjmp();
Xextern void	longjmp();
Xextern void	exit();
X
Xchar	*prg_nam = NULL;
Xchar	pubbin[] = PUBBIN;
Xjmp_buf	env;
Xchar	errbuf[ERRBUFSIZ + 1];
X
X/*
X * Certain messages.
X */
Xchar	*message[] = {
X	"Usage: %s <filename> ...\n",
X	"Not owner of %s"
X};
X
X/*
X * Index of message in upper message vector.
X */
X#define USAGE		0
X#define NOT_OWNER	1
X
X/*
X * sig_list - List of signals that have to be caught.
X */
Xint	sig_list[] = {
X	SIGHUP,
X	SIGINT,
X	SIGQUIT,
X	SIGTERM,
X	0
X};
X
X/*
X * sig_action - List of actions for all signals.
X */
Xint	sigcatch();
Xint	(*sig_action[NSIG])() = {
X	(int (*)()) 0,	/* invalid */
X	sigcatch,	/* SIGHUP  */
X	sigcatch,	/* SIGINT  */
X	sigcatch,	/* SIGQUIT */
X	sigcatch,	/* SIGILL  */
X	sigcatch,	/* SIGTRAP */
X	sigcatch,	/* SIGIOT  */
X	sigcatch,	/* SIGEMT  */
X	sigcatch,	/* SIGFPE  */
X	sigcatch,	/* SIGKILL */
X	sigcatch,	/* SIGBUS  */
X	sigcatch,	/* SIGSEGV */
X	sigcatch,	/* SIGSYS  */
X	sigcatch,	/* SIGPIPE */
X	sigcatch,	/* SIGALRM */
X	sigcatch,	/* SIGTERM */
X	sigcatch,	/* SIGUSR1 */
X	sigcatch	/* SIGUSR2 */
X			/* If there are more signals they are ignored! */
X};
X
X/*
X * print_error - Outputs error messages.
X * print_error uses "s" for perror(s) and "t" for error output.
X */
Xvoid	print_error(s, t)
Xchar	*s;
Xchar	*t;
X{
X	if ((s == NULL || !*s) && (t == NULL || !*t))
X		return;
X
X	if (prg_nam != NULL && *prg_nam)
X		(void) fprintf(stderr, "%s: ", prg_nam);
X	if (errno > 0 && errno <= sys_nerr && s != NULL && *s)
X		(void) fprintf(stderr, "%s: %s\n", s, sys_errlist[errno]);
X	if (t != NULL && *t)
X		(void) fprintf(stderr, "%s\n", t);
X		
X	return;
X}
X
X/*
X * build_dstname - Builds a pathname.
X * 'build_dstname' builds a new pathname and copies it to the string
X * "dst".
X * The pathname is build from the directory name "dir" and the
X * filename from pathname "src" appended.
X */
Xvoid	build_path(src, dir, dst)
Xchar	*src;
Xchar	*dir;
Xchar	*dst;
X{
X	char	*ptr;
X
X	if ((ptr = strrchr(src, '/')) != NULL) 
X		ptr++;
X	else
X		ptr = src;
X	(void) sprintf(dst, "%s/%s", dir, ptr);
X
X	return;
X}
X
Xint	sigcatch(num)
Xint	num;
X{
X	(void) signal(num, SIG_IGN);
X
X	longjmp(env, num);
X
X	return (-1);
X}
X
X/*
X * set_act_sig - Sets only an active signal.
X * This function sets only a signal which is "active" (i.e. not ignored).
X * It returns the old action associated with the signal.
X * Note: When you set an active signal inactive with this function, you
X * can not redefine it. Use signal(S) instead.
X */
X#if	defined(INT_SIG)
Xint	(*set_act_sig(sig_type, sig_func))()
Xint	sig_type;
Xint	(*sig_func)();
X#else	/* VOID_SIG */
Xvoid	(*set_act_sig(sig_type, sig_func))()
Xint	sig_type;
Xvoid	(*sig_func)();
X#endif	/* VOID_SIG */
X{
X#if	defined(INT_SIG)
X	int	(*old_func)();
X#else	/* VOID_SIG */
X	void	(*old_func)();
X#endif	/* VOID_SIG */
X
X	/*
X	 * Use an algorithm to leave no gap where the program
X	 * can be terminated at the cost of two "signal" calls
X	 * when all was right.
X	 */
X	if ((old_func = signal(sig_type, SIG_IGN)) != SIG_IGN)
X		(void) signal(sig_type, sig_func);
X
X	return (old_func);
X}
X
X/*
X * set_sig_grp - Sets a group of signals.
X * "sig_lst" is a zero terminated list of signals to be set.
X * The function to set the signals is pointed to by "set_fnc".
X * The new action (i.e. the new pointer to a function) for a
X * signal can be found in the vector "new_act" with the signal
X * (i.e. it's number) as it's offset.
X * If "old_act" is nonnull the old action of a signal is stored
X * here with the signal (i.e. it's number) as it's offset again.
X * If it is null the old action is stored in "new_act" instead.
X */
Xvoid	set_sig_grp(sig_lst, set_fnc, new_act, old_act)
Xint	sig_lst[];		/* Zero terminated list of functions   */
Xint	(*(*set_fnc)())();	/* Signal set function (see signal(S)) */
Xint	(*new_act[NSIG])();	/* Vector of new signal actions        */
Xint	(*old_act[NSIG])();	/* Vector of old signal actions        */
X{
X	while (*sig_lst != 0) {
X		if (old_act != (int (**)()) 0)
X			old_act[*sig_lst] = (*set_fnc)(*sig_lst,
X						       new_act[*sig_lst]);
X		else
X			new_act[*sig_lst] = (*set_fnc)(*sig_lst,
X						       new_act[*sig_lst]);
X		sig_lst++;
X	}
X
X	return;
X}
X
X/*
X * copy - copies a file from "src" to "dst".
X * If the file cannot be copied an error message is output and
X * -1 is returned.
X * No destination file is left except that this file already exists
X * and is not writable.
X * Owner, group and mode of the destination file are not reset.
X * Instead the mode is 0666 according to umask (see creat(S)).
X */
Xint	copy(src, dst)
Xchar	*src;
Xchar	*dst;
X{
X	char	buf[BUFSIZ];
X	int	src_fd;
X	int	dst_fd;
X	int	nread;
X
X	if ((src_fd = open(src, O_RDONLY)) == -1)
X		return (-1);
X
X	if ((dst_fd = creat(dst, 0666)) == -1) {
X		(void) close(src_fd);
X		print_error(src, "");
X
X		return (-1);
X	}
X
X	while ((nread = read(src_fd, buf, sizeof(buf) / sizeof(buf[0]))) > 0)
X		if (write(dst_fd, buf, nread) != nread) {
X			(void) close(src_fd);
X			(void) close(dst_fd);
X			(void) unlink(dst);
X			print_error(dst, "");
X
X			return (-1);
X		}
X
X	if (nread == -1) {
X		(void) close(src_fd);
X		(void) close(dst_fd);
X		(void) unlink(dst);
X		print_error(src, "");
X
X		return (-1);
X	}
X
X	(void) close(src_fd);
X	(void) close(dst_fd);
X
X	return (0);
X}
X
X/*
X * chall - Changes mode, owner and group of the specified file.
X */
Xint	chall(file, mode, owner, group)
Xchar	*file;
Xint	mode;
Xint	owner;
Xint	group;
X{
X	if (chmod(file, mode) == -1)
X		return (-1);
X	if (chown(file, owner, group) == -1)
X		return (-1);
X
X	return (0);
X}
X
X/*
X * check_files - Checks files.
X */
Xint	check_files(src, dst, dst_mode, dst_owner, dst_group)
Xchar	*src;
Xchar	*dst;
Xint	*dst_mode;
Xint	*dst_owner;
Xint	*dst_group;
X{
X	struct stat	src_stat, dst_stat;
X
X		/* Get info about source file */
X	if (stat(src, &src_stat) == -1) {
X		print_error(src, "");
X		return -1;
X	}
X
X#if	defined(INSTALL_OTHER)
X	if (access(src, 04) == -1)
X		print_error(src, "");
X		return -1;
X	}
X#else	/* not INSTALL_OTHER */
X	if (src_stat.st_uid != getuid()) {
X		(void) sprintf(errbuf, message[NOT_OWNER], src);
X		print_error("", errbuf);
X		return -1;
X	}
X#endif	/* not INSTALL_OTHER */
X
X#if	defined(SRC_OWNER)
X	*dst_owner = src_stat.st_uid;
X	*dst_group = src_stat.st_gid;
X#else	/* not SRC_OWNER */
X	*dst_owner = getuid();
X	*dst_group = getgid();
X#endif	/* not SRC_OWNER */
X
X#if	defined(SETUID)
X	if (((*dst_mode = src_stat.st_mode) & 07000) == 0)
X		*dst_mode |= 0755;
X#else	/* not SETUID */
X	*dst_mode = src_stat.st_mode & ~07000 | 0755;
X#endif	/* not SETUID */
X
X#if	!defined(OVERWRITE)
X	if (stat(dst, &dst_stat) == 0) {
X		if (*dst_owner != dst_stat.st_uid) {
X			(void) sprintf(errbuf, message[NOT_OWNER], dst);
X			print_error("", errbuf);
X			return -1;
X		}
X
X#if	defined(INSTALL_OTHER) && defined(SRC_OWNER) && defined(OWNER_UPDATE)
X		if (*dst_owner != getuid()) {
X			(void) sprintf(errbuf, message[NOT_OWNER], dst);
X			print_error("", errbuf);
X			return -1;
X		}
X#endif	/* INSTALL_OTHER and SRC_OWNER and OWNER_UPDATE */
X	}
X#endif	/* not OVERWRITE */
X
X	return 0;
X}
X
X/*
X * binst - Installs a file in the public binary directory.
X * Returns 0 if successfull, -1 otherwise.
X */
Xint	binst(src_path)
Xchar	*src_path;
X{
X	char	dst_path[MAXPATHLEN + 1];
X	int	dst_mode, dst_owner, dst_group;
X	int	sig_num;
X
X		/* Build destination pathname */
X	build_path(src_path, pubbin, dst_path);
X
X	if (check_files(src_path, dst_path, &dst_mode, &dst_owner,
X			&dst_group) == -1)
X		return -1;
X
X	/*
X	 * Set a long jump mark.
X	 * If we return from a jump triggered by a signal remove the
X	 * destination file (it is assumed to be an incomplete copy!),
X	 * print an error message, restore all signal actions and
X	 * execute the signal's previously stored action.
X	 * If it returns from this action (in fact it does not return!),
X	 * return with an error status.
X	 */
X	if ((sig_num = setjmp(env)) != 0) {
X		(void) unlink(dst_path);
X
X		(void) sprintf(errbuf, "%s removed", dst_path);
X		(void) print_error("", errbuf);
X
X		set_sig_grp(sig_list, signal, sig_action, (int (**)()) 0);
X
X		(*sig_action[sig_num])(sig_num);
X		
X		return -1;
X	}
X
X	/*
X	 * Catch all important signals which are not ignored.
X	 * If one is catched a long jump to the previously set mark is
X	 * performed.
X	 */
X	set_sig_grp(sig_list, set_act_sig, sig_action, (int (**)()) 0);
X
X	/*
X	 * Copy the files and set the permissions according to
X	 * the source file.
X	 */
X	if (copy(src_path, dst_path) == -1)
X		return -1;
X	(void) chall(dst_path, dst_mode, dst_owner, dst_group);
X
X	/*
X	 * Restore all previously catched signals.
X	 */
X	set_sig_grp(sig_list, set_act_sig, sig_action, (int (**)()) 0);
X	
X	return 0;
X}
X
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	int	status;
X
X	prg_nam = argv[0];
X
X	if (argc == 1) {
X		(void) fprintf(stderr, message[USAGE], argv[0]);
X		exit (1);
X	}
X
X	while (--argc)
X		status = binst(*++argv);
X
X	exit(-status);
X		/* For your 'lint' only... */
X	return -1;
X}
SHAR_EOF
$TOUCH -am 0126162191 binst.c &&
chmod 0444 binst.c ||
echo "restore of binst.c failed"
set `wc -c binst.c`;Wc_c=$1
if test "$Wc_c" != "9467"; then
	echo original size 9467, current size $Wc_c
fi
# ============= config.h ==============
echo "x - extracting config.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > config.h &&
X/*
X *	@(#) config.h	1.1 91/01/26 
X *
X *	Copyright (C) 1991 by Christian Schlichtherle
X *			      (chris@attron.ruhr.sub.org)
X *	
X *	Permission is hereby granted to use, copy, modify or distribute
X *	this file at will unless this copyright notice is removed.
X *	The author disclaims any kind of warranty.
X *
X *	config.h - C include file for binst(C).
X */
X
X#define INT_SIG			/* You have int (*signal())() */
X#undef VOID_SIG			/* You have void (*signal())() */
X
X/*
X * This is the name of the public binary directory.
X */
X#define PUBBIN	"/u/bin"
X
X/*
X * Define wether you should be able to install programs you do not
X * own but you can read or if you are only allowed to install your
X * own programs.
X */
X/* #define INSTALL_OTHER	/* Install all programs with read permission */
X
X/*
X * Define wether the destination file's owner and group should
X * be set to the source's owner and group or if it should be
X * set to the real user id and the real group id of the process.
X * To get in effect the INSTALL_OTHER flag must be set.
X */
X#define SRC_OWNER		/* Set owner and group to the source's one   */
X
X/*
X * Define wether you want to be able to overwrite other user's installed
X * programs or not.
X */
X/* #define OVERWRITE		/* Overwrite other user's installed programs */
X
X/*
X * The following flag appears to updates only.
X * Define wether an update to an installed program is restricted to
X * the owner of that program or not.
X * To get in effect the INSTALL_OTHER and the SRC_OWNER flag must be set
X * and the OVERWRITE flag must be cleared.
X */
X#define OWNER_UPDATE		/* Only owner is allowed to make an update   */
X
X/*
X * Define wether the destination file should have the set user id and
X * set group id bit on if the source file has it or not.
X * This flag also effects the sticky bit. If it is not set the sticky
X * bit will always be cleared.
X * To get in effect this flag requires the binst(C) program to
X * have the set user id bit set for root.
X * Note that all installed programs are given 0755 permission at least
X * unless they have the SETUID, SETGIG or sticky bit set.
X */
X#define SETUID			/* Set user and group id bit if source has   */
SHAR_EOF
$TOUCH -am 0126162191 config.h &&
chmod 0444 config.h ||
echo "restore of config.h failed"
set `wc -c config.h`;Wc_c=$1
if test "$Wc_c" != "2146"; then
	echo original size 2146, current size $Wc_c
fi
exit 0
-- 
Snail: Christian Schlichtherle, Elbscheweg 20, 5802 Wetter 4, Germany
Email: chris@attron.ruhr.sub.org	Tel.: +49 2335 7550
Politiker an die (vorderste) Front!