From: Todd C. Miller Date: Fri, 11 Jun 1993 22:03:45 +0000 (+0000) Subject: Initial revision X-Git-Tag: SUDO_1_3_0~112 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=958ee39a64006dd561ef7bef4a85869692f69b0c;p=sudo Initial revision --- diff --git a/check.c b/check.c new file mode 100644 index 000000000..276985793 --- /dev/null +++ b/check.c @@ -0,0 +1,276 @@ +/* + * sudo version 1.1 allows users to execute commands as root + * Copyright (C) 1991 The Root Group, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you make modifications to the source, we would be happy to have + * them to include in future releases. Feel free to send them to: + * Jeff Nieusma nieusma@rootgroup.com + * 3959 Arbol CT (303) 447-8093 + * Boulder, CO 80301-1752 + * + ******************************************************************* + * + * check.c + * + * check_user() only returns if the user's timestamp file + * is current or if they enter a correct password. + * + * Jeff Nieusma Thu Mar 21 22:39:07 MST 1991 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sudo.h" + +char *getpass(); + +static int check_timestamp(); +static void check_passwd(); +static void update_timestamp(); +static void reminder(); +static char *timestampfile_p; + +static int timedir_is_good; + + +/******************************************************************** + * + * check_user() + * + * This function only returns if the user can successfully + * verify who s/he is. + */ + +void check_user() +{ +register int rtn; + +umask ( 077 ); /* make sure the timestamp files are private */ + +if ( setuid (0) ) { /* have to be root to see timestamps */ + perror("setuid(0)"); + exit(1); + } +rtn = check_timestamp(); +if ( setruid (uid) ) { /* don't want to be root longer than necessary */ +#ifndef _AIX + perror("setruid(uid)"); + exit(1); +#endif + } + +if ( rtn && uid ) /* if timestamp is not current... */ + check_passwd(); + +if ( setuid (0) ) { /* have to be root to play with timestamps */ + perror("setuid(0)"); + exit(1); + } +update_timestamp(); +if ( setruid (uid) ) { /* don't want to be root longer than necessary */ +#ifndef _AIX + perror("setruid(uid)"); + exit(1); +#endif + } + +umask ( 022 ); /* want a real umask to exec() the command */ + +} + + + + +/******************************************************************** + * + * check_timestamp() + * + * this function checks the timestamp file. If it is within + * TIMEOUT minutes, no password will be required + */ + +static int check_timestamp() +{ +static char timestampfile[MAXPATHLEN+1]; +register char *p; +struct stat statbuf; +register int timestamp_is_old = -1; +time_t now; + + +sprintf ( timestampfile, "%s/%s", TIMEDIR, user ); +timestampfile_p = timestampfile; + +timedir_is_good = 1; /* now there's an assumption for ya... */ + + +/* walk through the path one directory at a time */ + +for ( p=timestampfile+1; p=index(p,'/'); *p++='/' ) { + *p='\0'; + if ( stat(timestampfile,&statbuf) < 0) { + if ( strcmp ( timestampfile, TIMEDIR )) + fprintf ( stderr, "Cannot stat() %s\n", timestampfile ); + timedir_is_good = 0; + *p='/'; + break; + } + } + + +if ( timedir_is_good ) { /* if all the directories are stat()able */ + if ( stat(timestampfile, &statbuf) ) { /* does the file exist? */ + if ( uid ) reminder(); /* if not, do the reminder */ + timestamp_is_old=1; /* and return (1) */ + } + else { /* otherwise, check the time */ + now = time ( (time_t *) NULL ); + if ( now - statbuf.st_mtime < 60 * TIMEOUT ) + timestamp_is_old = 0; /* if file is recent, return(0) */ + else + timestamp_is_old = 1; /* else make 'em enter password */ + } + } + +/* there was a problem stat()ing a directory */ + +else { + timestamp_is_old = 1; /* user has to enter password */ + if ( mkdir (TIMEDIR, 0700 ) ) { /* make the TIMEDIR directory */ + perror("check_timestamp: mkdir"); + timedir_is_good = 0; + } + else { + timedir_is_good = 1; /* TIMEDIR now exists */ + reminder(); + } + } + +return (timestamp_is_old); + +} + + + + + +/******************************************************************** + * + * update_timestamp() + * + * This function changes the timestamp to now + */ + +static void update_timestamp() +{ +register int fd; + +if ( timedir_is_good ) { + unlink ( timestampfile_p ); + if (( fd = open (timestampfile_p, O_WRONLY|O_CREAT|O_TRUNC, 0600 )) < 0 ) + perror( "update_timestamp: open" ); + close (fd); + } +} + + + + + +/******************************************************************** + * + * check_passwd() + * + * This function grabs the user's password and checks with + * the password in /etc/passwd + */ + +static void check_passwd() +{ +char *crypt(); +struct passwd *pw_ent; +char *encrypted; /* this comes from /etc/passwd */ +char *pass; /* this is what gets entered */ +register int counter=TRIES_FOR_PASSWORD; + + +if ( (pw_ent = getpwuid( uid )) == NULL ) { + sprintf ( user, "%u", uid ); + log_error( GLOBAL_NO_PW_ENT ); + inform_user ( GLOBAL_NO_PW_ENT ); + exit (1); + } + +encrypted = pw_ent -> pw_passwd; + +/* you get TRIES_FOR_PASSWORD times to guess your password */ + +while ( counter > 0 ) { + pass = getpass ( "Password:" ); + if ( *pass == (char)NULL ) exit(0); + if ( !strcmp(encrypted, crypt(pass,encrypted))) + + return; /* if the passwd is correct return() */ + -- counter; /* otherwise, try again */ + fprintf ( stderr, "%s\n", INCORRECT_PASSWORD ); + } + +log_error( PASSWORD_NOT_CORRECT ); +inform_user ( PASSWORD_NOT_CORRECT ); + +exit (1); + +} + + + + +/******************************************************************** + * + * reminder() + * + * this function just prints the the reminder message + */ + +static void reminder() +{ + +#ifdef SHORT_MESSAGE +fprintf(stderr,"\n%s\n%s\n\n%s\n%s\n\n", +#else +fprintf(stderr,"\n%s\n%s\n%s\n\n%s\n%s\n\n%s\n%s\n\n", + +" sudo version 1.1, Copyright (C) 1991 The Root Group, Inc.", +" sudo comes with ABSOLUTELY NO WARRANTY. This is free software,", +" and you are welcome to redistribute it under certain conditions.", +#endif + +"We trust you have received the usual lecture from the local Systems", +"Administrator. It usually boils down to these two things:", + +" #1) Respect the privacy of others.", +" #2) Think before you type." +); +} diff --git a/logging.c b/logging.c new file mode 100644 index 000000000..f5f8c9db0 --- /dev/null +++ b/logging.c @@ -0,0 +1,440 @@ +/* + * sudo version 1.1 allows users to execute commands as root + * Copyright (C) 1991 The Root Group, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you make modifications to the source, we would be happy to have + * them to include in future releases. Feel free to send them to: + * Jeff Nieusma nieusma@rootgroup.com + * 3959 Arbol CT (303) 447-8093 + * Boulder, CO 80301-1752 + * + **************************************************************** + * + * logging.c + * + * this file supports the general logging facilities + * if you want to change any error messages, this is probably + * the place to be... + * + * Jeff Nieusma Thu Mar 21 23:39:04 MST 1991 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "sudo.h" + +void log_error(); +void readchild(); +static void send_mail(); +static void reapchild(); +static int appropriate(); + +static char logline[MAXLOGLEN+8]; + +/********************************************************************** + * + * log_error() + * + * This function attempts to deliver mail to ALERTMAIL and either + * syslogs the error or writes it to the log file + */ + +void log_error( code ) +int code; +{ +char cwd[MAXPATHLEN+1]; +int argc; +char **argv; +register char *p; +register int count; +#ifndef SYSLOG +register FILE *fp; +time_t now; +#else +register int pri; /* syslog priority */ +#endif + + + +/* there is no need to log the date and time twice if using syslog */ + +#ifndef SYSLOG +now=time ( (time_t) 0 ); +sprintf( logline, "%19.19s : %8.8s : ", ctime (&now), user ); +#else +sprintf( logline, "%8.8s : ", user ); +#endif + +p = logline + strlen(logline); /* we need a pointer to the end of logline */ +#ifdef hpux +getcwd(cwd, (size_t)(MAXPATHLEN+1)); /* so we know where we are... */ +#else +getwd(cwd); /* so we know where we are... */ +#endif + +switch ( code ) { + + case ALL_SYSTEMS_GO: + sprintf ( p, "PWD=%s ; COMMAND=", cwd ); +#ifdef SYSLOG + pri=Syslog_priority_OK; +#endif + break; + + case VALIDATE_NO_USER: + sprintf ( p, "user NOT in sudoers ; PWD=%s ; COMMAND=", cwd ); +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + case VALIDATE_NOT_OK: + sprintf ( p, "command not allowed ; PWD=%s ; COMMAND=", cwd ); +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + case VALIDATE_ERROR: + sprintf ( p, "error in %s ; PWD=%s ; command: ", SUDOERS, cwd ); +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + case GLOBAL_NO_PW_ENT: + sprintf ( p, "There is no /etc/passwd entry for uid %d. ", uid ); +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + case PASSWORD_NOT_CORRECT: + sprintf ( p, "%d incorrect passwords ; PWD=%s ; COMMAND=", + TRIES_FOR_PASSWORD, cwd ); +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + case GLOBAL_NO_HOSTNAME: + strcat ( p, "This machine does not have a hostname " ); +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + case NO_SUDOERS_FILE: + switch ( errno ) { + case ENOENT: + sprintf ( p, "There is no %s file. ", SUDOERS ); + break; + case EACCES: + sprintf ( p, "%s needs to run setuid root. ", Argv[0] ); + break; + default: + sprintf ( p, "There is a problem opening %s ", SUDOERS ); + break; + } +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + case GLOBAL_HOST_UNREGISTERED: + sprintf ( p, "gethostbyname() cannot find host %s ", host ); +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + default: + strcat ( p, "found a wierd error : "); +#ifdef SYSLOG + pri=Syslog_priority_NO; +#endif + break; + + } + + +/* if this error is from load_globals() don't put argv in the message */ +if ( ! ( code & GLOBAL_PROBLEM ) ) { + + strcat ( logline, cmnd ); /* stuff the command into the logline */ + strcat ( logline, " "); + + argc = Argc-2; + argv = Argv; argv++; + p = logline + strlen(logline); + count = (int)(logline + MAXLOGLEN - p); + + /* now stuff as much of the rest of the line as will fit */ + while ( count > 0 && argc-- ) { + strncpy ( p, *++argv, count ); + strcat ( p, " "); + p += 1 + (count < strlen(*argv) ? count : strlen(*argv) ); + count = (int)(logline + MAXLOGLEN - p); + } + if ( count <= 0 ) /* if the line is too long, */ + strcat ( p, " ... " ); /* add an elipsis to the end */ + + } + +if ( appropriate(code) ) + send_mail(); + +#ifdef SYSLOG + +openlog ( Syslog_ident, Syslog_options, Syslog_facility ); +syslog ( pri, logline ); +closelog(); + +#else + +if ( (fp = fopen ( LOGFILE, "a" )) == NULL ) { + sprintf ( logline, "Can\'t open log file: %s", LOGFILE ); + send_mail(); + } +else { + fprintf ( fp, "%s\n", logline ); + (void) fclose (fp); +} + + +#endif + +} + + + + +/********************************************************************** + * + * send_mail() + * + * This function attempts to mail to ALERTMAIL about the sudo error + * + */ + +char *exec_argv[]= { "sendmail" , + "-t" , + ALERTMAIL , + (char *) NULL }; + + +static void send_mail() +{ +char *mailer=MAILER; +char *subject=MAILSUBJECT; +int fd[2]; +char buf[MAXLOGLEN+1024]; + +if ( (mailer = find_path ( mailer )) == NULL ) { + fprintf (stderr, "%s not found\n", mailer ); + exit (1); + } + +signal ( SIGCHLD, reapchild ); + +if ( fork () ) return; + +/* we don't want any security problems ... */ +if ( setuid ( uid ) ) { + perror("setuid(uid)"); + exit(1); + } + +signal ( SIGHUP, SIG_IGN ); +signal ( SIGINT, SIG_IGN ); +signal ( SIGQUIT, SIG_IGN ); + +if ( pipe(fd) ) { + perror( "send_mail: pipe" ); + exit ( 1 ); + } + +(void) dup2 ( fd[0], 0 ); +(void) dup2 ( fd[1], 1 ); +(void) close (fd[0]); +(void) close (fd[1]); + +if ( ! fork () ) { + + + /* child parent */ + + (void) close(1); + execv ( mailer, exec_argv ); + /* this should not happen */ + perror( "execv"); + exit (1); + + } + +else { + + (void) close(0); + + /* feed the data to sendmail */ + sprintf (buf, "To: %s\nSubject: %s\n\n%s\n\n", + ALERTMAIL, subject, logline ); + write ( 1, buf, strlen(buf)); + close ( 1 ); + + exit ( 0 ); + } + +} + + + + + +/**************************************************************** + * + * reapchild() + * + * This function gets rid fo all the ugly zombies + */ + +static void reapchild () +{ +(void) wait ( NULL ); +} + + + + + +/********************************************************************** + * + * inform_user () + * + * This function lets the user know what is happening + * when an error occurs + */ + +void inform_user( code ) +int code; +{ + +switch ( code ) { + + case VALIDATE_NO_USER: + fprintf( stderr, + "%s is not in the sudoers file. This incident will be reported.\n\n", + user ); + break; + + case VALIDATE_NOT_OK: + fprintf( stderr, + "Sorry, user %s is not allowed to execute %s\n\n", + user, cmnd ); + break; + + case VALIDATE_ERROR: + fprintf( stderr, + "Sorry, there is a fatal error in the sudoers file.\n\n" ); + break; + + case GLOBAL_NO_PW_ENT: + fprintf ( stderr, + "Intruder Alert! You don\'t exist in the passwd file\n\n"); + break; + + case GLOBAL_NO_HOSTNAME: + fprintf ( stderr, + "This machine does not have a hostname\n\n" ); + break; + + case GLOBAL_HOST_UNREGISTERED: + fprintf ( stderr, + "This machine is not available via gethostbyname()\n\n"); + break; + + case PASSWORD_NOT_CORRECT: + fprintf ( stderr, "Password not entered correctly after %d tries\n\n", + TRIES_FOR_PASSWORD ); + break; + + default: + fprintf ( stderr, + "Something wierd happened.\n\n" ); + break; + + } + +} + + + + + +/**************************************************************** + * + * appropriate() + * + * This function determines whether to send mail or not... + */ + +static int appropriate( code ) +int code; +{ + +switch ( code ) { + +/* these will NOT send mail */ + + case VALIDATE_OK: + case PASSWORD_NOT_CORRECT: +/* case ALL_SYSTEMS_GO: this is the same as OK */ + return (0); + break; + + case VALIDATE_NO_USER: +#ifdef SEND_MAIL_WHEN_NO_USER + return (1); +#else + return (0); +#endif + break; + + case VALIDATE_NOT_OK: +#ifdef SEND_MAIL_WHEN_NOT_OK + return (1); +#else + return (0); +#endif + break; + +/* these WILL send mail */ + + case VALIDATE_ERROR: + case NO_SUDOERS_FILE: + default: + return (1); + break; + + } +} diff --git a/parse.c b/parse.c new file mode 100644 index 000000000..d8cf113bd --- /dev/null +++ b/parse.c @@ -0,0 +1,405 @@ +/* + * sudo version 1.1 allows users to execute commands as root + * Copyright (C) 1991 The Root Group, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you make modifications to the source, we would be happy to have + * them to include in future releases. Feel free to send them to: + * Jeff Nieusma nieusma@rootgroup.com + * 3959 Arbol CT (303) 447-8093 + * Boulder, CO 80301-1752 + * +******************************************************************************** +* parse.c, sudo project +* David R. Hieb +* March 18, 1991 +* +* routines to implement and maintain the parsing and list management. +*******************************************************************************/ +#include +#include +#include +#include +#include + +#include "sudo.h" + +/* there are 3 main lists (User, Host_Alias, Cmnd_Alias) and 1 extra list */ +#define NUM_LISTS 3+1 + +extern char *user, *host, *cmnd; +extern FILE *yyin, *yyout; + +int user_list_found = FALSE; +int list_num, new_list[NUM_LISTS]; +int parse_error = FALSE, found_user = FALSE; +int next_type, num_host_alias = 0, num_cmnd_alias = 0; +LINK tmp_ptr, reset_ptr, save_ptr, list_ptr[NUM_LISTS]; + +/******************************************************************************* +* inserts a node into list 'list_num' and updates list_ptr[list_num] +*******************************************************************************/ +void insert_member(list_num, token_type, op_type, data_string) +int token_type, list_num; +char op_type; +char *data_string; +{ + tmp_ptr = (LINK)malloc(sizeof(LIST)); + tmp_ptr->type = token_type; + tmp_ptr->op = op_type; + tmp_ptr->data = (char *)malloc(strlen(data_string)+1); + strcpy(tmp_ptr->data, data_string); + tmp_ptr->next = (new_list[list_num] == TRUE) ? NULL : list_ptr[list_num]; + + list_ptr[list_num] = list_ptr[EXTRA_LIST] = tmp_ptr; +} + +/******************************************************************************* +* diagnostic list printing utility that prints list 'list_num' +*******************************************************************************/ +void print_list(list_num) +int list_num; +{ +LINK tmptmp_ptr; + +tmptmp_ptr = list_ptr[list_num]; + +while (list_ptr[list_num] != NULL) { + printf("type = %d, op = %c, data = %s\n", list_ptr[list_num]->type, + list_ptr[list_num]->op, list_ptr[list_num]->data); + tmp_ptr = list_ptr[list_num]; + list_ptr[list_num] = tmp_ptr->next; + } +list_ptr[list_num] = tmptmp_ptr; +} + +/******************************************************************************* +* delete list utility that deletes list 'list_num' +*******************************************************************************/ +void delete_list(list_num) +int list_num; +{ +while (list_ptr[list_num] != NULL) { + tmp_ptr = list_ptr[list_num]; + list_ptr[list_num] = tmp_ptr->next; +/* free(tmp_ptr); */ + } +} + +/******************************************************************************* +* this routine is what the lex/yacc code calls to build the different lists. +* once the lists are all built, control eventually returns to validate(). +*******************************************************************************/ +int call_back(token_type, op_type, data_string) +int token_type; +char op_type; +char *data_string; +{ +/* all nodes start out in the extra list since the node name is received last */ +list_num = EXTRA_LIST; + +/* + * if the last received node is TYPE1, then we can classify the list + * and effectively transfer the extra list to the correct list type. + */ +if (token_type == TYPE1) { + /* we have just build a "Host_Alias" list */ + if (strcmp(data_string, "Host_Alias") == 0) { + list_num = HOST_LIST; + if (num_host_alias > 0) { + reset_ptr->next = list_ptr[HOST_LIST]; + } + num_host_alias++; + } + /* we have just build a "Cmnd_Alias" list */ + else if (strcmp(data_string, "Cmnd_Alias") == 0) { + list_num = CMND_LIST; + if (num_cmnd_alias > 0) { + reset_ptr->next = list_ptr[CMND_LIST]; + } + num_cmnd_alias++; + } + /* we have just build a "User" list */ + else { + list_num = USER_LIST; + user_list_found = TRUE; + } + new_list[EXTRA_LIST] = TRUE; + new_list[list_num] = FALSE; + list_ptr[list_num] = list_ptr[EXTRA_LIST]; + } + +/* actually link the new node into list 'list_num' */ +insert_member(list_num, token_type, op_type, data_string); + +if (new_list[list_num] == TRUE) { + reset_ptr = list_ptr[list_num]; + new_list[list_num] = FALSE; + } + +/* + * we process one user record at a time from the sudoers file. if we + * find the user were looking for, we return to lex/yacc declaring + * that we have done so. otherwise, we reset the user list, delete the + * nodes and start over again looking for the user. + */ +if (user_list_found == TRUE) { + if (list_ptr[list_num]->type == TYPE1 && + strcmp(list_ptr[list_num]->data, user) == 0) { + return(FOUND_USER); + } + else { + new_list[list_num] = TRUE; + user_list_found = FALSE; + delete_list(list_num); + } + } +return(NOT_FOUND_USER); +} + +/******************************************************************************* +* this routine is called from cmnd_check() to resolve whether or not +* a user is permitted to perform a to-yet-be-determined command for +* a certain host name. +*******************************************************************************/ +int host_type_ok() +{ +/* check for the reserved keyword 'ALL'. if so, don't check the host name */ +if (strcmp(list_ptr[USER_LIST]->data, "ALL") == 0) { + return(TRUE); + } +/* this case is the normal lowercase hostname */ +else if (isupper(list_ptr[USER_LIST]->data[0]) == FALSE) { + return(strcmp(list_ptr[USER_LIST]->data, host) == 0); + } +/* by now we have a Host_Alias that will have to be expanded */ +else { + save_ptr = list_ptr[HOST_LIST]; + while (list_ptr[HOST_LIST] != NULL) { + if ((list_ptr[HOST_LIST]->type == TYPE2) && + (strcmp(list_ptr[HOST_LIST]->data, + list_ptr[USER_LIST]->data) == 0)) { + next_type = list_ptr[HOST_LIST]->next->type; + tmp_ptr = list_ptr[HOST_LIST]; + list_ptr[HOST_LIST] = tmp_ptr->next; + while (next_type == TYPE3) { + if (strcmp(list_ptr[HOST_LIST]->data, host) == 0) { + list_ptr[HOST_LIST] = save_ptr; + return(TRUE); + } + if (list_ptr[HOST_LIST]->next != NULL) { + next_type = list_ptr[HOST_LIST]->next->type; + tmp_ptr = list_ptr[HOST_LIST]; + list_ptr[HOST_LIST] = tmp_ptr->next; + } + else { + next_type = ~TYPE3; + } + } + } + else { + tmp_ptr = list_ptr[HOST_LIST]; + list_ptr[HOST_LIST] = tmp_ptr->next; + } + } + list_ptr[HOST_LIST] = save_ptr; + return(FALSE); + } +} + +/******************************************************************************* +* this routine is called from cmnd_check() to resolve whether or not +* a user is permitted to perform a certain command on the already +* established host. +*******************************************************************************/ +int cmnd_type_ok() +{ +/* check for the reserved keyword 'ALL'. */ +if (strcmp(list_ptr[USER_LIST]->data, "ALL") == 0) { + /* if the command has an absolute path, let them do it */ + if (cmnd[0] == '/') { + return(MATCH); + } + /* if the command does not have an absolute path, forget it */ + else { + return(NO_MATCH); + } + } +/* if the command has an absolute path, check it out */ +else if (list_ptr[USER_LIST]->data[0] == '/') { + /* op | data | return value + *--------------------------------- + * ' ' | No Match | return(NO_MATCH) + * '!' | No Match | return(NO_MATCH) + * ' ' | A Match | return(MATCH) + * '!' | A Match | return(QUIT_NOW) + * + * these special cases are important in subtracting from the + * Universe of commands in something like: + * user machine=ALL,!/bin/rm,!/etc/named ... + */ + if (strcmp(list_ptr[USER_LIST]->data, cmnd) == 0) { + if (list_ptr[USER_LIST]->op == '!') { + return(QUIT_NOW); + } + else { + return(MATCH); + } + } + else { + return(NO_MATCH); + } + } +/* by now we have a Cmnd_Alias that will have to be expanded */ +else { + save_ptr = list_ptr[CMND_LIST]; + while (list_ptr[CMND_LIST] != NULL) { + if ((list_ptr[CMND_LIST]->type == TYPE2) && + (strcmp(list_ptr[CMND_LIST]->data, + list_ptr[USER_LIST]->data) == 0)) { + next_type = list_ptr[CMND_LIST]->next->type; + tmp_ptr = list_ptr[CMND_LIST]; + list_ptr[CMND_LIST] = tmp_ptr->next; + while (next_type == TYPE3) { + if (strcmp(list_ptr[CMND_LIST]->data, cmnd) == 0) { + if (list_ptr[USER_LIST]->op == '!') { + list_ptr[CMND_LIST] = save_ptr; + return(QUIT_NOW); + } + else { + list_ptr[CMND_LIST] = save_ptr; + return(MATCH); + } + } + if (list_ptr[CMND_LIST]->next != NULL) { + next_type = list_ptr[CMND_LIST]->next->type; + tmp_ptr = list_ptr[CMND_LIST]; + list_ptr[CMND_LIST] = tmp_ptr->next; + } + else { + next_type = ~TYPE3; + } + } + } + else { + tmp_ptr = list_ptr[CMND_LIST]; + list_ptr[CMND_LIST] = tmp_ptr->next; + } + } + list_ptr[CMND_LIST] = save_ptr; + return(NO_MATCH); + } +} + +/******************************************************************************* +* this routine is called from validate() after the call_back() routine +* has built all the possible lists. this routine steps thru the user list +* calling on host_type_ok() and cmnd_type_ok() trying to resolve whether +* or not the user will be able to execute the command on the host. +*******************************************************************************/ +int cmnd_check() +{ +int return_code; + +while (list_ptr[USER_LIST] != NULL) { + if ((list_ptr[USER_LIST]->type == TYPE2) && host_type_ok()) { + next_type = list_ptr[USER_LIST]->next->type; + tmp_ptr = list_ptr[USER_LIST]; + list_ptr[USER_LIST] = tmp_ptr->next; + while (next_type == TYPE3) { + return_code = cmnd_type_ok(); + if (return_code == MATCH) { + return(VALIDATE_OK); + } + else if (return_code == QUIT_NOW) { + return(VALIDATE_NOT_OK); + } + if (list_ptr[USER_LIST]->next != NULL) { + next_type = list_ptr[USER_LIST]->next->type; + tmp_ptr = list_ptr[USER_LIST]; + list_ptr[USER_LIST] = tmp_ptr->next; + } + else { + next_type = ~TYPE3; + } + } + } + else { + tmp_ptr = list_ptr[USER_LIST]; + list_ptr[USER_LIST] = tmp_ptr->next; + } + } +return(VALIDATE_NOT_OK); +} + +/******************************************************************************* +* this routine is called from the sudo.c module and tries to validate +* the user, host and command triplet. +*******************************************************************************/ +int validate() +{ +FILE *sudoers_fp; +int i, return_code; + +if ((sudoers_fp = fopen(SUDOERS, "r")) == NULL ) { + perror(SUDOERS); + log_error( NO_SUDOERS_FILE ); + exit(1); + } + +yyin = sudoers_fp; +yyout = stdout; + +for (i = 0; i < NUM_LISTS; i++) + new_list[i] = TRUE; + +/* + * yyparse() returns with one of 3 values: 0) yyparse() worked fine; + * 1) yyparse() failed; FOUND_USER) the user was found and yyparse() + * was returned from prematurely. + */ +return_code = yyparse(); + +/* don't need to keep this open... */ +(void) fclose (sudoers_fp); + +/* if a parsing error occurred, set return_code accordingly */ +if (parse_error == TRUE) { + return_code = PARSE_ERROR; + } + +/* if the user was not found, set the return_code accordingly */ +if (found_user == FALSE) { + return_code = NOT_FOUND_USER; + } + +/* handle the 3 cases individually &*/ +switch(return_code) { + case FOUND_USER: + return_code = cmnd_check(); + delete_list(USER_LIST); + delete_list(HOST_LIST); + delete_list(CMND_LIST); + return(return_code); + break; + case NOT_FOUND_USER: + return(VALIDATE_NO_USER); + break; + case PARSE_ERROR: + return(VALIDATE_ERROR); + break; + } +}