]> granicus.if.org Git - sudo/commitdiff
Initial revision
authorTodd C. Miller <Todd.Miller@courtesan.com>
Fri, 11 Jun 1993 22:03:45 +0000 (22:03 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Fri, 11 Jun 1993 22:03:45 +0000 (22:03 +0000)
check.c [new file with mode: 0644]
logging.c [new file with mode: 0644]
parse.c [new file with mode: 0644]

diff --git a/check.c b/check.c
new file mode 100644 (file)
index 0000000..2769857
--- /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 <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <pwd.h>
+#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 (file)
index 0000000..f5f8c9d
--- /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 <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#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 (file)
index 0000000..d8cf113
--- /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 <stdio.h>
+#include <strings.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#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;
+    }
+}