Files
sudo/parse.yacc
1995-11-21 02:13:59 +00:00

509 lines
10 KiB
Plaintext

%{
/*
* CU sudo version 1.3.6
*
* 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.
*
* Please send bugs, changes, problems to sudo-bugs@cs.colorado.edu
*
*******************************************************************
*
* parse.yacc -- yacc parser and alias manipulation routines for sudo.
*
* Chris Jepeway <jepeway@cs.utk.edu>
*/
#ifndef lint
static char rcsid[] = "$Id$";
#endif /* lint */
#include "config.h"
#include <stdio.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif /* STDC_HEADERS */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <pwd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
#include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#ifdef HAVE_LSEARCH
#include <search.h>
#endif /* HAVE_LSEARCH */
#include "sudo.h"
#include <options.h>
#ifndef HAVE_LSEARCH
#include "emul/search.h"
#endif /* HAVE_LSEARCH */
extern int sudolineno, parse_error;
int errorlineno = -1;
int clearaliases = 1;
int printmatches = 0;
/*
* Alias types
*/
#define HOST 1
#define CMND 2
#define USER 3
/*
* The matching stack, we should not have to initialize this,
* since it is global but some compilers are just too braindamaged...
*/
#define MATCHSTACKSIZE (40)
struct matchstack match[MATCHSTACKSIZE] = { FALSE };
int top = 0;
#define push \
if (top > MATCHSTACKSIZE) \
yyerror("matching stack overflow"); \
else {\
match[top].user = -1; \
match[top].cmnd = -1; \
match[top].host = -1; \
top++; \
}
#define pop \
if (top == 0) \
yyerror("matching stack underflow"); \
else \
top--;
/*
* Protoypes
*/
extern int path_matches __P((char *, char *));
extern int addr_matches __P((char *));
extern int netgr_matches __P((char *, char *, char *));
static int find_alias __P((char *, int));
static int add_alias __P((char *, int));
static int more_aliases __P((size_t));
static char *dotcat __P((char *, char *));
void yyerror __P((char *));
void yyerror(s)
char *s;
{
/* save the line the first error occured on */
if (errorlineno == -1)
errorlineno = sudolineno;
#ifndef TRACELEXER
(void) fprintf(stderr, ">>> sudoers file: %s, line %d <<<\n", s, sudolineno);
#else
(void) fprintf(stderr, "<*> ");
#endif
parse_error = TRUE;
}
%}
%union {
char *string;
int tok;
}
%start file /* special start symbol */
%token <string> ALIAS /* an UPPERCASE alias name */
%token <string> NTWKADDR /* w.x.y.z */
%token <string> NETGROUP /* a netgroup (+NAME) */
%token <string> COMMAND /* an absolute pathname + args */
%token <string> NAME /* a mixed-case name */
%token <tok> COMMENT /* comment and/or carriage return */
%token <tok> ALL /* ALL keyword */
%token <tok> HOSTALIAS /* Host_Alias keyword */
%token <tok> CMNDALIAS /* Cmnd_Alias keyword */
%token <tok> USERALIAS /* User_Alias keyword */
%token <tok> ':' '=' ',' '!' '.' /* union member tokens */
%token <tok> ERROR
%type <string> fqdn cmnd
%%
file : entry
| file entry
;
entry : COMMENT
{ ; }
| error COMMENT
{ yyerrok; }
| { push; } user privileges {
while (top && user_matches != TRUE) {
pop;
}
}
| USERALIAS useraliases
{ ; }
| HOSTALIAS hostaliases
{ ; }
| CMNDALIAS cmndaliases
{ ; }
;
privileges : privilege
| privileges ':' privilege
;
privilege : hostspec '=' opcmndlist {
if (user_matches == TRUE) {
push;
user_matches = TRUE;
}
}
;
hostspec : ALL {
host_matches = TRUE;
}
| NTWKADDR {
if (addr_matches($1))
host_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
| NETGROUP {
if (netgr_matches($1, host, NULL))
host_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
| NAME {
if (strcmp(host, $1) == 0)
host_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
| ALIAS {
if (find_alias($1, HOST))
host_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
| fqdn {
#ifdef HAVE_STRCASECMP
if (strcasecmp($1, host) == 0)
host_matches = TRUE;
#else
if (strcmp($1, host) == 0)
host_matches = TRUE;
#endif /* HAVE_STRCASECMP */
(void) free($1);
$1 = NULL; /* XXX */
}
;
fqdn : NAME '.' NAME {
$$ = dotcat($1, $3);
(void) free($1);
(void) free($3);
$1 = $3 = NULL; /* XXX */
}
| fqdn '.' NAME {
$$ = dotcat($1, $3);
(void) free($3);
$3 = NULL; /* XXX */
}
;
opcmndlist : opcmnd
| opcmndlist ',' opcmnd
;
opcmnd : cmnd {
if (printmatches == TRUE && host_matches == TRUE &&
user_matches == TRUE)
(void) puts($1);
}
| '!' {
if (printmatches == TRUE && host_matches == TRUE
&& user_matches == TRUE) {
(void) putchar('!');
push;
user_matches = TRUE;
host_matches = TRUE;
} else {
push;
}
} opcmnd {
int cmnd_matched = cmnd_matches;
pop;
if (cmnd_matched == TRUE)
cmnd_matches = FALSE;
else if (cmnd_matched == FALSE)
cmnd_matches = TRUE;
}
;
cmnd : ALL {
cmnd_matches = TRUE;
}
| ALIAS {
if (find_alias($1, CMND))
cmnd_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
| COMMAND {
if (path_matches(cmnd, $1))
cmnd_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
;
hostaliases : hostalias
| hostaliases ':' hostalias
;
hostalias : ALIAS { push; } '=' hostlist {
if (host_matches == TRUE && !add_alias($1, HOST))
YYERROR;
pop;
}
;
hostlist : hostspec
| hostlist ',' hostspec
;
cmndaliases : cmndalias
| cmndaliases ':' cmndalias
;
cmndalias : ALIAS { push; } '=' cmndlist {
if (cmnd_matches == TRUE && !add_alias($1, CMND))
YYERROR;
pop;
(void) free($1);
$1 = NULL; /* XXX */
}
;
cmndlist : cmnd
{ ; }
| cmndlist ',' cmnd
;
useraliases : useralias
| useraliases ':' useralias
;
useralias : ALIAS { push; } '=' userlist {
if (user_matches == TRUE && !add_alias($1, USER))
YYERROR;
pop;
(void) free($1);
$1 = NULL; /* XXX */
}
;
userlist : user
{ ; }
| userlist ',' user
;
user : NAME {
if (strcmp($1, sudo_pw_ent->pw_name) == 0)
user_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
| NETGROUP {
if (netgr_matches($1, NULL, sudo_pw_ent->pw_name))
user_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
| ALIAS {
if (find_alias($1, USER))
user_matches = TRUE;
(void) free($1);
$1 = NULL; /* XXX */
}
| ALL {
user_matches = TRUE;
}
;
%%
typedef struct {
int type;
char name[BUFSIZ];
} aliasinfo;
#define MOREALIASES (32)
aliasinfo *aliases = NULL;
size_t naliases = 0;
size_t nslots = 0;
static int aliascmp(a1, a2)
const VOID *a1, *a2;
{
int r;
aliasinfo *ai1, *ai2;
ai1 = (aliasinfo *) a1;
ai2 = (aliasinfo *) a2;
r = strcmp(ai1->name, ai2->name);
if (r == 0)
r = ai1->type - ai2->type;
return(r);
}
static int add_alias(alias, type)
char *alias;
int type;
{
aliasinfo ai, *aip;
char s[512];
int ok;
ok = FALSE; /* assume failure */
ai.type = type;
(void) strcpy(ai.name, alias);
if (lfind((VOID *)&ai, (VOID *)aliases, &naliases, sizeof(ai),
aliascmp) != NULL) {
(void) sprintf(s, "Alias `%s' already defined", alias);
yyerror(s);
} else {
if (naliases == nslots && !more_aliases(nslots)) {
(void) sprintf(s, "Out of memory defining alias `%s'", alias);
yyerror(s);
}
aip = (aliasinfo *) lsearch((VOID *)&ai, (VOID *)aliases,
&naliases, sizeof(ai), aliascmp);
if (aip != NULL) {
ok = TRUE;
} else {
(void) sprintf(s, "Aliases corrupted defining alias `%s'", alias);
yyerror(s);
}
}
return(ok);
}
static int find_alias(alias, type)
char *alias;
int type;
{
aliasinfo ai;
(void) strcpy(ai.name, alias);
ai.type = type;
return(lfind((VOID *)&ai, (VOID *)aliases, &naliases,
sizeof(ai), aliascmp) != NULL);
}
static int more_aliases(nslots)
size_t nslots;
{
aliasinfo *aip;
if (nslots == 0)
aip = (aliasinfo *) malloc(MOREALIASES * sizeof(*aip));
else
aip = (aliasinfo *) realloc(aliases,
(nslots + MOREALIASES) * sizeof(*aip));
if (aip != NULL) {
aliases = aip;
nslots += MOREALIASES;
}
return(aip != NULL);
}
void dumpaliases()
{
size_t n;
for (n = 0; n < naliases; n++) {
switch (aliases[n].type) {
case HOST:
(void) puts("HOST");
break;
case CMND:
(void) puts("CMND");
break;
case USER:
(void) puts("USER");
break;
}
(void) printf("\t%s\n", aliases[n].name);
}
}
void reset_aliases()
{
if (aliases)
(void) free(aliases);
naliases = nslots = 0;
}
static char *dotcat(s1, s2)
char *s1;
char *s2;
{
int len1; /* length of param 1 */
int fulllen; /* length of params 1, 2, '.' */
char *s; /* string to return */
/* how much space do we need? */
len1 = strlen(s1);
fulllen = len1 + 1 + strlen(s2);
/* allocate the space */
s = (char *) malloc(fulllen + 1);
if (s == NULL)
yyerror("unable to allocate memory");
/* cat s1.s2 -> s efficient */
(void) strcpy(s, s1);
*(s + len1) = '.';
(void) strcpy(s + len1 + 1, s2);
return(s);
}