Simplify reading lines from configuration files.

Instead of allocating storage for each line read, we now read into fixed-size
buffers on the stack. This fixes a case where a malformed configuration file
could crash tinc.
This commit is contained in:
Guus Sliepen 2010-03-01 23:35:02 +01:00
parent 3cb91d75f8
commit 21f33b6382
2 changed files with 32 additions and 86 deletions

View file

@ -26,6 +26,7 @@
#include "conf.h" #include "conf.h"
#include "logger.h" #include "logger.h"
#include "netutl.h" /* for str2address */ #include "netutl.h" /* for str2address */
#include "protocol.h"
#include "utils.h" /* for cp */ #include "utils.h" /* for cp */
#include "xalloc.h" #include "xalloc.h"
@ -206,111 +207,60 @@ bool get_config_subnet(const config_t *cfg, subnet_t ** result) {
} }
/* /*
Read exactly one line and strip the trailing newline if any. If the Read exactly one line and strip the trailing newline if any.
file was on EOF, return NULL. Otherwise, return all the data in a
dynamically allocated buffer.
If line is non-NULL, it will be used as an initial buffer, to avoid
unnecessary mallocing each time this function is called. If buf is
given, and buf needs to be expanded, the var pointed to by buflen
will be increased.
*/ */
static char *readline(FILE * fp, char **buf, size_t *buflen) { static char *readline(FILE * fp, char *buf, size_t buflen) {
char *newline = NULL; char *newline = NULL;
char *p; char *p;
char *line; /* The array that contains everything that has been read so far */
char *idx; /* Read into this pointer, which points to an offset within line */
size_t size, newsize; /* The size of the current array pointed to by line */
size_t maxlen; /* Maximum number of characters that may be read with fgets. This is newsize - oldsize. */
if(feof(fp)) if(feof(fp))
return NULL; return NULL;
if(buf && buflen) { p = fgets(buf, buflen, fp);
size = *buflen;
line = *buf;
} else {
size = 100;
line = xmalloc(size);
}
maxlen = size; if(!p)
idx = line; return NULL;
*idx = 0;
for(;;) { newline = strchr(p, '\n');
errno = 0;
p = fgets(idx, maxlen, fp);
if(!p) { /* EOF or error */ if(!newline)
if(feof(fp)) return NULL;
break;
/* otherwise: error; let the calling function print an error message if applicable */ *newline = '\0'; /* kill newline */
free(line); if(newline > p && newline[-1] == '\r') /* and carriage return if necessary */
return NULL; newline[-1] = '\0';
}
newline = strchr(p, '\n'); return buf;
if(!newline) { /* We haven't yet read everything to the end of the line */
newsize = size << 1;
line = xrealloc(line, newsize);
idx = &line[size - 1];
maxlen = newsize - size + 1;
size = newsize;
} else {
*newline = '\0'; /* kill newline */
if(newline > p && newline[-1] == '\r') /* and carriage return if necessary */
newline[-1] = '\0';
break; /* yay */
}
}
if(buf && buflen) {
*buflen = size;
*buf = line;
}
return line;
} }
/* /*
Parse a configuration file and put the results in the configuration tree Parse a configuration file and put the results in the configuration tree
starting at *base. starting at *base.
*/ */
int read_config_file(avl_tree_t *config_tree, const char *fname) { bool read_config_file(avl_tree_t *config_tree, const char *fname) {
int err = -2; /* Parse error */
FILE *fp; FILE *fp;
char *buffer, *line; char buffer[MAX_STRING_SIZE];
char *line;
char *variable, *value, *eol; char *variable, *value, *eol;
int lineno = 0; int lineno = 0;
int len; int len;
bool ignore = false; bool ignore = false;
config_t *cfg; config_t *cfg;
size_t bufsize; bool result = false;
fp = fopen(fname, "r"); fp = fopen(fname, "r");
if(!fp) { if(!fp) {
logger(LOG_ERR, "Cannot open config file %s: %s", fname, logger(LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
strerror(errno)); return false;
return -3;
} }
bufsize = 100;
buffer = xmalloc(bufsize);
for(;;) { for(;;) {
if(feof(fp)) { line = readline(fp, buffer, sizeof buffer);
err = 0;
break;
}
line = readline(fp, &buffer, &bufsize);
if(!line) { if(!line) {
err = -1; if(feof(fp))
result = true;
break; break;
} }
@ -361,46 +311,46 @@ int read_config_file(avl_tree_t *config_tree, const char *fname) {
config_add(config_tree, cfg); config_add(config_tree, cfg);
} }
free(buffer);
fclose(fp); fclose(fp);
return err; return result;
} }
bool read_server_config() { bool read_server_config() {
char *fname; char *fname;
int x; bool x;
xasprintf(&fname, "%s/tinc.conf", confbase); xasprintf(&fname, "%s/tinc.conf", confbase);
x = read_config_file(config_tree, fname); x = read_config_file(config_tree, fname);
if(x == -1) { /* System error: complain */ if(!x) { /* System error: complain */
logger(LOG_ERR, "Failed to read `%s': %s", fname, strerror(errno)); logger(LOG_ERR, "Failed to read `%s': %s", fname, strerror(errno));
} }
free(fname); free(fname);
return x == 0; return x;
} }
FILE *ask_and_open(const char *filename, const char *what) { FILE *ask_and_open(const char *filename, const char *what) {
FILE *r; FILE *r;
char *directory; char *directory;
char *fn; char line[PATH_MAX];
const char *fn;
/* Check stdin and stdout */ /* Check stdin and stdout */
if(!isatty(0) || !isatty(1)) { if(!isatty(0) || !isatty(1)) {
/* Argh, they are running us from a script or something. Write /* Argh, they are running us from a script or something. Write
the files to the current directory and let them burn in hell the files to the current directory and let them burn in hell
for ever. */ for ever. */
fn = xstrdup(filename); fn = filename;
} else { } else {
/* Ask for a file and/or directory name. */ /* Ask for a file and/or directory name. */
fprintf(stdout, "Please enter a file to save %s to [%s]: ", fprintf(stdout, "Please enter a file to save %s to [%s]: ",
what, filename); what, filename);
fflush(stdout); fflush(stdout);
fn = readline(stdin, NULL, NULL); fn = readline(stdin, line, sizeof line);
if(!fn) { if(!fn) {
fprintf(stderr, "Error while reading stdin: %s\n", fprintf(stderr, "Error while reading stdin: %s\n",
@ -410,7 +360,7 @@ FILE *ask_and_open(const char *filename, const char *what) {
if(!strlen(fn)) if(!strlen(fn))
/* User just pressed enter. */ /* User just pressed enter. */
fn = xstrdup(filename); fn = filename;
} }
#ifdef HAVE_MINGW #ifdef HAVE_MINGW
@ -423,7 +373,6 @@ FILE *ask_and_open(const char *filename, const char *what) {
directory = get_current_dir_name(); directory = get_current_dir_name();
xasprintf(&p, "%s/%s", directory, fn); xasprintf(&p, "%s/%s", directory, fn);
free(fn);
free(directory); free(directory);
fn = p; fn = p;
} }
@ -437,12 +386,9 @@ FILE *ask_and_open(const char *filename, const char *what) {
if(!r) { if(!r) {
fprintf(stderr, "Error opening file `%s': %s\n", fprintf(stderr, "Error opening file `%s': %s\n",
fn, strerror(errno)); fn, strerror(errno));
free(fn);
return NULL; return NULL;
} }
free(fn);
return r; return r;
} }

View file

@ -54,7 +54,7 @@ extern bool get_config_string(const config_t *, char **);
extern bool get_config_address(const config_t *, struct addrinfo **); extern bool get_config_address(const config_t *, struct addrinfo **);
extern bool get_config_subnet(const config_t *, struct subnet_t **); extern bool get_config_subnet(const config_t *, struct subnet_t **);
extern int read_config_file(avl_tree_t *, const char *); extern bool read_config_file(avl_tree_t *, const char *);
extern bool read_server_config(void); extern bool read_server_config(void);
extern FILE *ask_and_open(const char *, const char *); extern FILE *ask_and_open(const char *, const char *);
extern bool is_safe_path(const char *); extern bool is_safe_path(const char *);