perl 2.0 patch #17

Larry Wall lwall at jpl-devvax.JPL.NASA.GOV
Sat Nov 19 19:42:30 AEST 1988


System: perl version 2.0
Patch #: 17
Priority: MEDIUM
Subject: patch 16 continued

Description:
	See patch 16.

Fix:	From rn, say "| patch -p -N -d DIR", where DIR is your perl source
	directory.  Outside of rn, say "cd DIR; patch -p -N <thisarticle".
	If you don't have the patch program, apply the following by hand,
	or get patch (version 2.0, latest patchlevel).

	After patching:
		Configure -d
		make depend
		make
		make test
		make install

	If patch indicates that patchlevel is the wrong version, you may need
	to apply one or more previous patches, or the patch may already
	have been applied.  See the patchlevel.h file to find out what has or
	has not been applied.  In any event, don't continue with the patch.

	If you are missing previous patches they can be obtained from me:

	Larry Wall
	lwall at jpl-devvax.jpl.nasa.gov

	If you send a mail message of the following form it will greatly speed
	processing:

	Subject: Command
	@SH mailpatch PATH perl 2.0 LIST
		   ^ note the c

	where PATH is a return path FROM ME TO YOU either in Internet notation,
	or in bang notation from some well-known host, and LIST is the number
	of one or more patches you need, separated by spaces, commas, and/or
	hyphens.  Saying 35- says everything from 35 to the end.


	You can also get the patches via anonymous FTP from
	jpl-devvax.jpl.nasa.gov (128.149.8.43).

Index: patchlevel.h
Prereq: 16
1c1
< #define PATCHLEVEL 16
---
> #define PATCHLEVEL 17

Index: perl.y
Prereq: 2.0.1.5
*** perl.y.old	Sat Nov 19 00:34:19 1988
--- perl.y	Sat Nov 19 00:34:22 1988
***************
*** 1,6 ****
! /* $Header: perl.y,v 2.0.1.5 88/10/31 16:42:23 lwall Exp $
   *
   * $Log:	perl.y,v $
   * Revision 2.0.1.5  88/10/31  16:42:23  lwall
   * patch15: printf "%%" is now more consistent
   * 
--- 1,9 ----
! /* $Header: perl.y,v 2.0.1.6 88/11/19 00:04:01 lwall Locked $
   *
   * $Log:	perl.y,v $
+  * Revision 2.0.1.6  88/11/19  00:04:01  lwall
+  * patch16: added getc function
+  * 
   * Revision 2.0.1.5  88/10/31  16:42:23  lwall
   * patch15: printf "%%" is now more consistent
   * 
***************
*** 78,84 ****
  %token <ival> APPEND OPEN WRITE SELECT CLOSE LOOPEX
  %token <ival> USING FORMAT DO SHIFT PUSH POP LVALFUN
  %token <ival> WHILE UNTIL IF UNLESS ELSE ELSIF CONTINUE SPLIT SPRINTF
! %token <ival> FOR FEOF TELL SEEK STAT 
  %token <ival> FUNC0 FUNC1 FUNC2 FUNC3 STABFUN
  %token <ival> JOIN SUB FILETEST LOCAL DELETE
  %token <ival> RELOP EQOP MULOP ADDOP
--- 81,87 ----
  %token <ival> APPEND OPEN WRITE SELECT CLOSE LOOPEX
  %token <ival> USING FORMAT DO SHIFT PUSH POP LVALFUN
  %token <ival> WHILE UNTIL IF UNLESS ELSE ELSIF CONTINUE SPLIT SPRINTF
! %token <ival> FOR FILOP TELL SEEK STAT 
  %token <ival> FUNC0 FUNC1 FUNC2 FUNC3 STABFUN
  %token <ival> JOIN SUB FILETEST LOCAL DELETE
  %token <ival> RELOP EQOP MULOP ADDOP
***************
*** 533,552 ****
  			{ $$ = make_op(O_CLOSE, 1,
  			    stab2arg(A_WORD,stabent($2,TRUE)),
  			    Nullarg, Nullarg,0); }
! 	|	FEOF '(' WORD ')'
! 			{ $$ = make_op(O_EOF, 1,
  			    stab2arg(A_WORD,stabent($3,TRUE)),
  			    Nullarg, Nullarg,0); }
! 	|	FEOF '(' expr ')'
! 			{ $$ = make_op(O_EOF, 1,
  			    $3,
  			    Nullarg, Nullarg,0); }
! 	|	FEOF '(' ')'
! 			{ $$ = make_op(O_EOF, 1,
  			    stab2arg(A_WORD,Nullstab),
  			    Nullarg, Nullarg,0); }
! 	|	FEOF
! 			{ $$ = make_op(O_EOF, 0,
  			    Nullarg, Nullarg, Nullarg,0); }
  	|	TELL '(' WORD ')'
  			{ $$ = make_op(O_TELL, 1,
--- 536,555 ----
  			{ $$ = make_op(O_CLOSE, 1,
  			    stab2arg(A_WORD,stabent($2,TRUE)),
  			    Nullarg, Nullarg,0); }
! 	|	FILOP '(' WORD ')'
! 			{ $$ = make_op($1, 1,
  			    stab2arg(A_WORD,stabent($3,TRUE)),
  			    Nullarg, Nullarg,0); }
! 	|	FILOP '(' expr ')'
! 			{ $$ = make_op($1, 1,
  			    $3,
  			    Nullarg, Nullarg,0); }
! 	|	FILOP '(' ')'
! 			{ $$ = make_op($1, 1,
  			    stab2arg(A_WORD,Nullstab),
  			    Nullarg, Nullarg,0); }
! 	|	FILOP
! 			{ $$ = make_op($1, 0,
  			    Nullarg, Nullarg, Nullarg,0); }
  	|	TELL '(' WORD ')'
  			{ $$ = make_op(O_TELL, 1,

Index: perly.c
Prereq: 2.0.1.8
*** perly.c.old	Sat Nov 19 00:34:37 1988
--- perly.c	Sat Nov 19 00:34:43 1988
***************
*** 1,6 ****
! char rcsid[] = "$Header: perly.c,v 2.0.1.8 88/10/31 16:44:49 lwall Exp $";
  /*
   * $Log:	perly.c,v $
   * Revision 2.0.1.8  88/10/31  16:44:49  lwall
   * patch15: now suppresses -S if / is anywhere in script name.
   * patch15: some support for defective 286 compilers
--- 1,15 ----
! char rcsid[] = "$Header: perly.c,v 2.0.1.9 88/11/19 00:14:36 lwall Locked $\nPatch level: ###\n";
  /*
   * $Log:	perly.c,v $
+  * Revision 2.0.1.9  88/11/19  00:14:36  lwall
+  * patch16: added $] to return rcsid and patchlevel
+  * patch16: added code to check for kernel setuid script bug
+  * patch16: "taint" checks for setuid scripts
+  * patch16: added redundant prohibitions on certain switches in setuid scripts
+  * patch16: now makes use of setre[ug]id() if available
+  * patch16: replaced insecure access()
+  * patch16: doesn't blow up finding suidperl on bad PATH now
+  * 
   * Revision 2.0.1.8  88/10/31  16:44:49  lwall
   * patch15: now suppresses -S if / is anywhere in script name.
   * patch15: some support for defective 286 compilers
***************
*** 60,65 ****
--- 69,75 ----
  #include "EXTERN.h"
  #include "perl.h"
  #include "perly.h"
+ #include "patchlevel.h"
  
  #ifdef IAMSUID
  #ifndef DOSUID
***************
*** 67,72 ****
--- 77,83 ----
  #endif
  #endif
  
+ 
  extern char *tokename[];
  extern int yychar;
  
***************
*** 76,81 ****
--- 87,98 ----
  
  static bool saw_return;
  
+ #ifdef SETUID_SCRIPTS_ARE_SECURE_NOW
+ #ifdef DOSUID
+ #undef DOSUID
+ #endif
+ #endif
+ 
  main(argc,argv,env)
  register int argc;
  register char **argv;
***************
*** 89,95 ****
--- 106,123 ----
      char **origargv = argv;
      char *validarg = "";
  #endif
+     int gid = (int)getgid();
+     int egid = (int)getegid();
  
+ #ifdef SETUID_SCRIPTS_ARE_SECURE_NOW
+ #ifdef IAMSUID
+ #undef IAMSUID
+     fatal("suidperl is no longer needed since the kernel can now execute\n\
+ setuid perl scripts securely.\n");
+ #endif
+ #endif
+ 
+     sprintf(index(rcsid,'#'), "%d\n", PATCHLEVEL);
      uid = (int)getuid();
      euid = (int)geteuid();
      linestr = str_new(80);
***************
*** 114,119 ****
--- 142,151 ----
  	    goto reswitch;
  #ifdef DEBUGGING
  	case 'D':
+ #ifdef TAINT
+ 	    if (euid != uid || egid != gid)
+ 		fatal("No -D allowed in setuid scripts");
+ #endif
  	    debug = atoi(s+1);
  #ifdef YYDEBUG
  	    yydebug = (debug & 1);
***************
*** 121,126 ****
--- 153,162 ----
  	    break;
  #endif
  	case 'e':
+ #ifdef TAINT
+ 	    if (euid != uid || egid != gid)
+ 		fatal("No -e allowed in setuid scripts");
+ #endif
  	    if (!e_fp) {
  	        e_tmpname = strcpy(safemalloc(sizeof(TMPPATH)),TMPPATH);
  		mktemp(e_tmpname);
***************
*** 136,141 ****
--- 172,181 ----
  	    argvoutstab = stabent("ARGVOUT",TRUE);
  	    break;
  	case 'I':
+ #ifdef TAINT
+ 	    if (euid != uid || egid != gid)
+ 		fatal("No -I allowed in setuid scripts");
+ #endif
  	    str_cat(str,"-");
  	    str_cat(str,s);
  	    str_cat(str," ");
***************
*** 158,167 ****
--- 198,215 ----
  	    s++;
  	    goto reswitch;
  	case 'P':
+ #ifdef TAINT
+ 	    if (euid != uid || egid != gid)
+ 		fatal("No -P allowed in setuid scripts");
+ #endif
  	    preprocess = TRUE;
  	    s++;
  	    goto reswitch;
  	case 's':
+ #ifdef TAINT
+ 	    if (euid != uid || egid != gid)
+ 		fatal("No -s allowed in setuid scripts");
+ #endif
  	    doswitches = TRUE;
  	    s++;
  	    goto reswitch;
***************
*** 174,180 ****
  	    s++;
  	    goto reswitch;
  	case 'v':
! 	    version();
  	    exit(0);
  	case 'w':
  	    dowarn = TRUE;
--- 222,228 ----
  	    s++;
  	    goto reswitch;
  	case 'v':
! 	    fputs(rcsid,stdout);
  	    exit(0);
  	case 'w':
  	    dowarn = TRUE;
***************
*** 257,270 ****
   -e 's/^#.*//' \
   %s | %s -C %s %s",
  	  argv[0], CPPSTDIN, str_get(str), CPPMINUS);
! #ifdef IAMSUID
  	if (euid != uid && !euid)	/* if running suidperl */
  #ifdef SETEUID
  	    seteuid(uid);		/* musn't stay setuid root */
  #else
  	    setuid(uid);
  #endif
  #endif
  	rsfp = popen(buf,"r");
      }
      else if (!*argv[0])
--- 305,322 ----
   -e 's/^#.*//' \
   %s | %s -C %s %s",
  	  argv[0], CPPSTDIN, str_get(str), CPPMINUS);
! #ifdef IAMSUID				/* actually, this is caught earlier */
  	if (euid != uid && !euid)	/* if running suidperl */
  #ifdef SETEUID
  	    seteuid(uid);		/* musn't stay setuid root */
  #else
+ #ifdef SETREUID
+ 	    setreuid(-1, uid);
+ #else
  	    setuid(uid);
  #endif
  #endif
+ #endif /* IAMSUID */
  	rsfp = popen(buf,"r");
      }
      else if (!*argv[0])
***************
*** 273,282 ****
  	rsfp = fopen(argv[0],"r");
      if (rsfp == Nullfp) {
  #ifdef DOSUID
! #ifndef IAMSUID
  	if (euid && stat(filename,&statbuf) >= 0 &&
  	  statbuf.st_mode & (S_ISUID|S_ISGID)) {
! 	    execvp("suidperl", origargv);	/* try again */
  	    fatal("Can't do setuid\n");
  	}
  #endif
--- 325,335 ----
  	rsfp = fopen(argv[0],"r");
      if (rsfp == Nullfp) {
  #ifdef DOSUID
! #ifndef IAMSUID		/* in case script is not readable before setuid */
  	if (euid && stat(filename,&statbuf) >= 0 &&
  	  statbuf.st_mode & (S_ISUID|S_ISGID)) {
! 	    sprintf(buf, "%s/%s", BIN, "suidperl");
! 	    execv(buf, origargv);	/* try again */
  	    fatal("Can't do setuid\n");
  	}
  #endif
***************
*** 302,308 ****
--- 355,369 ----
       * DOSUID must be defined in both perl and suidperl, and IAMSUID must
       * be defined in suidperl only.  suidperl must be setuid root.  The
       * Configure script will set this up for you if you want it.
+      *
+      * There is also the possibility of have a script which is running
+      * set-id due to a C wrapper.  We want to do the TAINT checks
+      * on these set-id scripts, but don't want to have the overhead of
+      * them in normal perl, and can't use suidperl because it will lose
+      * the effective uid info, so we have an additional non-setuid root
+      * version called taintperl that just does the TAINT checks.
       */
+ 
  #ifdef DOSUID
      if (fstat(fileno(rsfp),&statbuf) < 0)	/* normal stat is insecure */
  	fatal("Can't stat script \"%s\"",filename);
***************
*** 309,318 ****
--- 370,426 ----
      if (statbuf.st_mode & (S_ISUID|S_ISGID)) {
  	int len;
  
+ #ifdef IAMSUID
+ #ifndef SETREUID
+ 	/* On this access check to make sure the directories are readable,
+ 	 * there is actually a small window that the user could use to make
+ 	 * filename point to an accessible directory.  So there is a faint
+ 	 * chance that someone could execute a setuid script down in a
+ 	 * non-accessible directory.  I don't know what to do about that.
+ 	 * But I don't think it's too important.  The manual lies when
+ 	 * it says access() is useful in setuid programs.
+ 	 */
  	if (access(filename,1))		/* as a double check */
  	    fatal("Permission denied");
+ #else
+ 	/* If we can swap euid and uid, then we can determine access rights
+ 	 * with a simple stat of the file, and then compare device and
+ 	 * inode to make sure we did stat() on the same file we opened.
+ 	 * Then we just have to make sure he or she can execute it.
+ 	 */
+ 	{
+ 	    struct stat tmpstatbuf;
+ 
+ 	    if (setreuid(euid,uid) < 0 || getuid() != euid || geteuid() != uid)
+ 		fatal("Can't swap uid and euid");	/* really paranoid */
+ 	    if (stat(filename,&tmpstatbuf) < 0) /* testing full pathname here */
+ 		fatal("Permission denied");
+ 	    if (tmpstatbuf.st_dev != statbuf.st_dev ||
+ 		tmpstatbuf.st_ino != statbuf.st_ino) {
+ 		close(rsfp);
+ 		if (rsfp = popen("/bin/mail root","w")) {	/* heh, heh */
+ 		    fprintf(rsfp,
+ "User %d tried to run dev %d ino %d in place of dev %d ino %d!\n\
+ (Filename of set-id script was %s, uid %d gid %d.)\n\nSincerely,\nperl\n",
+ 			uid,tmpstatbuf.st_dev, tmpstatbuf.st_ino,
+ 			statbuf.st_dev, statbuf.st_ino,
+ 			filename, statbuf.st_uid, statbuf.st_gid);
+ 		    fclose(rsfp);
+ 		}
+ 		fatal("Permission denied\n");
+ 	    }
+ 	    if (setreuid(uid,euid) < 0 || getuid() != uid || geteuid() != euid)
+ 		fatal("Can't reswap uid and euid");
+ 	    if (!cando(S_IEXEC,FALSE))		/* can real uid exec? */
+ 		fatal("Permission denied\n");
+ 	}
+ #endif /* SETREUID */
+ #endif /* IAMSUID */
+ 
  	if ((statbuf.st_mode & S_IFMT) != S_IFREG)
  	    fatal("Permission denied");
+ 	if ((statbuf.st_mode >> 6) & S_IWRITE)
+ 	    fatal("Setuid/gid script is writable by world");
  	doswitches = FALSE;		/* -s is insecure in suid */
  	line++;
  	if (fgets(tokenbuf,sizeof tokenbuf, rsfp) == Nullch ||
***************
*** 332,341 ****
  	    strnNE(s,validarg,len) || !isspace(s[len]))
  	    fatal("Args must match #! line");
  
  	if (euid) {	/* oops, we're not the setuid root perl */
  	    fclose(rsfp);
  #ifndef IAMSUID
! 	    execvp("suidperl", origargv);	/* try again */
  #endif
  	    fatal("Can't do setuid\n");
  	}
--- 440,457 ----
  	    strnNE(s,validarg,len) || !isspace(s[len]))
  	    fatal("Args must match #! line");
  
+ #ifndef IAMSUID
+ 	if (euid != uid && (statbuf.st_mode & S_ISUID) &&
+ 	    euid == statbuf.st_uid)
+ 	    fatal("YOU HAVEN'T DISABLED SET-ID SCRIPTS IN THE KERNEL YET!\n\
+ FIX YOUR KERNEL OR PUT A C WRAPPER AROUND THIS SCRIPT!\n");
+ #endif /* IAMSUID */
+ 
  	if (euid) {	/* oops, we're not the setuid root perl */
  	    fclose(rsfp);
  #ifndef IAMSUID
! 	    sprintf(buf, "%s/%s", BIN, "suidperl");
! 	    execv(buf, origargv);	/* try again */
  #endif
  	    fatal("Can't do setuid\n");
  	}
***************
*** 344,365 ****
--- 460,493 ----
  #ifdef SETEGID
  	    setegid(statbuf.st_gid);
  #else
+ #ifdef SETREGID
+ 	    setregid(-1,statbuf.st_gid);
+ #else
  	    setgid(statbuf.st_gid);
  #endif
+ #endif
  	if (statbuf.st_mode & S_ISUID) {
  	    if (statbuf.st_uid != euid)
  #ifdef SETEUID
  		seteuid(statbuf.st_uid);	/* all that for this */
  #else
+ #ifdef SETREUID
+ 		setreuid(-1,statbuf.st_uid);
+ #else
  		setuid(statbuf.st_uid);
  #endif
+ #endif
  	}
  	else if (uid)			/* oops, mustn't run as root */
  #ifdef SETEUID
  	    seteuid(uid);
  #else
+ #ifdef SETREUID
+ 	    setreuid(-1,uid);
+ #else
  	    setuid(uid);
  #endif
+ #endif
  	euid = (int)geteuid();
  	if (!cando(S_IEXEC,TRUE))
  	    fatal("Permission denied\n");	/* they can't do this */
***************
*** 369,375 ****
--- 497,532 ----
  	fatal("-P not allowed for setuid/setgid script\n");
      else
  	fatal("Script is not setuid/setgid in suidperl\n");
+ #else
+ #ifndef TAINT		/* we aren't taintperl or suidperl */
+     /* script has a wrapper--can't run suidperl or we lose euid */
+     else if (euid != uid || egid != gid) {
+ 	fclose(rsfp);
+ 	sprintf(buf, "%s/%s", BIN, "taintperl");
+ 	execv(buf, origargv);	/* try again */
+ 	fatal("Can't run setuid script with taint checks");
+     }
+ #endif /* TAINT */
  #endif /* IAMSUID */
+ #else /* !DOSUID */
+ #ifndef TAINT		/* we aren't taintperl or suidperl */
+     if (euid != uid || egid != gid) {	/* (suidperl doesn't exist, in fact) */
+ #ifndef SETUID_SCRIPTS_ARE_SECURE_NOW
+ 	fstat(fileno(rsfp),&statbuf);	/* may be either wrapped or real suid */
+ 	if ((euid != uid && euid == statbuf.st_uid && statbuf.st_mode & S_ISUID)
+ 	    ||
+ 	    (egid != gid && egid == statbuf.st_gid && statbuf.st_mode & S_ISGID)
+ 	   )
+ 	    fatal("YOU HAVEN'T DISABLED SET-ID SCRIPTS IN THE KERNEL YET!\n\
+ FIX YOUR KERNEL OR PUT A C WRAPPER AROUND THIS SCRIPT!\n");
+ #endif /* SETUID_SCRIPTS_ARE_SECURE_NOW */
+ 	/* not set-id, must be wrapped */
+ 	fclose(rsfp);
+ 	sprintf(buf, "%s/%s", BIN, "taintperl");
+ 	execv(buf, origargv);	/* try again */
+ 	fatal("Can't run setuid script with taint checks");
+     }
+ #endif /* TAINT */
  #endif /* DOSUID */
  
      defstab = stabent("_",TRUE);
***************
*** 378,384 ****
  
      bufptr = str_get(linestr);
  
!     /* now parse the report spec */
  
      if (yyparse())
  	fatal("Execution aborted due to compilation errors.\n");
--- 535,541 ----
  
      bufptr = str_get(linestr);
  
!     /* now parse the script */
  
      if (yyparse())
  	fatal("Execution aborted due to compilation errors.\n");
***************
*** 403,408 ****
--- 560,568 ----
  	    str_numset(stabent(argv[0]+1,TRUE)->stab_val,(double)1.0);
  	}
      }
+ #ifdef TAINT
+     tainted = 1;
+ #endif
      if (argvstab = stabent("ARGV",allstabs)) {
  	aadd(argvstab);
  	for (; argc > 0; argc--,argv++) {
***************
*** 421,426 ****
--- 581,589 ----
  	    *--s = '=';
  	}
      }
+ #ifdef TAINT
+     tainted = 0;
+ #endif
      if (sigstab = stabent("SIG",allstabs))
  	hadd(sigstab);
  
***************
*** 434,443 ****
--- 597,614 ----
      /* these aren't necessarily magical */
      if (tmpstab = stabent(";",allstabs))
  	str_set(STAB_STR(tmpstab),"\034");
+ #ifdef TAINT
+     tainted = 1;
+ #endif
      if (tmpstab = stabent("0",allstabs))
  	str_set(STAB_STR(tmpstab),origfilename);
+ #ifdef TAINT
+     tainted = 0;
+ #endif
      if (tmpstab = stabent("$",allstabs))
  	str_numset(STAB_STR(tmpstab),(double)getpid());
+     if (tmpstab = stabent("]",allstabs))
+ 	str_set(STAB_STR(tmpstab),rcsid);
  
      tmpstab = stabent("stdin",TRUE);
      tmpstab->stab_io = stio_new();

Index: stab.c
Prereq: 2.0.1.5
*** stab.c.old	Sat Nov 19 00:34:52 1988
--- stab.c	Sat Nov 19 00:34:54 1988
***************
*** 1,6 ****
! /* $Header: stab.c,v 2.0.1.5 88/09/07 17:03:28 lwall Exp $
   *
   * $Log:	stab.c,v $
   * Revision 2.0.1.5  88/09/07  17:03:28  lwall
   * patch14: attempted fix for machines where $* = 1 was failing
   * 
--- 1,10 ----
! /* $Header: stab.c,v 2.0.1.6 88/11/19 00:19:26 lwall Locked $
   *
   * $Log:	stab.c,v $
+  * Revision 2.0.1.6  88/11/19  00:19:26  lwall
+  * patch16: $@ now reports correct error line after do EXPR
+  * patch16: now makes use of setre[ug]id() if available
+  * 
   * Revision 2.0.1.5  88/09/07  17:03:28  lwall
   * patch14: attempted fix for machines where $* = 1 was failing
   * 
***************
*** 320,355 ****
  	    errno = (int)str_gnum(str);		/* will anyone ever use this? */
  	    break;
  	case '<':
- #ifdef SETRUID
  	    uid = (int)str_gnum(str);
  	    if (setruid(uid) < 0)
  		uid = (int)getuid();
  #else
  	    fatal("setruid() not implemented");
  #endif
  	    break;
  	case '>':
- #ifdef SETEUID
  	    euid = (int)str_gnum(str);
  	    if (seteuid(euid) < 0)
  		euid = (int)geteuid();
  #else
  	    fatal("seteuid() not implemented");
  #endif
  	    break;
  	case '(':
  #ifdef SETRGID
  	    setrgid((int)str_gnum(str));
  #else
  	    fatal("setrgid() not implemented");
  #endif
  	    break;
  	case ')':
  #ifdef SETEGID
  	    setegid((int)str_gnum(str));
  #else
  	    fatal("setegid() not implemented");
  #endif
  	    break;
  	case '.':
  	case '+':
--- 324,377 ----
  	    errno = (int)str_gnum(str);		/* will anyone ever use this? */
  	    break;
  	case '<':
  	    uid = (int)str_gnum(str);
+ #ifdef SETRUID
  	    if (setruid(uid) < 0)
  		uid = (int)getuid();
  #else
+ #ifdef SETREUID
+ 	    if (setreuid(uid, -1) < 0)
+ 		uid = (int)getuid();
+ #else
  	    fatal("setruid() not implemented");
  #endif
+ #endif
  	    break;
  	case '>':
  	    euid = (int)str_gnum(str);
+ #ifdef SETEUID
  	    if (seteuid(euid) < 0)
  		euid = (int)geteuid();
  #else
+ #ifdef SETREUID
+ 	    if (setreuid(-1, euid) < 0)
+ 		euid = (int)geteuid();
+ #else
  	    fatal("seteuid() not implemented");
  #endif
+ #endif
  	    break;
  	case '(':
  #ifdef SETRGID
  	    setrgid((int)str_gnum(str));
  #else
+ #ifdef SETREGID
+ 	    setregid((int)str_gnum(str), -1);
+ #else
  	    fatal("setrgid() not implemented");
  #endif
+ #endif
  	    break;
  	case ')':
  #ifdef SETEGID
  	    setegid((int)str_gnum(str));
  #else
+ #ifdef SETREGID
+ 	    setregid(-1, (int)str_gnum(str));
+ #else
  	    fatal("setegid() not implemented");
  #endif
+ #endif
  	    break;
  	case '.':
  	case '+':
***************
*** 513,518 ****
--- 535,541 ----
  	stab->stab_name = savestr(name);
  	stab->stab_val = str_new(0);
  	stab->stab_next = stab_index[*name];
+ 	stab->stab_line = line;
  	stab_index[*name] = stab;
  	return stab;
      }
***************
*** 548,554 ****
  		continue;
  	    if (i == 'I' && strEQ(stab->stab_name, "INC"))
  		continue;
! 	    warn("Possible typo: %s,", stab->stab_name);
  	}
      }
  }
--- 571,578 ----
  		continue;
  	    if (i == 'I' && strEQ(stab->stab_name, "INC"))
  		continue;
! 	    line = stab->stab_line;
! 	    warn("Possible typo: \"%s\"", stab->stab_name);
  	}
      }
  }

Index: stab.h
Prereq: 2.0
*** stab.h.old	Sat Nov 19 00:34:58 1988
--- stab.h	Sat Nov 19 00:34:59 1988
***************
*** 1,6 ****
! /* $Header: stab.h,v 2.0 88/06/05 00:11:05 root Exp $
   *
   * $Log:	stab.h,v $
   * Revision 2.0  88/06/05  00:11:05  root
   * Baseline version 2.0.
   * 
--- 1,9 ----
! /* $Header: stab.h,v 2.0.1.1 88/11/19 00:20:26 lwall Locked $
   *
   * $Log:	stab.h,v $
+  * Revision 2.0.1.1  88/11/19  00:20:26  lwall
+  * patch16: added stab_line field
+  * 
   * Revision 2.0  88/06/05  00:11:05  root
   * Baseline version 2.0.
   * 
***************
*** 7,20 ****
   */
  
  struct stab {
!     struct stab *stab_next;
!     char	*stab_name;
!     STR		*stab_val;
!     struct stio *stab_io;
!     FCMD	*stab_form;
!     ARRAY	*stab_array;
!     HASH	*stab_hash;
!     SUBR	*stab_sub;
      char	stab_flags;
  };
  
--- 10,24 ----
   */
  
  struct stab {
!     struct stab *stab_next;	/* next symbol table entry under this letter */
!     char	*stab_name;	/* name of symbol */
!     STR		*stab_val;	/* scalar value */
!     struct stio *stab_io;	/* filehandle value */
!     FCMD	*stab_form;	/* format value */
!     ARRAY	*stab_array;	/* array value */
!     HASH	*stab_hash;	/* associative array value */
!     SUBR	*stab_sub;	/* subroutine value */
!     short	stab_line;	/* line first declared at (for -w) */
      char	stab_flags;
  };
  
***************
*** 23,37 ****
  
  struct stio {
      FILE	*fp;
!     long	lines;
!     long	page;
!     long	page_len;
!     long	lines_left;
!     char	*top_name;
!     STAB	*top_stab;
!     char	*fmt_name;
!     STAB	*fmt_stab;
!     short	subprocess;
      char	type;
      char	flags;
  };
--- 27,41 ----
  
  struct stio {
      FILE	*fp;
!     long	lines;		/* $. */
!     long	page;		/* $% */
!     long	page_len;	/* $= */
!     long	lines_left;	/* $- */
!     char	*top_name;	/* $^ */
!     STAB	*top_stab;	/* $^ */
!     char	*fmt_name;	/* $~ */
!     STAB	*fmt_stab;	/* $~ */
!     short	subprocess;	/* -| or |- */
      char	type;
      char	flags;
  };

Index: str.c
Prereq: 2.0.1.3
*** str.c.old	Sat Nov 19 00:35:04 1988
--- str.c	Sat Nov 19 00:35:05 1988
***************
*** 1,6 ****
! /* $Header: str.c,v 2.0.1.3 88/08/03 22:39:56 root Exp $
   *
   * $Log:	str.c,v $
   * Revision 2.0.1.3  88/08/03  22:39:56  root
   * patch11: support for incompetent compilers that can't parse str_get macro
   * 
--- 1,9 ----
! /* $Header: str.c,v 2.0.1.4 88/11/19 00:21:57 lwall Locked $
   *
   * $Log:	str.c,v $
+  * Revision 2.0.1.4  88/11/19  00:21:57  lwall
+  * patch16: "taint" checks for setuid scripts
+  * 
   * Revision 2.0.1.3  88/08/03  22:39:56  root
   * patch11: support for incompetent compilers that can't parse str_get macro
   * 
***************
*** 25,30 ****
--- 28,36 ----
  str_get(str)
  STR *str;
  {
+ #ifdef TAINT
+     tainted |= str->str_tainted;
+ #endif
      return str->str_pok ? str->str_ptr : str_2ptr(str);
  }
  #endif
***************
*** 58,63 ****
--- 64,72 ----
  		str = stab->stab_val;
  		str->str_cur = 0;
  		str->str_nok = 0;
+ #ifdef TAINT
+ 		str->str_tainted = tainted;
+ #endif
  		if (str->str_ptr != Nullch)
  		    str->str_ptr[0] = '\0';
  		if (stab->stab_array) {
***************
*** 80,85 ****
--- 89,97 ----
      str->str_nval = num;
      str->str_pok = 0;		/* invalidate pointer */
      str->str_nok = 1;		/* validate number */
+ #ifdef TAINT
+     str->str_tainted = tainted;
+ #endif
  }
  
  extern int errno;
***************
*** 148,153 ****
--- 160,168 ----
  STR *dstr;
  register STR *sstr;
  {
+ #ifdef TAINT
+     tainted |= sstr->str_tainted;
+ #endif
      if (!sstr)
  	str_nset(dstr,No,0);
      else if (sstr->str_nok)
***************
*** 169,174 ****
--- 184,192 ----
      *(str->str_ptr+str->str_cur) = '\0';
      str->str_nok = 0;		/* invalidate number */
      str->str_pok = 1;		/* validate pointer */
+ #ifdef TAINT
+     str->str_tainted = tainted;
+ #endif
  }
  
  str_set(str,ptr)
***************
*** 185,190 ****
--- 203,211 ----
      str->str_cur = len;
      str->str_nok = 0;		/* invalidate number */
      str->str_pok = 1;		/* validate pointer */
+ #ifdef TAINT
+     str->str_tainted = tainted;
+ #endif
  }
  
  str_chop(str,ptr)	/* like set but assuming ptr is in str */
***************
*** 212,217 ****
--- 233,241 ----
      *(str->str_ptr+str->str_cur) = '\0';
      str->str_nok = 0;		/* invalidate number */
      str->str_pok = 1;		/* validate pointer */
+ #ifdef TAINT
+     str->str_tainted |= tainted;
+ #endif
  }
  
  str_scat(dstr,sstr)
***************
*** 218,223 ****
--- 242,250 ----
  STR *dstr;
  register STR *sstr;
  {
+ #ifdef TAINT
+     tainted |= sstr->str_tainted;
+ #endif
      if (!sstr)
  	return;
      if (!(sstr->str_pok))
***************
*** 242,247 ****
--- 269,277 ----
      str->str_cur += len;
      str->str_nok = 0;		/* invalidate number */
      str->str_pok = 1;		/* validate pointer */
+ #ifdef TAINT
+     str->str_tainted |= tainted;
+ #endif
  }
  
  char *
***************
*** 326,331 ****
--- 356,364 ----
      str->str_pok = nstr->str_pok;
      if (str->str_nok = nstr->str_nok)
  	str->str_nval = nstr->str_nval;
+ #ifdef TAINT
+     str->str_tainted = nstr->str_tainted;
+ #endif
      safefree((char*)nstr);
  }
  
***************
*** 340,345 ****
--- 373,381 ----
      str->str_cur = 0;
      str->str_nok = 0;
      str->str_pok = 0;
+ #ifdef TAINT
+     str->str_tainted = 0;
+ #endif
      str->str_link.str_next = freestrroot;
      freestrroot = str;
  }
***************
*** 587,589 ****
--- 623,658 ----
      str_numset(str,n);
      return str;
  }
+ 
+ #ifdef TAINT
+ taintproper(s)
+ char *s;
+ {
+ #ifdef DEBUGGING
+     if (debug & 2048)
+ 	fprintf(stderr,"%s %d %d %d\n",s,tainted,uid, euid);
+ #endif
+     if (tainted && (!euid || euid != uid)) {
+ 	if (!unsafe)
+ 	    fatal("%s", s);
+ 	else if (dowarn)
+ 	    warn("%s", s);
+     }
+ }
+ 
+ taintenv()
+ {
+     register STR *envstr;
+ 
+     envstr = hfetch(envstab->stab_hash,"PATH");
+     if (!envstr || envstr->str_tainted) {
+ 	tainted = 1;
+ 	taintproper("Insecure PATH");
+     }
+     envstr = hfetch(envstab->stab_hash,"IFS");
+     if (envstr && envstr->str_tainted) {
+ 	tainted = 1;
+ 	taintproper("Insecure IFS");
+     }
+ }
+ #endif /* TAINT */

Index: str.h
Prereq: 2.0.1.2
*** str.h.old	Sat Nov 19 00:35:09 1988
--- str.h	Sat Nov 19 00:35:10 1988
***************
*** 1,6 ****
! /* $Header: str.h,v 2.0.1.2 88/09/07 17:04:00 lwall Exp $
   *
   * $Log:	str.h,v $
   * Revision 2.0.1.2  88/09/07  17:04:00  lwall
   * patch14: searches should now work on chars with the 128 bit set
   * 
--- 1,9 ----
! /* $Header: str.h,v 2.0.1.3 88/11/19 00:22:40 lwall Locked $
   *
   * $Log:	str.h,v $
+  * Revision 2.0.1.3  88/11/19  00:22:40  lwall
+  * patch16: "taint" checks for setuid scripts
+  * 
   * Revision 2.0.1.2  88/09/07  17:04:00  lwall
   * patch14: searches should now work on chars with the 128 bit set
   * 
***************
*** 25,30 ****
--- 28,36 ----
      char	str_nok;	/* state of str_nval */
      unsigned char str_rare;	/* used by search strings */
      unsigned char str_prev;	/* also used by search strings */
+ #ifdef TAINT
+     bool	str_tainted;	/* 1 if possibly under control of $< */
+ #endif
  };
  
  #define Nullstr Null(STR*)
***************
*** 31,39 ****
--- 37,52 ----
  
  /* the following macro updates any magic values this str is associated with */
  
+ #ifdef TAINT
  #define STABSET(x) \
+     (x)->str_tainted |= tainted; \
      if ((x)->str_link.str_magic) \
  	stabset((x)->str_link.str_magic,(x))
+ #else
+ #define STABSET(x) \
+     if ((x)->str_link.str_magic) \
+ 	stabset((x)->str_link.str_magic,(x))
+ #endif
  
  EXT STR **tmps_list;
  EXT int tmps_max INIT(-1);

Index: toke.c
Prereq: 2.0.1.5
*** toke.c.old	Sat Nov 19 00:35:18 1988
--- toke.c	Sat Nov 19 00:35:21 1988
***************
*** 1,6 ****
! /* $Header: toke.c,v 2.0.1.5 88/09/07 17:09:52 lwall Exp $
   *
   * $Log:	toke.c,v $
   * Revision 2.0.1.5  88/09/07  17:09:52  lwall
   * patch14: added detection of "sort" not used as keyword
   * patch14: case insensitive search speedup
--- 1,10 ----
! /* $Header: toke.c,v 2.0.1.6 88/11/19 00:25:21 lwall Locked $
   *
   * $Log:	toke.c,v $
+  * Revision 2.0.1.6  88/11/19  00:25:21  lwall
+  * patch16: added getc function
+  * patch16: variables in patterns are no longer hidden from -w typo detection
+  * 
   * Revision 2.0.1.5  88/09/07  17:09:52  lwall
   * patch14: added detection of "sort" not used as keyword
   * patch14: case insensitive search speedup
***************
*** 46,51 ****
--- 50,56 ----
  #define MOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)MULOP)
  #define EOP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)EQOP)
  #define ROP(f) return(yylval.ival=f,expectterm = TRUE,bufptr = s,(int)RELOP)
+ #define FOP(f) return(yylval.ival=f,expectterm = FALSE,bufptr = s,(int)FILOP)
  
  yylex()
  {
***************
*** 140,146 ****
  		s++;
  	    if (*s)
  		s++;
! 	    line++;
  	}
  	else
  	    *s = '\0';
--- 145,152 ----
  		s++;
  	    if (*s)
  		s++;
! 	    if (*s)
! 		line++;
  	}
  	else
  	    *s = '\0';
***************
*** 407,413 ****
  	    UNI(O_EVAL);		/* we don't know what will be used */
  	}
  	if (strEQ(d,"eof"))
! 	    TERM(FEOF);
  	if (strEQ(d,"exp"))
  	    FUN1(O_EXP);
  	if (strEQ(d,"each"))
--- 413,419 ----
  	    UNI(O_EVAL);		/* we don't know what will be used */
  	}
  	if (strEQ(d,"eof"))
! 	    FOP(O_EOF);
  	if (strEQ(d,"exp"))
  	    FUN1(O_EXP);
  	if (strEQ(d,"each"))
***************
*** 442,447 ****
--- 448,455 ----
  	    LOOPX(O_GOTO);
  	if (strEQ(d,"gmtime"))
  	    FUN1(O_GMTIME);
+ 	if (strEQ(d,"getc"))
+ 	    FOP(O_GETC);
  	yylval.cval = savestr(d);
  	OPERATOR(WORD);
      case 'h': case 'H':
***************
*** 825,830 ****
--- 833,846 ----
  	    arg->arg_type = O_ITEM;
  	    arg[1].arg_type = A_DOUBLE;
  	    arg[1].arg_ptr.arg_str = str_make(tokenbuf);
+ 	    d = scanreg(d,buf);
+ 	    stabent(buf,TRUE);		/* make sure it's created */
+ 	    for (; *d; d++) {
+ 		if (*d == '$' && d[1] && d[-1] != '\\' && d[1] != '|') {
+ 		    d = scanreg(d,buf);
+ 		    stabent(buf,TRUE);
+ 		}
+ 	    }
  	    goto got_pat;		/* skip compiling for now */
  	}
      }
***************
*** 895,900 ****
--- 911,924 ----
  	    arg->arg_type = O_ITEM;
  	    arg[1].arg_type = A_DOUBLE;
  	    arg[1].arg_ptr.arg_str = str_make(tokenbuf);
+ 	    d = scanreg(d,buf);
+ 	    stabent(buf,TRUE);		/* make sure it's created */
+ 	    for (; *d; d++) {
+ 		if (*d == '$' && d[1] && d[-1] != '\\' && d[1] != '|') {
+ 		    d = scanreg(d,buf);
+ 		    stabent(buf,TRUE);
+ 		}
+ 	    }
  	    goto get_repl;		/* skip compiling for now */
  	}
      }

Index: util.c
Prereq: 2.0.1.5
*** util.c.old	Sat Nov 19 00:35:28 1988
--- util.c	Sat Nov 19 00:35:30 1988
***************
*** 1,6 ****
! /* $Header: util.c,v 2.0.1.5 88/10/31 16:51:04 lwall Exp $
   *
   * $Log:	util.c,v $
   * Revision 2.0.1.5  88/10/31  16:51:04  lwall
   * patch15: some support for defective 286 compilers
   * patch15: support for varargs and vprintf
--- 1,9 ----
! /* $Header: util.c,v 2.0.1.6 88/11/19 00:31:02 lwall Locked $
   *
   * $Log:	util.c,v $
+  * Revision 2.0.1.6  88/11/19  00:31:02  lwall
+  * patch16: return type of vsprintf() now depends on CHARSPRINTF
+  * 
   * Revision 2.0.1.5  88/10/31  16:51:04  lwall
   * patch15: some support for defective 286 compilers
   * patch15: support for varargs and vprintf
***************
*** 641,647 ****
--- 644,654 ----
  {
      char *pat;
      char *s;
+ #ifdef CHARSPRINTF
      char *vsprintf();
+ #else
+     int vsprintf();
+ #endif
  
      s = buf;
      pat = va_arg(args, char *);
***************
*** 805,811 ****
--- 812,822 ----
  #ifdef VARARGS
  #ifndef VPRINTF
  
+ #ifdef CHARSPRINTF
  char *
+ #else
+ int
+ #endif
  vsprintf(dest, pat, args)
  char *dest, *pat, *args;
  {

Index: util.h
Prereq: 2.0
*** util.h.old	Sat Nov 19 00:35:35 1988
--- util.h	Sat Nov 19 00:35:36 1988
***************
*** 1,14 ****
! /* $Header: util.h,v 2.0 88/06/05 00:15:15 root Exp $
   *
   * $Log:	util.h,v $
   * Revision 2.0  88/06/05  00:15:15  root
   * Baseline version 2.0.
   * 
   */
  
! int *screamfirst INIT(Null(int*));
! int *screamnext INIT(Null(int*));
! int *screamcount INIT(Null(int*));
  
  char	*safemalloc();
  char	*saferealloc();
--- 1,17 ----
! /* $Header: util.h,v 2.0.1.1 88/11/19 00:32:02 lwall Locked $
   *
   * $Log:	util.h,v $
+  * Revision 2.0.1.1  88/11/19  00:32:02  lwall
+  * patch16: several variables weren't declared EXT
+  * 
   * Revision 2.0  88/06/05  00:15:15  root
   * Baseline version 2.0.
   * 
   */
  
! EXT int *screamfirst INIT(Null(int*));
! EXT int *screamnext INIT(Null(int*));
! EXT int *screamcount INIT(Null(int*));
  
  char	*safemalloc();
  char	*saferealloc();



More information about the Comp.sources.bugs mailing list