[-]
[+]
|
Changed |
mod_security-ix.changes
|
|
[-]
[+]
|
Changed |
mod_security-ix.spec
^
|
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/CHANGES
^
|
@@ -1,3 +1,40 @@
+08 Jun 2012 - 2.6.6
+-------------------
+
+ * Added build system support for KfreeBSD and HURD.
+
+ * Fixed a multipart bypass issue related to quote parsing
+ Credits to Qualys Vulnerability & Malware Research Labs (VMRL).
+
+20 Mar 2012 - 2.6.5
+-------------------
+
+ * Fixed increased a specific message debug level in SBDM code (MODSEC-293).
+
+ * Cleanup build system.
+
+09 Mar 2012 - 2.6.4
+-------------------
+
+ * Fixed Mlogc 100% CPU consume (Thanks Klaubert Herr and Ebrahim Khalilzadeh).
+
+ * Fixed ModSecurity cannot load session and user sdbm data.
+
+ * Fixed updateTargetById was creating rule unparsed content making apache memory grow.
+
+ * Code cleanup.
+
+23 Feb 2012 - 2.6.4-rc1
+-------------------
+
+ * Fixed @rsub adding garbage data into stream variables.
+
+ * Fixed regex for section A into mlogc-batch-load.pl (Thanks Ebrahim Khalilzadeh).
+
+ * Fixed logdata cuts message without closing it with final chars.
+
+ * Added sanitizeMatchedBytes support to verifyCPF, verifyCC and verifySSN.
+
23 Dec 2011 - 2.6.3
-------------------
@@ -245,7 +282,7 @@
importantly, override server configuration in <Location> and others.
(MODSEC-98) [Ivan Ristic]
- * Renamed the sanitise family of actiont to sanitize. Kept the old variants
+ * Renamed the sanitise family of actions to sanitize. Kept the old variants
for backward compatibility. (MODSEC-95) [Ivan Ristic]
* Improve the logging of the ctl action. (MODSEC-99) [Ivan Ristic]
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/msc_multipart.c
^
|
@@ -20,6 +20,32 @@
#include "msc_util.h"
#include "msc_parsers.h"
+void validate_quotes(modsec_rec *msr, unsigned char *data) {
+ int i, len;
+
+ if(msr == NULL)
+ return;
+
+ if(msr->mpd == NULL)
+ return;
+
+ if(data == NULL)
+ return;
+
+ len = strlen(data);
+
+ for(i = 0; i < len; i++) {
+
+ if(data[i] == '\'') {
+ if (msr->txcfg->debuglog_level >= 9) {
+ msr_log(msr, 9, "Multipart: Invalid quoting detected: %s length %d bytes",
+ log_escape_nq(msr->mp, data), len);
+ }
+ msr->mpd->flag_invalid_quoting = 1;
+ }
+ }
+}
+
#if 0
static char *multipart_construct_filename(modsec_rec *msr) {
@@ -155,6 +181,9 @@
/* evaluate part */
if (strcmp(name, "name") == 0) {
+
+ validate_quotes(msr, value);
+
if (msr->mpd->mpp->name != NULL) {
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition name: %s",
log_escape_nq(msr->mp, value));
@@ -169,6 +198,9 @@
}
else
if (strcmp(name, "filename") == 0) {
+
+ validate_quotes(msr, value);
+
if (msr->mpd->mpp->filename != NULL) {
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition filename: %s",
log_escape_nq(msr->mp, value));
@@ -187,7 +219,18 @@
while((*p == '\t') || (*p == ' ')) p++;
/* the next character must be a zero or a semi-colon */
if (*p == '\0') return 1; /* this is OK */
- if (*p != ';') return -12;
+ if (*p != ';') {
+ p--;
+ if(*p == '\'' || *p == '\"') {
+ if (msr->txcfg->debuglog_level >= 9) {
+ msr_log(msr, 9, "Multipart: Invalid quoting detected: %s length %d bytes",
+ log_escape_nq(msr->mp, p), strlen(p));
+ }
+ msr->mpd->flag_invalid_quoting = 1;
+ }
+ p++;
+ return -12;
+ }
p++; /* move over the semi-colon */
}
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/msc_release.h
^
|
@@ -38,7 +38,7 @@
#define MODSEC_VERSION_MAJOR "2"
#define MODSEC_VERSION_MINOR "6"
-#define MODSEC_VERSION_MAINT "3"
+#define MODSEC_VERSION_MAINT "6"
#define MODSEC_VERSION_TYPE ""
#define MODSEC_VERSION_RELEASE ""
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/msc_util.c
^
|
@@ -121,9 +121,9 @@
* \retval 0 On failure
* \retval string length On Success
*/
-int decode_base64_ext(char *plain_text, const char *input, int input_len)
+int decode_base64_ext(char *plain_text, const unsigned char *input, int input_len)
{
- const char *encoded = input;
+ const unsigned char *encoded = input;
int i = 0, j = 0, k = 0;
int ch = 0;
@@ -183,8 +183,7 @@
*
* \retval n The converted number
*/
-int convert_to_int(const char c)
-{
+int convert_to_int(const char c) {
int n;
if ((c>='0') && (c<='9'))
n = c - '0';
@@ -224,14 +223,6 @@
msr_log(msr, 9, "Added phrase match to TX.%d: %s",
tx_n, log_escape_nq_ex(msr->mp, s->value, s->value_len));
}
-
- /*
- for(i = 0; i <= 9; i++) {
- char buf[2];
- apr_snprintf(buf, sizeof(buf), "%d", i);
- apr_table_unset(msr->tx_vars, buf);
- }
- */
}
return 0;
@@ -446,7 +437,7 @@
}
*d = '\0';
- return strlen(begin);
+ return strlen((char *)begin);
}
/**
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/msc_util.h
^
|
@@ -81,7 +81,7 @@
char DSOLOCAL *log_escape_nul(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length);
-int DSOLOCAL decode_base64_ext(char *plain_text, const char *input, int input_len);
+int DSOLOCAL decode_base64_ext(char *plain_text, const unsigned char *input, int input_len);
int DSOLOCAL convert_to_int(const char c);
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/persist_dbm.c
^
|
@@ -108,10 +108,12 @@
goto cleanup;
}
- if(strstr(col_name,"USER") || strstr(col_name,"SESSION"))
- dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", msr->txcfg->webappid, "_", col_name, NULL);
- else
- dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL);
+ dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL);
+
+ if (msr->txcfg->debuglog_level >= 9) {
+ msr_log(msr, 9, "collection_retrieve_ex: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name),
+ log_escape(msr->mp, dbm_filename));
+ }
key.dptr = (char *)col_key;
key.dsize = col_key_len + 1;
@@ -353,6 +355,11 @@
// ENH: lowercase the var name in the filename
dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", var_name->value, NULL);
+ if (msr->txcfg->debuglog_level >= 9) {
+ msr_log(msr, 9, "collection_store: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, var_name->value),
+ log_escape(msr->mp, dbm_filename));
+ }
+
/* Delete IS_NEW on store. */
apr_table_unset(col, "IS_NEW");
@@ -584,11 +591,16 @@
else
dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL);
+ if (msr->txcfg->debuglog_level >= 9) {
+ msr_log(msr, 1, "collections_remove_stale: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name),
+ log_escape(msr->mp, dbm_filename));
+ }
+
rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK,
- CREATEMODE, msr->mp);
+ CREATEMODE, msr->mp);
if (rc != APR_SUCCESS) {
msr_log(msr, 1, "Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename),
- get_apr_error(msr->mp, rc));
+ get_apr_error(msr->mp, rc));
dbm = NULL;
goto error;
}
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/re.c
^
|
@@ -237,17 +237,24 @@
p = apr_strtok(NULL,",",&savedptr);
}
- curr_targets = msre_generate_target_string(ruleset->mp, rule);
-
- rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, curr_targets, NULL, NULL);
+ if(match == 0) {
+ curr_targets = msre_generate_target_string(ruleset->mp, rule);
+ rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, curr_targets, NULL, NULL);
+ }
end:
- if(target_list != NULL)
+ if(target_list != NULL) {
free(target_list);
- if(replace != NULL)
+ target_list = NULL;
+ }
+ if(replace != NULL) {
free(replace);
- if(target != NULL)
+ replace = NULL;
+ }
+ if(target != NULL) {
free(target);
+ target = NULL;
+ }
return NULL;
}
@@ -1785,8 +1792,9 @@
var->value_len = strlen(actionset->logdata);
expand_macros(msr, var, NULL, msr->mp);
- logdata = apr_psprintf(msr->mp, " [data \"%s\"]",
+ logdata = apr_psprintf(msr->mp, " [data \"%s",
log_escape_hex(msr->mp, (unsigned char *)var->value, var->value_len));
+ logdata = apr_pstrcat(msr->mp, logdata, "\"]", NULL);
/* If it is > 512 bytes, then truncate at 512 with ellipsis.
* NOTE: 512 actual data + 9 bytes of label = 521
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/re_actions.c
^
|
@@ -1855,7 +1855,7 @@
/* IMP1 We have a function for this now, parse_name_eq_value? */
s = strstr(data, "=");
if (s == NULL) return 0;
- col_name = strtolower_inplace(data);
+ col_name = strtolower_inplace((unsigned char *)data);
col_key = s + 1;
*s = '\0';
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/re_operators.c
^
|
@@ -449,40 +449,54 @@
if(msr->stream_output_data != NULL && output_body == 1) {
- char *stream_output_data = NULL;
+ memset(msr->stream_output_data, 0x0, msr->stream_output_length);
+ free(msr->stream_output_data);
+ msr->stream_output_data = NULL;
+ msr->stream_output_length = 0;
- stream_output_data = (char *)realloc(msr->stream_output_data, size+1);
- msr->stream_output_length = size;
+ msr->stream_output_data = (char *)malloc(size+1);
- if(stream_output_data == NULL) {
+ if(msr->stream_output_data == NULL) {
return -1;
}
- var->value_len = size;
+ msr->stream_output_length = size;
+ memset(msr->stream_output_data, 0x0, size+1);
+
msr->of_stream_changed = 1;
- msr->stream_output_data = (char *)stream_output_data;
- if(msr->stream_output_data != NULL)
- apr_cpystrn(msr->stream_output_data, data, size);
+ strncpy(msr->stream_output_data, data, size);
+ msr->stream_output_data[size] = '\0';
+
+ var->value_len = size;
+ var->value = msr->stream_output_data;
}
if(msr->stream_input_data != NULL && input_body == 1) {
- char *stream_input_data = NULL;
- stream_input_data = (char *)realloc(msr->stream_input_data, size+1);
- msr->stream_input_length = size;
+ memset(msr->stream_input_data, 0x0, msr->stream_input_length);
+ free(msr->stream_input_data);
+ msr->stream_input_data = NULL;
+ msr->stream_input_length = 0;
+
+ msr->stream_input_data = (char *)malloc(size+1);
- if(stream_input_data == NULL) {
+ if(msr->stream_input_data == NULL) {
return -1;
}
- var->value_len = size;
+ msr->stream_input_length = size;
+ memset(msr->stream_input_data, 0x0, size+1);
+
msr->if_stream_changed = 1;
- msr->stream_input_data = (char *)stream_input_data;
- if(msr->stream_input_data != NULL)
- apr_cpystrn(msr->stream_input_data, data, size);
+ strncpy(msr->stream_input_data, data, size);
+ msr->stream_input_data[size] = '\0';
+
+ var->value_len = size;
+ var->value = msr->stream_input_data;
+
}
if (! *error_msg) {
@@ -590,7 +604,12 @@
/* Are we supposed to capture subexpressions? */
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
+ if(!matched_bytes)
+ matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0;
+
matched = apr_table_get(rule->actionset->actions, "sanitizeMatched") ? 1 : 0;
+ if(!matched)
+ matched = apr_table_get(rule->actionset->actions, "sanitiseMatched") ? 1 : 0;
/* Show when the regex captures but "capture" is not set */
if (msr->txcfg->debuglog_level >= 6) {
@@ -2251,6 +2270,10 @@
int rc;
int is_cc = 0;
int offset;
+ int matched_bytes = 0;
+ char *qspos = NULL;
+ const char *parm = NULL;
+ msc_parm *mparm = NULL;
if (error_msg == NULL) return -1;
*error_msg = NULL;
@@ -2317,6 +2340,10 @@
* and we are done.
*/
+ matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
+ if(!matched_bytes)
+ matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0;
+
if (apr_table_get(rule->actionset->actions, "capture")) {
for(; i < rc; i++) {
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
@@ -2333,6 +2360,30 @@
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
log_escape_nq_ex(msr->mp, s->value, s->value_len));
}
+
+ if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) {
+ qspos = apr_psprintf(msr->mp, "%s", var->name);
+ parm = strstr(qspos, ":");
+ if (parm != NULL) {
+ parm++;
+ mparm = apr_palloc(msr->mp, sizeof(msc_parm));
+ if (mparm == NULL)
+ continue;
+
+ mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
+ mparm->pad_1 = rule->actionset->arg_min;
+ mparm->pad_2 = rule->actionset->arg_max;
+ apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
+ } else {
+ mparm = apr_palloc(msr->mp, sizeof(msc_parm));
+ if (mparm == NULL)
+ continue;
+
+ mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
+ apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
+ }
+ }
+
}
}
@@ -2500,6 +2551,11 @@
int rc;
int is_cpf = 0;
int offset;
+ int matched_bytes = 0;
+ char *qspos = NULL;
+ const char *parm = NULL;
+ msc_parm *mparm = NULL;
+
if (error_msg == NULL) return -1;
*error_msg = NULL;
@@ -2566,6 +2622,10 @@
* and we are done.
*/
+ matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
+ if(!matched_bytes)
+ matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0;
+
if (apr_table_get(rule->actionset->actions, "capture")) {
for(; i < rc; i++) {
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
@@ -2582,6 +2642,30 @@
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
log_escape_nq_ex(msr->mp, s->value, s->value_len));
}
+
+ if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) {
+ qspos = apr_psprintf(msr->mp, "%s", var->name);
+ parm = strstr(qspos, ":");
+ if (parm != NULL) {
+ parm++;
+ mparm = apr_palloc(msr->mp, sizeof(msc_parm));
+ if (mparm == NULL)
+ continue;
+
+ mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
+ mparm->pad_1 = rule->actionset->arg_min;
+ mparm->pad_2 = rule->actionset->arg_max;
+ apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
+ } else {
+ mparm = apr_palloc(msr->mp, sizeof(msc_parm));
+ if (mparm == NULL)
+ continue;
+
+ mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
+ apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
+ }
+ }
+
}
}
@@ -2739,6 +2823,10 @@
int rc;
int is_ssn = 0;
int offset;
+ int matched_bytes = 0;
+ char *qspos = NULL;
+ const char *parm = NULL;
+ msc_parm *mparm = NULL;
if (error_msg == NULL) return -1;
*error_msg = NULL;
@@ -2805,6 +2893,10 @@
* and we are done.
*/
+ matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
+ if(!matched_bytes)
+ matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0;
+
if (apr_table_get(rule->actionset->actions, "capture")) {
for(; i < rc; i++) {
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
@@ -2821,6 +2913,30 @@
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
log_escape_nq_ex(msr->mp, s->value, s->value_len));
}
+
+ if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) {
+ qspos = apr_psprintf(msr->mp, "%s", var->name);
+ parm = strstr(qspos, ":");
+ if (parm != NULL) {
+ parm++;
+ mparm = apr_palloc(msr->mp, sizeof(msc_parm));
+ if (mparm == NULL)
+ continue;
+
+ mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
+ mparm->pad_1 = rule->actionset->arg_min;
+ mparm->pad_2 = rule->actionset->arg_max;
+ apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
+ } else {
+ mparm = apr_palloc(msr->mp, sizeof(msc_parm));
+ if (mparm == NULL)
+ continue;
+
+ mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
+ apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
+ }
+ }
+
}
}
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/apache2/re_tfns.c
^
|
@@ -337,16 +337,15 @@
changed = 1;
incomment = 1;
i += 2;
- } else if ((input[i] == '-')&&(i + 1 < input_len)&&(input[i + 1] == '-')) {
+ } else if ((input[i] == '-')&&(i + 1 < input_len)&&(input[i + 1] == '-')
+ && (incomment == 0)) {
changed = 1;
input[i] = ' ';
break;
- i += 2;
- } else if (input[i] == '#') {
+ } else if (input[i] == '#' && (incomment == 0)) {
changed = 1;
input[i] = ' ';
- break;
- i++;
+ break;
} else {
input[j] = input[i];
i++;
@@ -736,7 +735,7 @@
{
*rval_len = input_len;
*rval = apr_palloc(mptmp, *rval_len);
- *rval_len = decode_base64_ext(*rval, (const char *)input, input_len);
+ *rval_len = decode_base64_ext(*rval, (const unsigned char *)input, input_len);
return *rval_len ? 1 : 0;
}
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/configure
^
|
@@ -12294,13 +12294,21 @@
freebsdos=true
;;
*-*-netbsd*)
- echo "Checking plataform... Identified as FreeBSD"
+ echo "Checking plataform... Identified as NetBSD"
netbsdos=true
;;
*-*-openbsd*)
- echo "Checking plataform... Identified as FreeBSD"
+ echo "Checking plataform... Identified as OpenBSD"
openbsdos=true
;;
+ *-*-kfreebsd*)
+ echo "Checking plataform... Identified as kFreeBSD, treating as linux"
+ linuxos=true
+ ;;
+ *-*-gnu*.*)
+ echo "Checking plataform... Identified as HURD, treating as linux"
+ linuxos=true
+ ;;
*)
echo "Unknown CANONICAL_HOST $host"
exit
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/configure.ac
^
|
@@ -110,13 +110,21 @@
freebsdos=true
;;
*-*-netbsd*)
- echo "Checking plataform... Identified as FreeBSD"
+ echo "Checking plataform... Identified as NetBSD"
netbsdos=true
;;
*-*-openbsd*)
- echo "Checking plataform... Identified as FreeBSD"
+ echo "Checking plataform... Identified as OpenBSD"
openbsdos=true
;;
+ *-*-kfreebsd*)
+ echo "Checking plataform... Identified as kFreeBSD, treating as linux"
+ linuxos=true
+ ;;
+ *-*-gnu*.*)
+ echo "Checking plataform... Identified as HURD, treating as linux"
+ linuxos=true
+ ;;
*)
echo "Unknown CANONICAL_HOST $host"
exit
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/mlogc/mlogc-batch-load.pl.in
^
|
@@ -92,7 +92,7 @@
next;
};
if ($sect eq 'A') {
- if ($line =~ m%^(\[[-\d/: a-zA-Z]{27}\]) (\S+) (\S+) (\d+) (\S+) (\d+)%) {
+ if ($line =~ m%^(\[[^:]+:\d+:\d+:\d+ [^\]]+\]) (\S+) (\S+) (\d+) (\S+) (\d+)%) {
$data{logtime} = $1;
$data{uniqueid} = $2;
$data{remote_addr} = $3;
|
[-]
[+]
|
Changed |
modsecurity-apache_2.6.6.tar.bz2/mlogc/mlogc.c
^
|
@@ -421,10 +421,13 @@
{
char linebuf[4100];
int line_count = -1;
+ int line_size = 0;
+ apr_status_t rc = 0;
+ char *p = NULL;
for(;;) {
- apr_status_t rc = apr_file_gets(linebuf, 4096, fd);
- char *p;
+ memset(linebuf, 0, 4100);
+ rc = apr_file_gets(linebuf, 4096, fd);
if (rc == APR_EOF) break;
if (rc != APR_SUCCESS) {
@@ -440,14 +443,16 @@
}
p = &linebuf[0];
+ line_size = strlen(p);
/* Remove the \n from the end of the line. */
- while(*p != '\0') {
+ while(*p != '\0' && line_size > 0) {
if (*p == '\n') {
*p = '\0';
break;
}
p++;
+ line_size--;
}
if (linebuf[0] == '#') { /* Ignore comments. */
|