/* The code should compile with either ANSI C or K&R compilers. */

/*
 *      Copyright (c) 1995, 1996 by William Deich.
 *      Written by William Deich.  Not derived from licensed software.

 *    You may distribute under the terms of either the GNU General Public
 *    License or the Artistic License, as specified in the README file.
 */

/* Use P__() to have prototypes in STD C code, and not use
 * prototypes in K&R C: declare functions as:
 *	func_type  funcname P__((arglist));
 */

/* ==================================================================== */

#include "localsys.h"
#include "s_hsearch.h"
#include "version.h"

/* ==================================================================== */

#ifndef __STDC__
    extern char *malloc();
    extern char *realloc();
#endif

#ifndef HAVE_ALNUM
#define alnum(c) (isalpha(c) || isdigit(c))
#endif

#define SEP	" \t\v\n"		/* How to split fields on input lines */
#define QM	"\"'"			/* Quotemarks in fields */
#define CM	"#"			/* Comments in input file */
#define SAFE_IFS "IFS= \t\n"
#define OPTION_SEP '='			/* option separator */
#define CONDITION_SEP '~'		/* condition separator */

#define CLEAR_SETTINGS NULL

/* The safe path should normally be defined/edited in config.h.  This is
 * a just-in-case-it's-missing definition.
 */
#ifndef SAFE_PATH
#define SAFE_PATH "/bin:/usr/bin:/usr/ucb"
#endif

/* The name under this program assumes it is installed.  If argv[0] isn't
 * [/.../]ONETRUENAME, we assume we're running via symlink.
 */
#ifndef ONETRUENAME
#define ONETRUENAME "super"
#endif

/* Kind of help we give */
#define HELP_BASIC	0	/* Basic help shows what you can execute */
#define HELP_FULL	1	/* Full help on each command */
#define HELP_FACTS	2	/* Just-the-facts-ma'm mode */
#define HELP_USAGE	3	/* Print usage only, no per-cmd help */

#ifndef SUPERFILE
#ifdef SUPERDIR
#define SUPERFILE SUPERDIR "/super.tab"
#else
#define SUPERFILE "/usr/local/lib/super.tab"
#endif
#endif

#ifndef PERUSER_SUPERFILE
#define PERUSER_SUPERFILE ".supertab"
#endif

#ifndef MAXFD
int getdtablesize P__(( void ));
#define MAXFD (getdtablesize()-1)
#endif

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif

/* MAXSETENV is maximum number of variables that can be setenv'd on a
 * super control line.  This isn't the maximum that can be passed; it's
 * only the number of environment variables definitions that can be
 * made on one control line.
 */
#define MAXSETENV 40

/* Default value for the maximum length of a user-supplied environment
 * variable definition.
 */
#define MAXENVLEN 1000

/* If defined, then user patterns are allowed to match the uid as well as
 * the actual username.  We DISABLE this by default because users are
 * almost always identified by username.
 */
/* #define MATCH_DECIMAL_UID */

/* If defined, then group patterns are allowed to match the
 * gid as well as the group name, if any.  We ENABLE this by default
 * because it's not unusual for users to be put into unnamed groups
 * in the password file.
 */
#define MATCH_DECIMAL_GID

/* maximum number of tries at entering the password */
#define MAXTRY 3

/* default maxlength per arg passed to a command, maxlength all args */
#define MAXLEN1ARG 1000
#define MAXLENARGS 10000

/* define max length per option passed to super (not to the referenced
 * command), the max total length, and the valid option pattern.
 */
#define MAXOPTLEN 250
#define MAXOPTLEN_TOT 500
#define OPT_PATTERN "^[-/:+.=%@!,_a-zA-Z0-9]*$"

#ifdef HAVE_INNETGR
extern int innetgr();
#endif

#ifndef HAVE_MEMSET
void *memset P__(( void *s, int c, size_t n ));
#endif

/* special code to indicate we haven't specified a uid yet */
#define UID_NOTSET ((uid_t) ~0)

/* special code to indicate we haven't specified a gid yet */
#define GID_NOTSET ((gid_t) ~0)

/* special code to indicate we haven't specified supplementary groups */
#define GROUPS_NOTSET ((GETGROUPS_T) ~0)


/* Number of elements in an array */
#define NELEM(x) (sizeof(x)/(sizeof(*x)))

/* n rounded up to a multiple of m -- both should be positive */
#define ROUNDUP(n, m) ( ((n) % (m) == 0) ? (n) : ((n)/(m) + 1)*(m) )

/* STRBEG evaluates to !0 if s1 begins with substring s2 */
#define STRBEG(s1, s2) (strncmp((s1), (s2), strlen(s2)) == 0)
 
/* STRMATCH3 expects 'end' to be a pointer into s2, and evaluates to !0
 * if characters s2..(end-1) fully match s1 (that is, the next character in
 * s1 is null.
 */
#define STRMATCH3(s1, s2, end) \
	       (strncmp(s1, s2, end-s2) == 0 && s1[end-s2]=='\0')
		    

#define UNTIL(e) while(!(e))

/* ========================================================================= */

/*
 * Super's main external variables
 */
    extern char *prog;			/* this program */
    extern int debug;			/* debug level; 0=off */
    extern int check_syntax;		/* just check syntax of superfile */
    extern int use_stdin;		/* force stdin for passwds */
    extern char *superfile;		/* The actual superfile to be opened. */
    extern char superfile_init[];	/* The super.init file. */
    extern int it_came_from_cmdline;	/* Set by -F/-T/-U/-G flags */
    extern int using_user_supertab;	/* !0 means using a user's .supertab */
    extern int error_counter;		/* incremented by Error() */

/* The following external variables control the Error() routine.
 * They are modified at various points by super(), to give detailed control
 * over the error messages that are printed.
 */
    extern int error_stderr;		/* stderr() bool */
    extern int error_syslog;		/* syslog() bool */
    extern char *error_rlog_host;	/* where rsyslog() msgs are sent */
    extern int error_priority;		/* syslog() "priority" */
    extern int error_facility;		/* openlog() "facility" */
    extern char *error_command;		/* our program name */
    extern char *error_user;		/* our caller's username */

    extern int error_line;		/* input line number of error */
    extern int error_nl;		/* number of lines this msg refers to */
    extern char *error_srcfile;		/* filename of input with error */

/*
 * For use with strqtokS -- see that routine for definitions of strqS_xxx
 */
    extern unsigned char *strqS_qm;	/* For fast access by strqtokS */
    extern unsigned char my_qm[256];
    extern unsigned char *strqS_cc;	/* For fast access by strqtokS */
    extern unsigned char my_cc[256];
 

/*
 * Extern variable and routines used to compile/match user/group/host patterns.
 */
extern char *(*pat_compile) P__(( char *));
extern int (*pat_compare) P__(( char *));
extern int need_re_anchor;
/* ========================================================================= */

/*
 * Basic structures from which we construct other, bigger entities.
 */

struct simpleList {
    char *pat;
    struct simpleList *next;
};
typedef struct simpleList SimpleList;

struct simple2List {
    char *pat;
    struct simpleList *other;
    struct simple2List *next;
};
typedef struct simple2List Simple2List;

struct timeEnt {
    short begin;	/* Start time, in minutes */
    short end;		/* End time, in minutes */
    char day;		/* day of week to match (0..6); 7 means any day */
    char invert;	/* match is to be inverted */
};
typedef struct timeEnt TimeEnt;

struct timeList {
    TimeEnt te;
    struct timeList *next;
};
typedef struct timeList TimeList;

/* Linked open files */
struct fileList {
    char *givenname;	/* filename, as given in the parent file */
    char *fullname;	/* filename, fully expanded */
    FILE *fp;
    int line;		/* current line */
    int nl;		/* number of lines in this block */
    struct fileList *prev;
};
typedef struct fileList FileList;

struct countedString {
    char *s;		/* malloc'd string */
    int n;		/* number of characters in s */
    unsigned char used;	/* !0 means this string is in use */
};
typedef struct countedString CountedString;

struct strArray {
    CountedString *str;	/* Pts to malloc'd array of CountedString's */
    int n;		/* number strings allocated */
};
typedef struct strArray StrArray;

struct argRangePat {
    char *pat;		/* pattern to be matched by ... */
    int arg1;		/* this arg through... */
    int arg2;		/* this arg. */
    struct argRangePat *next;
};
typedef struct argRangePat ArgRangePat;

struct ourTime {
    time_t start;	/* when program started */
    short min;		/* local time of day, in minutes */
    char day;		/* local day, with 0=sunday */
};	
typedef struct ourTime OurTime;

struct conditions {
    int user;		/* !0 -> Last match to a user pattern */
    int time;		/* !0 -> Matched a time pattern */
    int allinverted;	/* !0 -> all time patterns scanned were inverted */
};
typedef struct conditions Conditions;

/* The progList struct is for listing all Cmd::File pairs on a control line. */
struct progList {
    char *Cmd;
    char *File;
};
typedef struct progList ProgList;

/* ========================================================================= */
#ifdef HAVE_ENUM
enum {SUPER_AUTH_PASSWORD, SUPER_AUTH_PAM} Method;
#else
/* No enums! */
#define SUPER_AUTH_PASSWORD 1
#define SUPER_AUTH_PAM 2
#endif

/* Authentication information */
struct authInfo {
    int required;	/* 0 = no auth required */
    int method;		/* AUTH_PASSWD, AUTH_PAM, etc */
    int timeout;	/* Time before re-authentication required */
    int renewtime;	/* update the timestamp file with each use of cmd? */
    int perhost;	/* create timestamp files separately for each host? */
    char user[1024];	/* value of authuser=xxx, if entered */
    char ts_user[1024];	/* value of timestampuid=xxx, if entered */
    char *prompt;	/* optional string with which to prompt for authinfo */
};
typedef struct authInfo AuthInfo;

/* Information for logging use */
struct logInfo {
    FILE *fp;			/* logfile pointer */
    char filename[1024];	/* logfile name */
    char user[1024];		/* value of loguid=xxx, if entered */
    uid_t uid;			/* UID under which we open logfile */
    pid_t pid;			/* PID of the logger; -1 means none running. */
    unsigned char newfile;	/* Bool: !0 if logfile given but not yet used */
    unsigned char newuid;	/* Bool: !0 if loguid given, but not yet used */
    int syslog_success;		/* syslog() "priority" for success msgs */
};
typedef struct logInfo LogInfo;

/* progMatch is for keeping track of matches in a ProgList */
struct progMatch {
    ProgList *cmd_file;
    int match;	/* index in proglist of matched command; -1 if no match */
    int evermatched;	/* 0 if no cmd in file matched pat */
    char *commandfound;	/* If match >= 0, commandfound points to actual
			 * command matched.  This can differ from
			 * proglist.Cmd[match], because that Cmd can be
			 * a pattern.
			 */
    int n;
    int nalloc;
};
typedef struct progMatch ProgMatch;

/* ========================================================================= */

/*
 * Global information from the :global lines
 */
struct globalInfo {
    char owner[32];	/* Owner of FullPath must be this person; overridden
			 * by local owner=xxx option, if present.
			 */
    char *chdir_path;	/* Change to this dir before exec'ing; null if none */
    int relative_path;	/* Allow filenames to be relative.  This is
			 * in general a stupid idea.  Don't do it!
			 */
    int group_slash;	/* Allow group names to have slashes.  If you
			 * allow this, you make it harder to catch certain
			 * command-line typos.  Don't do it!
			 */
    int maxenvlen;	/* max length of envvar (all of "name=value") */
    char **env;		/* null-terminated list of vars from env=var[,...] */

    int nice_incr;	/* value of the nice=nnn increment value */

    int mask;		/* umask setting */

    long maxlen1arg;	/* max len of any single arg */
    long maxlenargs;	/* max len of all args, combined */
    int usr_args[2];	/* number of user-entered args allowed */
    ArgRangePat argpats;	/* arg[MMM-]NNN=xxx arguments */

    AuthInfo authinfo;	/* authentication information */

    Simple2List userbefore;	/* list of u/g/h pats before per-cmd pats */
    Simple2List userafter;	/* list of u/g/h pats after per-cmd pats */
    SimpleList b_a_text;	/* list of original text for above */
    int user_clear;		/* clear userbefore list if new val seen */

    TimeList timebefore;	/* A list of the actual time ranges used */
    TimeList timeafter;		/* A list of the actual time ranges used */
    int time_clear;		/* clear timebefore list if new val seen */

    int use_after;		/* !set to !0 when we see <> */

    LogInfo log;		/* Information for logging to file */

    char mailcmd[500];		/* Information for logging via mail */
    int mail_success;		/* bool: mail on success? (-1 = unknown) */
    int gethostbyname;		/* bool: use gethostbyname()? */

    GETGROUPS_T groups[NGROUPS_MAX];	/* groups from [add]groups=xxx,... */
    int ngroups;		/* number of supplementary groups */
    int groups_added;		/* bool: groups were addgroups=, not groups= */

    char *setenv[MAXSETENV+1];	/* values of setenv=var[,...] option */
};
typedef struct globalInfo GlobalInfo;

/*
 * Information describing the caller
 */
struct userInfo {
    struct passwd caller;		/* who's invoking program */
    char hostname[MAXHOSTNAMELEN];	/* whence came the user */
    char lc_hostname[MAXHOSTNAMELEN];	/* hostname in lower case */
    int orig_mask;			/* umask setting at startup */

    uid_t orig_uid;			/* uid at prog start, from getuid() */
    gid_t orig_gid;			/* gid at prog start, from getgid() */
    uid_t new_uid;			/* new uid, from uid=xxx or u+g=xxx */
    gid_t new_gid;			/* new gid, from gid=xxx or u+g=xxx */
    uid_t new_euid;			/* new euid, from uid=xxx or euid=xxx */
    gid_t new_egid;			/* new egid, from gid=xxx or egid=xxx */

    OurTime ourtime;			/* when we started, etc */

    char encr[1000];			/* encrypted password; length is */
					/* pretty large by current standards */
    char salt[1000];			/* salt from password */
};
typedef struct userInfo UserInfo;

/*
 * Per-entry (in the super.tab file) information.  This gets filled in
 * at various points, as the program learns more.
 */
struct localInfo {
    ProgMatch progs;	/* Records prog::file sets, and is updated w/ matches */

    char *info;		/* value of info=xxx option */
    char *die;		/* Gets msg from die=msg ; null if none */
    char *print;	/* Gets msg from print=msg ; null if none */
    char *chdir_path;	/* Change to this dir before exec'ing; null if none */
    char *argv0;	/* value of argv0=xxx option */

    char user[32];	/* value of uid=xxx options */
    char euser[32];	/* value of euid=xxx options */
    char group[32];	/* value of group=xxx options */
    char egroup[32];	/* value of egroup=xxx options */
    char u_g[32];	/* value of u+g=xxx option */
    char owner[32];	/* value of owner=xxx option */
    uid_t file_uid;	/* uid of the matched FullPath */
    gid_t file_gid;	/* gid of the matched FullPath */
    GETGROUPS_T groups[NGROUPS_MAX];	/* groups from [add]groups=xxx,... */
    int ngroups;	/* number of supplementary groups */
    int groups_added;	/* bool: groups were addgroups=, not groups= */
    int maxenvlen;	/* max length of envvar (all of "name=value") */
    char **env;		/* null-terminated list of vars from env=var[,...] */
    char *setenv[MAXSETENV+1];	/* values of setenv=var[,...] option */
    char *fdlist;	/* value of fd=nnn[,...] option */
    int mask;		/* value of umask=xxx option */
    int nice_incr;	/* value of the nice=nnn increment value */
    Simple2List userpats;	/* list of PermittedUser patterns */
    SimpleList origtext;	/* list of PermittedUser patterns */
    TimeList time;	/* A list of the actual time ranges used */
    int usr_args[2];	/* number of user-entered args allowed */
    long maxlen1arg;	/* max len of any single arg */
    long maxlenargs;	/* max len of all args, combined */
    ArgRangePat argpats;	/* arg[MMM-]NNN=xxx arguments */
    char **checkvar;	/* null-term'd list of vars from checkvar=var[,...] */

    char mailcmd[500];	/* Information for logging via mail */
    int mail_success;	/* bool: mail on successful tries? (-1 = unknown) */

    int *fd;		/* descriptors from fdlist string */
    AuthInfo authinfo;	/* authentication requirements on this command */
};
typedef struct localInfo LocalInfo;

/* ========================================================================= */

extern FileList *currfile;		/* list of currently-open files */

extern char authInitMsg1[];		/* msg from authentication init */
extern char authInitMsg2[];		/* suppl msg from authentication init */
extern int authInitErrno;		/* errno, if relevant, to go
					 * with authInitMsg1 */

extern GlobalInfo globalinfo;		/* :global info */

extern UserInfo userinfo;		/* User's info */

extern LocalInfo localinfo;		/* per-cmd info */

extern Conditions matches;		/* To keep track of what matched */

/* ========================================================================= */
void	 ARfree P__((ArgRangePat *head));
int	ARinsert P__((ArgRangePat *start, int arg1, int arg2, char *pat));
ArgRangePat *
	ARnext P__((ArgRangePat *start, int iarg));
void	add_builtin_variables P__(( void ));
#ifdef HAVE_SYSINFO
int	add_sysinfo_variables P__(( void ));
#endif
#ifdef HAVE_UNAME
int	add_uname_variables P__(( void ));
#endif
int	add_variable P__(( char *varname, char *vardefn ));
void	anchor P__(( char *in, char *out ));
char*	approve P__((char *usrcmd, int givehelp, int verbose));
char*	auth_name P__(( void ));
int	balancedbraces P__(( char *s ));
char ** blkdup P__((char **blk));
int	blkfree P__((char **av0));
char ** Blkdup P__((char *str, char **blk));
int	Blkfree P__((char *str, char **av0));
int	blkprint P__((char **));
int	build_cmd_file P__((char *, int , char *, char **));
char**	buttonup P__((char *));
int	canonicalize_hostname P__(( char *buf, int len));
int	check_arglistlen P__(( char **argv ));
int	check_auth P__(( char *cmd ));
int	check_owner P__(( void ));
int	check_rpath P__((char *r_path, char *path));
void	check_stdio P__((void));
int	check_value P__(( char *value, char *pat ));
int	check_var_value P__(( void ));
int	checkarg P__(( char *str ));
int	checkenv P__(( char *name,  char *value,  char *pat ));
char*	clean_buf P__(( char *buf, char *outbuf ));
void	close_writer P__(( void ));
int	colon_define P__(( char *command ));
int	colon_die P__(( char *command ));
int	colon_getenv P__(( char *command ));
int	colon_global P__(( char *command ));
int	colon_include P__(( char *command, int allow_missing ));
int	colon_if P__(( char *command, int *colonif_succeeded ));
int	conditions_and_options P__(( char *cond_or_opt_wd ));
struct passwd *
	construct_user_superfile P__(( char *user ));
char*	dayname P__(( int daynum ));
int	daynum P__(( int unixtime ));
void	debug_print P__((char *path, char **arglist, char **envp, int n_bltin));
void	debug_hello P__((void));
int	do_options P__((int argc, char **argv, int *help, int *vers,
		    int *verbose, int *printvars,
		    char **owned_by_file, char **required_path));
char*	do_variables P__(( char *str));
char*	docrypt P__((char *key, char *salt));
char*	ends P__(( char *s1, char *s2 ));
#ifdef HAVE_STDARG_H
int	Error P__(( int show_perror, int die, char * fmt, ... ));
#else
int	Error();
#endif
FileList* file_open P__(( FileList *list, char *name,
			int allow_missing, uid_t *req_uid, gid_t *req_gid ));
FileList* file_close P__(( FileList *list ));
char*	fillbuffer P__(( FILE *fp, int *indentok, int *nl ));
int	findgid P__(( int allowbrackets, char *grouplabel ));
int	findgroup P__(( char *grouplabel ));
int	fixup_fullpath P__((int , char *, char *, char *, int ));
void	free_SimpleList P__(( SimpleList *));
void	free_Simple2List P__(( Simple2List *));
void	free_TimeList P__(( TimeList *));
int	get_canonical_hostname P__((char *buf, int len));
int	get_encrypted_pw P__(( void ));
int	get_owner P__((char *path, uid_t *uid, gid_t *gid));
int	get_pam P__(( char *cmd, char *caller, char *user ));
int	get_password P__(( char *cmd, char *caller, char *user,
			char *salt,  char *encr ));
char*	get_variable P__(( char *varname ));
char*	Getenv P__(( char *s ));
int	Getgroups P__((int, GETGROUPS_T *));
struct group *
	getgrentry P__(( char *name ));
int	getlogdir P__((char *user, char *buf));
struct passwd *
	getpwentry P__((int allow_brackets, char *username));
int	global_arg P__((char *word));
int	globbraces P__(( char *s, int wrap_in_braces, char ***globlist ));
int	handle_option P__((char *word, char *s, int isglobal));
int	ingroup P__(( char * user, gid_t gid, char * gp_pat ));
void	init_nice_incr P__((int is_global));
void	init_strqtokS P__(( void ));
void	init_umask P__((int is_global));
void	init_globalinfo P__( (void) );
void	init_localinfo P__( (void) );
int	init_userinfo P__( (void) );
int	InsertCondition P__(( char *, char *, int ));
int	InsertTimeList P__(( char *, char **, TimeList *, char *, int ));
int	InsertUserList P__((char *, char **, Simple2List *, SimpleList *, int));
void	logmsg P__(( char * cmd, char ** args ));
int	makedir P__(( char *directories, int *err, char *msgbuf ));
char*	makedirname P__(( char *prefix, char *hostname, char *path, int *err, char *msgbuf ));
int	match_pattern P__((int match, int glob, char *str, char *pattern));
void	match_ugh_user P__((Simple2List *sl, int isglobal));
void	matchtime P__(( OurTime *our, TimeList *tl ));
char**	newargs P__((char *path_plus, char **argv, int *n_builtin));
FILE*	open_writer P__(( char *user, char *filename, pid_t *pid_p ));
void	opensuperlog P__(( void ));
int	option_clear_settings P__((char *word, char *s, int isglobal));
int	option_global_reset_settings P__((void));
int	option_local_clear_settings P__((void));
void	p_regcomp_flags P__(( int ));
char*	p_compile P__(( char *));
int	p_compare P__(( char *));
int	parseline P__((int givehelp, int checksyntax, char *buf, char *usrcmd));
void	print_variable P__(( int indx, char *key, char *data ));
void	printhelp P__(( int verbose ));
void	printhelp_hello P__(( int verbose, char *usrcmd ));
int	process_colon_cmds P__(( char *command, int *colonif_succeeded ));
int	process_logfile_opt P__(( void ));
int	rcl_nice_incr P__((void));
int	rcl_umask P__((void));
void	re_anchor P__(( char *in, char *out));
int	s_getpass P__((char *prompt, int use_stdin, char *buf, int buflen));
char*	s_re_comp P__(( char *));
int	s_re_exec P__(( char *));
void	readtime_init P__(( void ));
int	readtimerange P__(( char *str, short *t1, short *t2, char *d ));
int	readtime P__(( char *str, short *t1, char *d ));
int	save_var_value P__((char *var_value));
int	set_chdir P__((void));
int	set_nice_incr P__((void));
int	set_u_g P__(( void ));
void	set_umask P__((void));
int	Setgroups P__((int, GETGROUPS_T *));
int	shell_compare P__(( char *));
char*	shell_compile P__(( char *));
void	store_nice_incr P__((int nice_incr, int is_global));
void	store_umask P__((int mask, int is_global));
char*	str_val P__(( char *left, int sep, char *str ));
int	StrBulkCpy P__(( StrArray *dst, int dst_ielt, StrArray *src, int src_ielt, int nelt ));
int	StrEltCpy P__(( StrArray *a, int ielt, char *str));
char*	StrEltGetPtr P__(( StrArray *a, int ielt ));
void	StrEltsUnused P__(( StrArray *a ));
void	StrInit P__(( StrArray *a ));
int	stringcopy P__(( char *to, char *from, int n));
int	StrLastInUse P__(( StrArray *a ));
int	StrNalloc P__(( StrArray *a, int nelt));
int	StrNelts P__(( StrArray *a ));
void	strtolower P__(( char *string ));
char*	strqtokS P__(( char *, char *, char *, char *, unsigned int ));
int	read_syslogcode P__(( char *str, int *code ));
void	user_supertab P__(( char *file_or_user, int is_file, char *cmd ));
int	wildmat P__(( char *text, char *p ));
