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!