~ubuntu-branches/ubuntu/oneiric/sudo/oneiric-updates

1.2.7 by Bdale Garbee
Import upstream version 1.7.0
1
%{
2
/*
3
 * Copyright (c) 1996, 1998-2005, 2007-2008
4
 *	Todd C. Miller <Todd.Miller@courtesan.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
18
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
19
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20
 *
21
 * Sponsored in part by the Defense Advanced Research Projects
22
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
23
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
24
 */
25
26
#include <config.h>
27
28
#include <sys/types.h>
29
#include <sys/param.h>
30
#include <stdio.h>
31
#ifdef STDC_HEADERS
32
# include <stdlib.h>
33
# include <stddef.h>
34
#else
35
# ifdef HAVE_STDLIB_H
36
#  include <stdlib.h>
37
# endif
38
#endif /* STDC_HEADERS */
39
#ifdef HAVE_STRING_H
40
# include <string.h>
41
#else
42
# ifdef HAVE_STRINGS_H
43
#  include <strings.h>
44
# endif
45
#endif /* HAVE_STRING_H */
46
#ifdef HAVE_UNISTD_H
47
# include <unistd.h>
48
#endif /* HAVE_UNISTD_H */
49
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
50
# include <malloc.h>
51
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
52
#include <ctype.h>
53
#include "sudo.h"
54
#include "parse.h"
55
#include <gram.h>
56
57
#ifndef lint
58
__unused static const char rcsid[] = "$Sudo: toke.l,v 1.27 2008/11/24 00:41:36 millert Exp $";
59
#endif /* lint */
60
61
extern YYSTYPE yylval;
62
int sudolineno = 1;
63
char *sudoers;
64
static int sawspace = 0;
65
static int arg_len = 0;
66
static int arg_size = 0;
67
68
static int append		__P((char *, int));
69
static int _fill		__P((char *, int, int));
70
static int fill_cmnd		__P((char *, int));
71
static int fill_args		__P((char *, int, int));
72
static int switch_buffer	__P((char *));
73
static int ipv6_valid		__P((const char *s));
74
static char *parse_include	__P((char *));
75
extern void yyerror		__P((const char *));
76
77
#define fill(a, b)		_fill(a, b, 0)
78
79
#define	push_include(_p)	(switch_buffer((_p)))
80
#define	pop_include()		(switch_buffer(NULL))
81
82
/* realloc() to size + COMMANDARGINC to make room for command args */
83
#define COMMANDARGINC	64
84
85
#ifdef TRACELEXER
86
#define LEXTRACE(msg)	fputs(msg, stderr)
87
#else
88
#define LEXTRACE(msg)
89
#endif
90
%}
91
92
HEX16			[0-9A-Fa-f]{1,4}
93
OCTET			(1?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5])
94
IPV4ADDR		{OCTET}(\.{OCTET}){3}
95
IPV6ADDR		({HEX16}?:){2,7}{HEX16}?|({HEX16}?:){2,6}:{IPV4ADDR}
96
97
HOSTNAME		[[:alnum:]_-]+
98
WORD			([^#>!=:,\(\) \t\n\\]|\\[^\n])+
99
ID			#-?[0-9]+
100
PATH			\/(\\[\,:= \t#]|[^\,:=\\ \t\n#])+
101
ENVAR			([^#!=, \t\n\\\"]|\\[^\n])([^#=, \t\n\\\"]|\\[^\n])*
102
DEFVAR			[a-z_]+
103
104
%option nounput
105
%option noyywrap
106
107
%s	GOTDEFS
108
%x	GOTCMND
109
%x	STARTDEFS
110
%x	INDEFS
111
%x	INSTR
112
113
%%
114
<GOTDEFS>[[:blank:]]+	BEGIN STARTDEFS;
115
116
<STARTDEFS>{DEFVAR}	{
117
			    BEGIN INDEFS;
118
			    LEXTRACE("DEFVAR ");
119
			    if (!fill(yytext, yyleng))
120
				yyterminate();
121
			    return(DEFVAR);
122
			}
123
124
<INDEFS>{
125
    ,			{
126
			    BEGIN STARTDEFS;
127
			    LEXTRACE(", ");
128
			    return(',');
129
			}			/* return ',' */
130
131
    =			{
132
			    LEXTRACE("= ");
133
			    return('=');
134
			}			/* return '=' */
135
136
    \+=			{
137
			    LEXTRACE("+= ");
138
			    return('+');
139
			}			/* return '+' */
140
141
    -=			{
142
			    LEXTRACE("-= ");
143
			    return('-');
144
			}			/* return '-' */
145
146
    \"			{
147
			    LEXTRACE("BEGINSTR ");
148
			    yylval.string = NULL;
149
			    BEGIN INSTR;
150
			}
151
152
    {ENVAR}		{
153
			    LEXTRACE("WORD(2) ");
154
			    if (!fill(yytext, yyleng))
155
				yyterminate();
156
			    return(WORD);
157
			}
158
}
159
160
<INSTR>{
161
    \\[[:blank:]]*\n[[:blank:]]*	{
162
			    /* Line continuation char followed by newline. */
163
			    ++sudolineno;
164
			    LEXTRACE("\n");
165
			}
166
167
    \"			{
168
			    LEXTRACE("ENDSTR ");
169
			    BEGIN INDEFS;
170
			    return(WORD);
171
			}
172
173
    \\			{
174
			    LEXTRACE("BACKSLASH ");
175
			    if (!append(yytext, yyleng))
176
				yyterminate();
177
			}
178
179
    ([^\"\n\\]|\\\")+	{
180
			    LEXTRACE("STRBODY ");
181
			    if (!append(yytext, yyleng))
182
				yyterminate();
183
			}
184
}
185
186
<GOTCMND>{
187
    \\[\*\?\[\]\!]	{
188
			    /* quoted fnmatch glob char, pass verbatim */
189
			    LEXTRACE("QUOTEDCHAR ");
190
			    if (!fill_args(yytext, 2, sawspace))
191
				yyterminate();
192
			    sawspace = FALSE;
193
			}
194
195
    \\[:\\,= \t#]	{
196
			    /* quoted sudoers special char, strip backslash */
197
			    LEXTRACE("QUOTEDCHAR ");
198
			    if (!fill_args(yytext + 1, 1, sawspace))
199
				yyterminate();
200
			    sawspace = FALSE;
201
			}
202
203
    [#:\,=\n]		{
204
			    BEGIN INITIAL;
205
			    yyless(0);
206
			    return(COMMAND);
207
			}			/* end of command line args */
208
209
    [^\\:, \t\n]+ 	{
210
			    LEXTRACE("ARG ");
211
			    if (!fill_args(yytext, yyleng, sawspace))
212
				yyterminate();
213
			    sawspace = FALSE;
214
			}			/* a command line arg */
215
}
216
217
<INITIAL>^#include[[:blank:]]+\/.*\n {
218
			    char *path;
219
220
			    if ((path = parse_include(yytext)) == NULL)
221
				yyterminate();
222
223
			    LEXTRACE("INCLUDE\n");
224
225
			    /* Push current buffer and switch to include file */
226
			    if (!push_include(path))
227
				yyterminate();
228
			}
229
230
<INITIAL>^[[:blank:]]*Defaults([:@>\!]{WORD})? {
231
			    int n;
232
			    for (n = 0; isblank((unsigned char)yytext[n]); n++)
233
				continue;
234
			    n += 8;
235
			    BEGIN GOTDEFS;
236
			    switch (yytext[n++]) {
237
				case ':':
238
				    yyless(n);
239
				    LEXTRACE("DEFAULTS_USER ");
240
				    return(DEFAULTS_USER);
241
				case '>':
242
				    yyless(n);
243
				    LEXTRACE("DEFAULTS_RUNAS ");
244
				    return(DEFAULTS_RUNAS);
245
				case '@':
246
				    yyless(n);
247
				    LEXTRACE("DEFAULTS_HOST ");
248
				    return(DEFAULTS_HOST);
249
				case '!':
250
				    yyless(n);
251
				    LEXTRACE("DEFAULTS_CMND ");
252
				    return(DEFAULTS_CMND);
253
				default:
254
				    LEXTRACE("DEFAULTS ");
255
				    return(DEFAULTS);
256
			    }
257
			}
258
259
<INITIAL>^[[:blank:]]*(Host|Cmnd|User|Runas)_Alias	{
260
			    int n;
261
			    for (n = 0; isblank((unsigned char)yytext[n]); n++)
262
				continue;
263
			    switch (yytext[n]) {
264
				case 'H':
265
				    LEXTRACE("HOSTALIAS ");
266
				    return(HOSTALIAS);
267
				case 'C':
268
				    LEXTRACE("CMNDALIAS ");
269
				    return(CMNDALIAS);
270
				case 'U':
271
				    LEXTRACE("USERALIAS ");
272
				    return(USERALIAS);
273
				case 'R':
274
				    LEXTRACE("RUNASALIAS ");
275
				    return(RUNASALIAS);
276
			    }
277
			}
278
279
NOPASSWD[[:blank:]]*:	{
280
				/* cmnd does not require passwd for this user */
281
			    	LEXTRACE("NOPASSWD ");
282
			    	return(NOPASSWD);
283
			}
284
285
PASSWD[[:blank:]]*:	{
286
				/* cmnd requires passwd for this user */
287
			    	LEXTRACE("PASSWD ");
288
			    	return(PASSWD);
289
			}
290
291
NOEXEC[[:blank:]]*:	{
292
			    	LEXTRACE("NOEXEC ");
293
			    	return(NOEXEC);
294
			}
295
296
EXEC[[:blank:]]*:	{
297
			    	LEXTRACE("EXEC ");
298
			    	return(EXEC);
299
			}
300
301
SETENV[[:blank:]]*:	{
302
			    	LEXTRACE("SETENV ");
303
			    	return(SETENV);
304
			}
305
306
NOSETENV[[:blank:]]*:	{
307
			    	LEXTRACE("NOSETENV ");
308
			    	return(NOSETENV);
309
			}
310
311
\+{WORD}		{
312
			    /* netgroup */
313
			    if (!fill(yytext, yyleng))
314
				yyterminate();
315
			    LEXTRACE("NETGROUP ");
316
			    return(NETGROUP);
317
			}
318
319
\%{WORD}		{
320
			    /* UN*X group */
321
			    if (!fill(yytext, yyleng))
322
				yyterminate();
323
			    LEXTRACE("USERGROUP ");
324
			    return(USERGROUP);
325
			}
326
327
{IPV4ADDR}(\/{IPV4ADDR})? {
328
			    if (!fill(yytext, yyleng))
329
				yyterminate();
330
			    LEXTRACE("NTWKADDR ");
331
			    return(NTWKADDR);
332
			}
333
334
{IPV4ADDR}\/([12][0-9]*|3[0-2]*) {
335
			    if (!fill(yytext, yyleng))
336
				yyterminate();
337
			    LEXTRACE("NTWKADDR ");
338
			    return(NTWKADDR);
339
			}
340
341
{IPV6ADDR}(\/{IPV6ADDR})? {
342
			    if (!ipv6_valid(yytext)) {
343
				LEXTRACE("ERROR ");
344
				return(ERROR);
345
			    }
346
			    if (!fill(yytext, yyleng))
347
				yyterminate();
348
			    LEXTRACE("NTWKADDR ");
349
			    return(NTWKADDR);
350
			}
351
352
{IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) {
353
			    if (!ipv6_valid(yytext)) {
354
				LEXTRACE("ERROR ");
355
				return(ERROR);
356
			    }
357
			    if (!fill(yytext, yyleng))
358
				yyterminate();
359
			    LEXTRACE("NTWKADDR ");
360
			    return(NTWKADDR);
361
			}
362
363
[[:upper:]][[:upper:][:digit:]_]* {
364
			    if (strcmp(yytext, "ALL") == 0) {
365
				LEXTRACE("ALL ");
366
				return(ALL);
367
			    }
368
#ifdef HAVE_SELINUX
369
			    /* XXX - restrict type/role to initial state */
370
			    if (strcmp(yytext, "TYPE") == 0) {
371
				LEXTRACE("TYPE ");
372
				return(TYPE);
373
			    }
374
			    if (strcmp(yytext, "ROLE") == 0) {
375
				LEXTRACE("ROLE ");
376
				return(ROLE);
377
			    }
378
#endif /* HAVE_SELINUX */
379
			    if (!fill(yytext, yyleng))
380
				yyterminate();
381
			    LEXTRACE("ALIAS ");
382
			    return(ALIAS);
383
			}
384
385
<GOTDEFS>({PATH}|sudoedit) {
386
			    /* no command args allowed for Defaults!/path */
387
			    if (!fill_cmnd(yytext, yyleng))
388
				yyterminate();
389
			    LEXTRACE("COMMAND ");
390
			    return(COMMAND);
391
			}
392
393
sudoedit		{
394
			    BEGIN GOTCMND;
395
			    LEXTRACE("COMMAND ");
396
			    if (!fill_cmnd(yytext, yyleng))
397
				yyterminate();
398
			}			/* sudo -e */
399
400
{PATH}			{
401
			    /* directories can't have args... */
402
			    if (yytext[yyleng - 1] == '/') {
403
				LEXTRACE("COMMAND ");
404
				if (!fill_cmnd(yytext, yyleng))
405
				    yyterminate();
406
				return(COMMAND);
407
			    } else {
408
				BEGIN GOTCMND;
409
				LEXTRACE("COMMAND ");
410
				if (!fill_cmnd(yytext, yyleng))
411
				    yyterminate();
412
			    }
413
			}			/* a pathname */
414
415
<INITIAL,GOTDEFS>({ID}|{WORD}) {
416
			    /* a word */
417
			    if (!fill(yytext, yyleng))
418
				yyterminate();
419
			    LEXTRACE("WORD(4) ");
420
			    return(WORD);
421
			}
422
423
\(			{
424
			    LEXTRACE("( ");
425
			    return ('(');
426
			}
427
428
\)			{
429
			    LEXTRACE(") ");
430
			    return(')');
431
			}
432
433
,			{
434
			    LEXTRACE(", ");
435
			    return(',');
436
			}			/* return ',' */
437
438
=			{
439
			    LEXTRACE("= ");
440
			    return('=');
441
			}			/* return '=' */
442
443
:			{
444
			    LEXTRACE(": ");
445
			    return(':');
446
			}			/* return ':' */
447
448
<*>!+			{
449
			    if (yyleng % 2 == 1)
450
				return('!');	/* return '!' */
451
			}
452
453
<*>\n			{
454
			    BEGIN INITIAL;
455
			    ++sudolineno;
456
			    LEXTRACE("\n");
457
			    return(COMMENT);
458
			}			/* return newline */
459
460
<*>[[:blank:]]+		{			/* throw away space/tabs */
461
			    sawspace = TRUE;	/* but remember for fill_args */
462
			}
463
464
<*>\\[[:blank:]]*\n	{
465
			    sawspace = TRUE;	/* remember for fill_args */
466
			    ++sudolineno;
467
			    LEXTRACE("\n\t");
468
			}			/* throw away EOL after \ */
469
470
<INITIAL,STARTDEFS,INDEFS>#([^\n0-9-].*)?\n	{
471
			    BEGIN INITIAL;
472
			    ++sudolineno;
473
			    LEXTRACE("\n");
474
			    return(COMMENT);
475
			}			/* return comments */
476
477
<*>.			{
478
			    LEXTRACE("ERROR ");
479
			    return(ERROR);
480
			}	/* parse error */
481
482
<*><<EOF>>		{
483
			    if (YY_START != INITIAL) {
484
			    	BEGIN INITIAL;
485
				LEXTRACE("ERROR ");
486
				return(ERROR);
487
			    }
488
			    if (!pop_include())
489
				yyterminate();
490
			}
491
492
%%
493
static int
494
_fill(src, len, olen)
495
    char *src;
496
    int len, olen;
497
{
498
    int i, j;
499
    char *dst;
500
501
    dst = olen ? realloc(yylval.string, olen + len + 1) : malloc(len + 1);
502
    if (dst == NULL) {
503
	yyerror("unable to allocate memory");
504
	return(FALSE);
505
    }
506
    yylval.string = dst;
507
508
    /* Copy the string and collapse any escaped characters. */
509
    dst += olen;
510
    for (i = 0, j = 0; i < len; i++, j++) {
511
	if (src[i] == '\\' && i != len - 1)
512
	    dst[j] = src[++i];
513
	else
514
	    dst[j] = src[i];
515
    }
516
    dst[j] = '\0';
517
    return(TRUE);
518
}
519
520
static int
521
append(src, len)
522
    char *src;
523
    int len;
524
{
525
    int olen = 0;
526
527
    if (yylval.string != NULL)
528
	olen = strlen(yylval.string);
529
530
    return(_fill(src, len, olen));
531
}
532
533
#define SPECIAL(c) \
534
    ((c) == ',' || (c) == ':' || (c) == '=' || (c) == ' ' || (c) == '\t' || (c) == '#')
535
536
static int
537
fill_cmnd(src, len)
538
    char *src;
539
    int len;
540
{
541
    char *dst;
542
    int i;
543
544
    arg_len = arg_size = 0;
545
546
    dst = yylval.command.cmnd = (char *) malloc(len + 1);
547
    if (yylval.command.cmnd == NULL) {
548
	yyerror("unable to allocate memory");
549
	return(FALSE);
550
    }
551
552
    /* Copy the string and collapse any escaped sudo-specific characters. */
553
    for (i = 0; i < len; i++) {
554
	if (src[i] == '\\' && i != len - 1 && SPECIAL(src[i + 1]))
555
	    *dst++ = src[++i];
556
	else
557
	    *dst++ = src[i];
558
    }
559
    *dst = '\0';
560
561
    yylval.command.args = NULL;
562
    return(TRUE);
563
}
564
565
static int
566
fill_args(s, len, addspace)
567
    char *s;
568
    int len;
569
    int addspace;
570
{
571
    int new_len;
572
    char *p;
573
574
    if (yylval.command.args == NULL) {
575
	addspace = 0;
576
	new_len = len;
577
    } else
578
	new_len = arg_len + len + addspace;
579
580
    if (new_len >= arg_size) {
581
	/* Allocate more space than we need for subsequent args */
582
	while (new_len >= (arg_size += COMMANDARGINC))
583
	    ;
584
585
	p = yylval.command.args ?
586
	    (char *) realloc(yylval.command.args, arg_size) :
587
	    (char *) malloc(arg_size);
588
	if (p == NULL) {
589
	    efree(yylval.command.args);
590
	    yyerror("unable to allocate memory");
591
	    return(FALSE);
592
	} else
593
	    yylval.command.args = p;
594
    }
595
596
    /* Efficiently append the arg (with a leading space if needed). */
597
    p = yylval.command.args + arg_len;
598
    if (addspace)
599
	*p++ = ' ';
600
    if (strlcpy(p, s, arg_size - (p - yylval.command.args)) != len) {
601
	yyerror("fill_args: buffer overflow");	/* paranoia */
602
	return(FALSE);
603
    }
604
    arg_len = new_len;
605
    return(TRUE);
606
}
607
608
struct sudoers_state {
609
    YY_BUFFER_STATE bs;
610
    char *path;
611
    int lineno;
612
};
613
614
#define MAX_SUDOERS_DEPTH	128
615
#define SUDOERS_STACK_INCREMENT	16
616
617
static int
618
switch_buffer(path)
619
    char *path;
620
{
621
    static size_t stacksize, depth;
622
    static struct sudoers_state *state;
623
    static int keepopen;
624
    FILE *fp;
625
626
    if (path != NULL) {
627
	/* push current state */
628
	if (depth >= stacksize) {
629
	    if (depth > MAX_SUDOERS_DEPTH) {
630
		yyerror("too many levels of includes");
631
		return(FALSE);
632
	    }
633
	    stacksize += SUDOERS_STACK_INCREMENT;
634
	    state = (struct sudoers_state *) realloc(state,
635
		sizeof(state) * stacksize);
636
	    if (state == NULL) {
637
		yyerror("unable to allocate memory");
638
		return(FALSE);
639
	    }
640
	}
641
	if ((fp = open_sudoers(path, &keepopen)) == NULL) {
642
	    yyerror(path);
643
	    return(FALSE);
644
	}
645
	state[depth].bs = YY_CURRENT_BUFFER;
646
	state[depth].path = sudoers;
647
	state[depth].lineno = sudolineno;
648
	depth++;
649
	sudolineno = 1;
650
	sudoers = path;
651
	yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
652
    } else {
653
	/* pop */
654
	if (depth == 0)
655
	    return(FALSE);
656
	depth--;
657
	if (!keepopen)
658
	    fclose(YY_CURRENT_BUFFER->yy_input_file);
659
	yy_delete_buffer(YY_CURRENT_BUFFER);
660
	yy_switch_to_buffer(state[depth].bs);
661
	efree(sudoers);
662
	sudoers = state[depth].path;
663
	sudolineno = state[depth].lineno;
664
	keepopen = FALSE;
665
    }
666
    return(TRUE);
667
}
668
669
static char *
670
parse_include(base)
671
    char *base;
672
{
673
    char *cp, *ep, *path;
674
    int len;
675
676
    /* Pull out path from #include line. */
677
    cp = base + sizeof("#include");
678
    while (isblank((unsigned char) *cp))
679
	cp++;
680
    ep = cp;
681
    while (*ep != '\0' && !isspace((unsigned char) *ep))
682
	ep++;
683
684
    /* Make a copy of path and return it. */
685
    len = (int)(ep - cp);
686
    if ((path = malloc(len + 1)) == NULL)
687
	yyerror("unable to allocate memory");
688
    memcpy(path, cp, len);
689
    path[len] = '\0';
690
691
    /* Push any excess characters (e.g. comment, newline) back to the lexer */
692
    if (*ep != '\0')
693
	yyless((int)(ep - base));
694
695
    return(path);
696
}
697
698
/*
699
 * Check to make sure an IPv6 address does not contain multiple instances
700
 * of the string "::".  Assumes strlen(s) >= 1.
701
 * Returns TRUE if address is valid else FALSE.
702
 */
703
static int
704
ipv6_valid(s)
705
    const char *s;
706
{
707
    int nmatch = 0;
708
709
    for (; *s != '\0'; s++) {
710
	if (s[0] == ':' && s[1] == ':') {
711
	    if (++nmatch > 1)
712
		break;
713
	}
714
	if (s[0] == '/')
715
	    nmatch = 0;			/* reset if we hit netmask */
716
    }
717
718
    return (nmatch <= 1);
719
}