From owner-freebsd-isp@FreeBSD.ORG Wed Jul 19 23:13:51 2006 Return-Path: X-Original-To: freebsd-isp@freebsd.org Delivered-To: freebsd-isp@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id A3AE416A4DF for ; Wed, 19 Jul 2006 23:13:51 +0000 (UTC) (envelope-from mark@gaiahost.coop) Received: from biodiesel.gaiahost.coop (biodiesel.gaiahost.coop [64.95.78.120]) by mx1.FreeBSD.org (Postfix) with ESMTP id 4137643D45 for ; Wed, 19 Jul 2006 23:13:50 +0000 (GMT) (envelope-from mark@gaiahost.coop) Received: from gaiahost.coop (host-64-65-195-19.spr.choiceone.net [::ffff:64.65.195.19]) (AUTH: LOGIN mark@hubcapconsulting.com) by biodiesel.gaiahost.coop with esmtp; Wed, 19 Jul 2006 19:13:50 -0400 id 007A4065.44BEBCAE.00002E60 Received: by gaiahost.coop (sSMTP sendmail emulation); Wed, 19 Jul 2006 19:13:49 -0400 Date: Wed, 19 Jul 2006 19:13:48 -0400 From: Mark Bucciarelli To: freebsd-isp@freebsd.org Message-ID: <20060719231348.GN1672@rabbit> Mail-Followup-To: freebsd-isp@freebsd.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline User-Agent: Mutt/1.4.2.1i Subject: injection_projection code X-BeenThere: freebsd-isp@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Internet Services Providers List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 19 Jul 2006 23:13:51 -0000 Following up on the C CGI thread, I settled on the old cgiemail program from MIT, deleted all the printf and html- and url-encoding crap, and added code to protect against email header injection. If you see any holes in the injection_protection code, please let me know--it turned out to be harder than I thought, mainly because of hex encoding. Once I get a git repository up, this will available as Free Software. This only runs for the template variables that are replaced in the email headers. I identify the headers by looking for two linefeeds (or carriage returns, or carriage return+linefeeds) in a row in the template. As a little background, cgiemail lets you define a template and with field names escaped in square brackets. Then you define the form vars that match the field names and set the form action to /cgi-bin/cgiemail/template.txt. It's another issue, but I don't like that cgiemail uses PATH_TRANSLATED to lookup the template file. PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO, so you have to put the template files in a place that is readable via a web browser. Seems like the templates are better off in a cgi-bin dir that is outside the document root. /** * Sanitize string to protect against email header injection. * * From my testing, a line feed encoded as "\%6e" is still a line feed * according to sendmail. Get rid of hex encodeing before looking for * nasties. * * This routine modifies the string passed in. */ void injection_protection( char *s ) { char *injections[] = { "\\n", "\\r", "\n", "\r", "content-type:", "bcc:", "to:", "cc:" }; char *cleaned; char *p; int i; hex2char( s ); // If we find any injection text, drop whatever comes after it. for ( i = 0; i < sizeof(injections)/sizeof(char*); i++ ) { // s = "abcde\n" // 0123456 // p = "\n" // p - s = 5 p = strcasestr( s, injections[i] ); if ( p ) { // MKB: TODO: log injection attempt. if ( p - s > 0 ) strlcpy( s, s, p - s + 1 ); else *s = '\0'; } } } /** * Convert all hex entries to char's in the given string. * * If hex code resolves to a non-printable character, just drop * it. * * Return new string in the arg passed in. * */ void hex2char( char * s ) { char *cleaned; char *p; char *q; char hex_string[3]; unsigned int hex_int; cleaned = (char*) calloc(strlen(s) + 1, sizeof(char)); // Replace hex values with characters. If hex code references a // non-printable character, drop it and continue with rest of string. hex_string[2] = '\0'; p = s; q = cleaned; while ( *p ) { if ( *p != '%' ) { *q++ = *p++; } else { p++; if ( *p ) { hex_string[0] = *p++; if ( *p ) hex_string[1] = *p++; else hex_string[1] = '\0'; sscanf(hex_string, "%x", &hex_int); if( isprint(hex_int) ) { *q++ = (char)hex_int; } if ( *p == '\0' ) *q = '\0'; } else { // value terminates in a percentage sign. *q++ = '%'; *q = '\0'; } } } strcpy( s, cleaned ); free( cleaned ); }