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:
parent
3cb91d75f8
commit
21f33b6382
2 changed files with 32 additions and 86 deletions
116
src/conf.c
116
src/conf.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 *);
|
||||||
|
|
Loading…
Reference in a new issue