[656] in linux-security and linux-alert archive

home help back first fref pref prev next nref lref last post

[linux-security] two comments..

daemon@ATHENA.MIT.EDU (*Hobbit*)
Wed Apr 3 15:04:26 1996

Date: Wed, 3 Apr 1996 13:02:59 -0500
From: *Hobbit* <hobbit@avian.org>
To: linux-security@tarsier.cv.nrao.edu
Cc: best-of-security@suburbia.net

1> syslog:  If you block UDP 514 but log denied packets, you have not
quite disabled the obvious flooding attack!

2> good chars vs. bad chars:  Why do SO many people do checks of this
sort in ways akin to the suggested fax-mgetty fix code

          if ( c == ' ' || c == '/' || c == '\\'|| c == '&' || c == ';' ||
	      c == '(' || c == ')' || c == '>' || c == '<' || c == '|' ||
	      c == '?' || c == '*' || c == '\''|| c == '"' || c == '`' ) ...

where simply taking your character and using it as an index into a
predefined good/bad array is a> faster and b> more easily configured for
an "allow only known-good chars" basis rather than a "deny the bad chars
we just happened to think of now" basis?  Much less margin for errors
such as allowing newlines and escapes and other trash, and [I tested this
and shit you not] about double the speed of the above check.  For example,
this array [which some compilers may be unhappy about as given, oh well]
roughly mirrors the above code:

static unsigned char bflgs[] = {
	  1,   1,   1,   1,   1,   1,   1,   1,		/* nul - ^G */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* ^H - ^O */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* ^P - ^W */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* ^X - ^_ */
	  0,   1,   0,   1,   1,   1,   0,   0,		/* sp - ' */
	  0,   0,   0,   1,   1,   1,   1,   0,		/* ( - / */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* 0 - 7 */
	  1,   1,   1,   0,   0,   1,   0,   0,		/* 8 - ? */
	  0,   1,   1,   1,   1,   1,   1,   1,		/* @ - G */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* H - O */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* P - W */
	  1,   1,   1,   1,   0,   1,   1,   1,		/* X - _ */
	  0,   1,   1,   1,   1,   1,   1,   1,		/* ` - g */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* h - o */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* p - w */
	  1,   1,   1,   1,   0,   1,   0,   0		/* x - del */
}; /* bflgs */

and characters are easily sanitized/checked/whatever by or-ing out the
high bit from the character in question and doing something like

  register unsigned char q;
  register int x;

  for (x = 0; x <= len; x++) {
    q = (unsigned char) (string[x] & 0x7f);	/* rip hibits */
    if (q == 0)
      break;			/* end of string */
    if (bflgs[q] == 0) {
      appropriate code		/* bad char, no donut */
    } else {
      appropriate code		/* good char, DTRT */
    }
  } /* for */

You can also assign other meanings inside the array, such as "good as is",
"no-questions-asked-BAD", "must superquote for the shell", "replace with
something innocuous", etc, and switch appropriately.  All this really strikes
me as C 101, but I see an awful lot of gnarly code kicking around that tries
to accomplish similar goals in inelegant and inefficient ways.  The fax-getty
bandaid took the cake for me, so I whipped up a comparative speed test for
both methods.  Looking at the output assembler is alone convincing, before
even running the test.

The above array would ideally begin life in "completely anal" mode, and
have other characters made "valid" as needed for specific applications.

static unsigned char bflgs[] = {
	  0,   0,   0,   0,   0,   0,   0,   0,		/* nul - ^G */
	  0,   0,   0,   0,   0,   0,   0,   0,		/* ^H - ^O */
	  0,   0,   0,   0,   0,   0,   0,   0,		/* ^P - ^W */
	  0,   0,   0,   0,   0,   0,   0,   0,		/* ^X - ^_ */
	  0,   0,   0,   0,   0,   0,   0,   0,		/* sp - ' */
	  0,   0,   0,   0,   0,   0,   0,   0,		/* ( - / */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* 0 - 7 */
	  1,   1,   0,   0,   0,   0,   0,   0,		/* 8 - ? */
	  0,   1,   1,   1,   1,   1,   1,   1,		/* @ - G */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* H - O */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* P - W */
	  1,   1,   1,   0,   0,   0,   0,   0,		/* X - _ */
	  0,   1,   1,   1,   1,   1,   1,   1,		/* ` - g */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* h - o */
	  1,   1,   1,   1,   1,   1,   1,   1,		/* p - w */
	  1,   1,   1,   0,   0,   0,   0,   0		/* x - del */
}; /* bflgs */

It seems to me that another lousy 128 bytes of static storage is well worth the
versatility such an approach would give someone writing security-sensitive code
that deals with user input.  Why, for example, should Web servers be handling
any control characters other than newline elements at all?  Why a thousand
other similar questions about daemons in our daily lives??

_H*

home help back first fref pref prev next nref lref last post