#define _GNU_SOURCE #include #include #include #include #include #include #include #include "log.h" #include "confparser.h" #include "enum_functions.h" static bool test_conf_perms(char *config); static bool test_conf_syntax(char *config); static bool test_conf_required(void); struct conf_table conf; const char *conf_db_pass(void) { return conf.db.pass; } const char *conf_db_hostname(void) { return conf.db.hostname; } void confexport(void) { printf("db.type=%s\n" "db.hostname=%s\n" "db.port=%s\n" "db.pass=%s\n" "rmps.agent_ip=%s\n" "rmps.agent_port=%s\n" "rmps.client_ip=%s\n" "rmps.client_port=%s\n" "rmps.logfile=%s\n" "rmps.errlog=%s\n" "rmps.loglevel=%d\n" "rmps.pidfile=%s\n" "rmps.agent_tls_crt=%s\n" "rmps.agent_tls_key=%s\n" "rmps.cafile=%s\n" "rmps.cipherlist=%s\n" "rmps.agent_poolsize=%d\n" "rmps.client_tls_crt=%s\n" "rmps.client_tls_key=%s\n" "rmps.client_poolsize=%d\n", conf.db.type, conf.db.hostname, conf.db.port, conf.db.pass, conf.rmps.agent_ip, conf.rmps.agent_port, conf.rmps.client_ip, conf.rmps.client_port, conf.rmps.logfile, conf.rmps.errlog, conf.rmps.loglevel, conf.rmps.pidfile, conf.rmps.agent_tls_crt, conf.rmps.agent_tls_key, conf.rmps.cafile, conf.rmps.cipherlist, conf.rmps.agent_poolsize, conf.rmps.client_tls_crt, conf.rmps.client_tls_key, conf.rmps.client_poolsize ); } /* static int fopen_and_mkdir(const char *dir) { char tmp[256]; char *p = NULL; size_t len; FILE *fp; snprintf(tmp, sizeof(tmp), "%s", dir); len = strlen(tmp); if (tmp[len - 1] == '/') tmp[len - 1] = 0; for (p = tmp + 1; *p; p++) if (*p == '/') { struct stat st = {0}; *p = 0; if (stat(tmp, &st) == -1) log(VERBOSE, "Path \"%s\" doesn't exist. Creating...", tmp); if (mkdir(tmp, 0700) == -1 && errno != EEXIST) { log(ERROR, "Permission denied to create dir: %s", tmp); return false; } *p = '/'; } fp = fopen(dir, "a"); if (!fp) { log(ERROR, "Permission denied to write into: %s", dir); return false; } fclose(fp); return true; } */ static void init_conf(void) { conf.isvalid = 0; /* initial state */ asprintf(&conf.db.type, "mysql"); conf.db.hostname[0] = '\0'; conf.db.port = NULL; conf.db.pass = NULL; asprintf(&conf.rmps.agent_ip, "any"); asprintf(&conf.rmps.agent_port, "7000"); asprintf(&conf.rmps.client_ip, "any"); asprintf(&conf.rmps.client_port, "7001"); asprintf(&conf.rmps.logfile, "/var/log/rmps/rmpsd.log"); asprintf(&conf.rmps.errlog, "/var/log/rmps/rmpsd.err"); conf.rmps.loglevel = 2; /* WARNING == 2 */ asprintf(&conf.rmps.pidfile, "/run/rmps/rmpsd.pid"); asprintf(&conf.rmps.agent_tls_crt, "/etc/rmps/certs/server.crt"); asprintf(&conf.rmps.agent_tls_key, "/etc/rmps/certs/server.key"); asprintf(&conf.rmps.cafile, "/etc/rmps/certs/ca.crt"); conf.rmps.cipherlist = NULL; conf.rmps.agent_poolsize = 2; asprintf(&conf.rmps.client_tls_crt, "/etc/rmps/certs/server.crt"); asprintf(&conf.rmps.client_tls_key, "/etc/rmps/certs/server.key"); conf.rmps.client_poolsize = 2; //conf.nfs = {0}; /* TODO */ } static void log_and_free(char **msg, const char *config, char *mem) { log(ERROR, *msg, config); if (*msg) /* in case enumtostr() fails */ free(*msg); free(mem); } static bool test_conf_perms(char *config) { struct stat s; char *confresult; char *config_copy = strdup(config); int err = stat(config, &s); if (err == -1) { if (errno == ENOENT) { enumtostr(&confresult, CONF_MISSING); log_and_free(&confresult, config, config_copy); return false; } } else { if (!S_ISREG(s.st_mode)) { enumtostr(&confresult, CONF_NOTFILE); log_and_free(&confresult, config, config_copy); return false; } if (!(0400 & s.st_mode)) { enumtostr(&confresult, CONF_PERM); log_and_free(&confresult, config, config_copy); return false; } if (access(config, R_OK) != 0) { enumtostr(&confresult, CONF_NOT_READABLE); log_and_free(&confresult, config, config_copy); return false; } } err = stat(dirname(config_copy), &s); if (err == -1) { if (errno == ENOENT) { enumtostr(&confresult, CONF_DIR_MISSING); log_and_free(&confresult, config, config_copy); return false; } } else { if (!S_ISDIR(s.st_mode)) { enumtostr(&confresult, CONF_DIR_NOTDIR); log_and_free(&confresult, config, config_copy); return false; } if (!(0400 & s.st_mode) || !(0100 & s.st_mode)) { enumtostr(&confresult, CONF_DIR_PERM); log_and_free(&confresult, config, config_copy); return false; } } free(config_copy); return true; /* conf is readable */ } /* Import the config and test the syntax */ static bool test_conf_syntax(char *config) { int i, line_itr = 0; bool failed = false, val_ok = true; char *line = NULL, *val_ptr; size_t len = 0; FILE *fp = fopen(config, "r"); if (fp == NULL) { log(ERROR, "Failed to read %s", config); return false; } while (getline(&line, &len, fp) != -1) { line_itr++; /* kill comments and ignore BLANK lines */ val_ptr = strstr(line, "#"); if (val_ptr) *val_ptr = '\0'; if (line[strspn(line, " \t\v\r\n")] == '\0') continue; /* If we have "=", it's a possible var */ val_ptr = strstr(line, "="); if (val_ptr) *val_ptr = '\0'; else { log(ERROR, "Bad entry in %s, line %d: %s", config, line_itr, line); val_ok = false; failed = true; continue; } /* Check if there actually is a value after '=' */ i = strlen(val_ptr + 1); if (val_ptr[i] == '\n') { val_ptr[i] = '\0'; /* move ptr to beginning of the value str */ val_ptr = val_ptr + 1; } if (val_ptr[strspn(val_ptr, " \t\v\r\n")] == '\0') { log(ERROR, "Specified entry without value, line %d: %s", line_itr, line); failed = true; continue; } /* Here we check every single conf entry manually */ if (!strcmp(line, "db.type")) { if (!strcmp(val_ptr, "mysql")) { /* || !strcmp(val_ptr[1], "postgresql") */ /* || !strcmp(val_ptr[1], "oracle") */ asprintf(&conf.db.type, "%s", val_ptr); if (!conf.db.port) asprintf(&conf.db.port, "%s", val_ptr); } else { val_ok = false; log(ERROR, "Invalid db.type: %s", "%s", val_ptr); failed = true; } } else if (!strcmp(line, "db.hostname")) /* Just save it, launch_rmps will check it */ strncpy(conf.db.hostname, val_ptr, HOSTNAMESIZE - 1); else if (!strcmp(line, "db.port")) { i = strlen(val_ptr); if (i < 6) { /* max 5 digits for network port */ if ((signed int)strspn(val_ptr, "1234567890") == i) { i = atoi(val_ptr); if (i > 0 && i < 65536) { asprintf(&conf.db.port, "%s", val_ptr); continue; } } } log(ERROR, "Invalid db.port value: %s", val_ptr); val_ok = false; failed = true; } else if (!strcmp(line, "db.pass")) asprintf(&conf.db.pass, "%s", val_ptr); else if (!strcmp(line, "rmps.agent_ip")) { asprintf(&conf.rmps.agent_ip, "%s", val_ptr); } else if (!strcmp(line, "rmps.agent_port")) { i = strlen(val_ptr); if (i < 6) { /* max 5 digits for network port */ if ((signed int)strspn(val_ptr, "1234567890") == i) { i = atoi(val_ptr); if (i > 0 && i < 65536) { asprintf(&conf.rmps.agent_port, "%s", val_ptr); continue; } } } log(ERROR, "Invalid rmps.agent_port value: %s", val_ptr); val_ok = false; failed = true; } else if (!strcmp(line, "rmps.agent_poolsize")) { i = strlen(val_ptr); if ((signed int)strspn(val_ptr, "1234567890") == i) { i = atoi(val_ptr); if (i >= 0) { conf.rmps.agent_poolsize = i; continue; } } log(ERROR, "Invalid rmps.agent_poolsize value: %s", val_ptr); val_ok = false; failed = true; } else if (!strcmp(line, "rmps.client_ip")) { asprintf(&conf.rmps.client_ip, "%s", val_ptr); } else if (!strcmp(line, "rmps.client_port")) { i = strlen(val_ptr); if (i < 6) { /* max 5 digits for network port */ if ((signed int)strspn(val_ptr, "1234567890") == i) { i = atoi(val_ptr); if (i > 0 && i < 65536) { asprintf(&conf.rmps.client_port, "%s", val_ptr); continue; } } } log(ERROR, "Invalid rmps.client_port value: %s", val_ptr); val_ok = false; failed = true; } else if (!strcmp(line, "rmps.client_poolsize")) { i = strlen(val_ptr); if ((signed int)strspn(val_ptr, "1234567890") == i) { i = atoi(val_ptr); if (i >= 0) { conf.rmps.client_poolsize = i; continue; } } log(ERROR, "Invalid rmps.client_poolsize value: %s", val_ptr); val_ok = false; failed = true; } else if (!strcmp(line, "rmps.logfile")) { asprintf(&conf.rmps.logfile, "%s", val_ptr); /*if (fopen_and_mkdir(conf.rmps.logfile) != 0) failed = true;*/ } else if (!strcmp(line, "rmps.errlog")) { asprintf(&conf.rmps.errlog, "%s", val_ptr); /*if (fopen_and_mkdir(conf.rmps.errlog) != 0) failed = true;*/ } else if (!strcmp(line, "rmps.pidfile")) { asprintf(&conf.rmps.pidfile, "%s", val_ptr); /*if (fopen_and_mkdir(conf.rmps.pidfile) != 0) failed = true;*/ } else if (!strcmp(line, "rmps.loglevel")) { if (strlen(val_ptr) == 1 && (val_ptr[0] > '0' && val_ptr[0] < '5')) conf.rmps.loglevel = val_ptr[0] - '0'; else { log(ERROR, "Invalid rmps.loglevel: %s", val_ptr); failed = true; } } else if (!strcmp(line, "rmps.agent_tls_crt")) { if (access(val_ptr, F_OK) == -1) { log(ERROR, "%s is missing", val_ptr); failed = true; } else if (access(val_ptr, R_OK) == -1) { log(ERROR, "%s is not readable", val_ptr); failed = true; } else asprintf(&conf.rmps.agent_tls_crt, "%s", val_ptr); } else if (!strcmp(line, "rmps.agent_tls_key")) { if (access(val_ptr, F_OK) == -1) { log(ERROR, "%s is missing", val_ptr); failed = true; } else if (access(val_ptr, R_OK) == -1) { log(ERROR, "%s is not readable", val_ptr); failed = true; } else asprintf(&conf.rmps.agent_tls_key, "%s", val_ptr); } else if (!strcmp(line, "rmps.cipherlist")) { asprintf(&conf.rmps.cipherlist, "%s", val_ptr); } else if (!strcmp(line, "rmps.cafile")) { if (access(val_ptr, F_OK) == -1) { log(ERROR, "%s is missing", val_ptr); failed = true; } else if (access(val_ptr, R_OK) == -1) { log(ERROR, "%s is not readable\n", val_ptr); failed = true; } else asprintf(&conf.rmps.cafile, "%s", val_ptr); } else if (!strcmp(line, "rmps.client_tls_crt")) { if (access(val_ptr, F_OK) == -1) { log(ERROR, "%s is missing", val_ptr); failed = true; } else if (access(val_ptr, R_OK) == -1) { log(ERROR, "%s is not readable", val_ptr); failed = true; } else asprintf(&conf.rmps.client_tls_crt, "%s", val_ptr); } else if (!strcmp(line, "rmps.client_tls_key")) { if (access(val_ptr, F_OK) == -1) { log(ERROR, "%s is missing", val_ptr); failed = true; } else if (access(val_ptr, R_OK) == -1) { log(ERROR, "%s is not readable", val_ptr); failed = true; } else asprintf(&conf.rmps.client_tls_key, "%s", val_ptr); } else { log(ERROR, "Unknown config entry on line %d: %s", line_itr, line); failed = true; } if (!val_ok) { log(ERROR, "Invalid value for \"%s\", line %d: \"%s\"", line, line_itr, val_ptr); val_ok = !val_ok; } } fclose(fp); if (errno) log(ERROR, "confparse - getline() - %s", strerror(errno)); free(line); if (failed) return false; return true; } static bool test_conf_required(void) { bool failed = false; if (conf.db.hostname[0] == '\0') { log(ERROR, "cond.db.hostname - not set!"); failed = true; } if (!conf.db.type) { log(ERROR, "cond.db.type - not set!"); failed = true; } if (!conf.db.pass) { log(ERROR, "cond.db.pass - not set!"); failed = true; } if (!conf.db.port) { log(ERROR, "cond.db.port - not set!"); failed = true; } if (!conf.rmps.cipherlist) { log(ERROR, "conf.rmps.cipherlist - not set!"); failed = true; } if (failed) return false; return true; } bool confparse(char *config) { init_conf(); if (test_conf_perms(config) && test_conf_syntax(config) && test_conf_required()) conf.isvalid = true; return conf.isvalid; /* seems legit */ }