/* * confparser.c * * Copyright (C) 2018 by Bogomil Vasilev * * This file is part of Remote Management and Provisioning System (RMPS). * * RMPS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * RMPS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with RMPS. If not, see . */ #include #include #include #include #include #include #include #include "log.h" #include "confparser.h" #include "enum_functions.h" static int test_conf_perms(char *config); static int test_conf_syntax(char *config); struct conf_table conf = { 0, /* isvalid initial state */ { "", /* db.type */ "", /* db.hostname */ "", /* db.port */ "" /* db.pass */ }, { "127.0.0.1", /* rmps.agent_ip */ "7000", /* rmps.agent_port */ "127.0.0.1", /* rmps.client_ip */ "7001", /* rmps.client_port */ "/var/log/rmps/rmpsd.log", "/var/log/rmps/rmpsd.err", '2', /* rmps.loglevel */ "/run/rmps/rmpsd.pid", "/etc/rmps/agent.crt", "/etc/rmps/agent.key", "/etc/rmps/ca.crt", "", /* rmps.cipherlist */ 2, /* rmps.agent_poolsize */ "/etc/rmps/client.crt", "/etc/rmps/client.key", 2 /* rmps.client_poolsize */ }, { 0 /* nfs -> TODO */ } }; 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 1; } *p = '/'; } fp = fopen(dir, "a"); if (!fp) { log(ERROR, "Permission denied to write into: %s", dir); return 1; } fclose(fp); return 0; } static int test_conf_perms(char *config) { struct stat s; char confresult[128]; char *config_copy = strdup(config); int err = stat(config, &s); if (err == -1) { if (errno == ENOENT) { enumtostr(confresult, CONF_MISSING); log(ERROR, confresult, config); return 1; } } else { if (!S_ISREG(s.st_mode)) { enumtostr(confresult, CONF_NOTFILE); log(ERROR, confresult, config); return 1; } if (!(0400 & s.st_mode)) { enumtostr(confresult, CONF_PERM); log(ERROR, confresult, config); return 1; } if (access(config, R_OK) != 0) { enumtostr(confresult, CONF_NOT_READABLE); log(ERROR, confresult, config); return 1; } if (s.st_uid != 0) { enumtostr(confresult, CONF_FILE_UID_INSECURE); log(WARNING, confresult, config); } else if (s.st_gid != 0) { enumtostr(confresult, CONF_FILE_GID_INSECURE); log(WARNING, confresult, config); } else if ((0004 & s.st_mode) || (0002 & s.st_mode)) { enumtostr(confresult, CONF_FILE_PERM_INSECURE); log(WARNING, confresult, config); } } err = stat(dirname(config_copy), &s); if (err == -1) { if (errno == ENOENT) { enumtostr(confresult, CONF_DIR_MISSING); log(ERROR, confresult, config_copy); return 1; } } else { if (!S_ISDIR(s.st_mode)) { enumtostr(confresult, CONF_DIR_NOTDIR); log(ERROR, confresult, config_copy); return 1; } if (!(0400 & s.st_mode) || !(0100 & s.st_mode)) { enumtostr(confresult, CONF_DIR_PERM); log(ERROR, confresult, config_copy); return 1; } if (s.st_uid != 0) { enumtostr(confresult, CONF_DIR_UID_INSECURE); log(WARNING, confresult, config_copy); } else if (s.st_gid != 0) { enumtostr(confresult, CONF_DIR_GID_INSECURE); log(WARNING, confresult, config_copy); } else if ((0004 & s.st_mode) || (0002 & s.st_mode)) { enumtostr(confresult, CONF_DIR_PERM_INSECURE); log(WARNING, confresult, config_copy); } } return 0; /* conf is readable */ } static int test_conf_syntax(char *config) { int i, j = 0, ok = 1, failed = 0; char buf[CFGLINESIZE], *tmp; FILE *fp = fopen(config, "r"); if (fp == NULL) { log(ERROR, "Failed to read %s", config); return 1; } while (fgets(buf, CFGLINESIZE, fp) != NULL) { j++; /* kill comments and ignore BLANK lines */ tmp = strstr(buf, "#"); if (tmp) *tmp = '\0'; if (buf[strspn(buf, " \t\v\r\n")] == '\0') continue; /* If we have "=", it's a possible var */ tmp = strstr(buf, "="); if (tmp) *tmp = '\0'; else { log(ERROR, "Bad entry in %s, line %d: %s", config, j, buf); ok = 0; failed = 1; continue; } /* Check if there actually is a value after '=' */ i = strlen(tmp + 1); if (tmp[i] == '\n') tmp[i] = '\0'; if (tmp[strspn(tmp + 1, " \t\v\r\n") + 1] == '\0') { log(ERROR, "Specified entry without value, line %d: %s", j, buf); failed = 1; continue; } /* Here we check every single conf entry manually */ if (!strcmp(buf, "db.type")) { if (!strcmp(tmp + 1, "mysql")) { /* || !strcmp(tmp[1], "postgresql") */ /* || !strcmp(tmp[1], "oracle") */ strncpy(conf.db.type, tmp + 1, sizeof(conf.db.type) - 1); if (conf.db.port[0] == '\0') strncpy(conf.db.port, "3306", sizeof(conf.db.port) - 1); } else { ok = 0; failed = 1; } } else if (!strcmp(buf, "db.hostname")) /* Just save it, launch_rmps will check it */ strncpy(conf.db.hostname, tmp + 1, sizeof(conf.db.hostname) - 1); else if (!strcmp(buf, "db.port")) { i = strlen(tmp + 1); if (i < 6) { /* max 5 digits for network port */ if ((signed int)strspn(tmp + 1, "1234567890") == i) { i = atoi(tmp + 1); if (i > 0 && i < 65536) { strncpy(conf.db.port, tmp + 1, sizeof(conf.db.port)-1); continue; } } } ok = 0; failed = 1; } else if (!strcmp(buf, "db.pass")) strncpy(conf.db.pass, tmp + 1, sizeof(conf.db.pass) - 1); else if (!strcmp(buf, "rmps.agent_ip")) { /* TODO */ } else if (!strcmp(buf, "rmps.agent_port")) { i = strlen(tmp + 1); if (i < 6) { /* max 5 digits for network port */ if ((signed int)strspn(tmp + 1, "1234567890") == i) { i = atoi(tmp + 1); if (i > 0 && i < 65536) { strncpy(conf.rmps.agent_port, tmp + 1, sizeof(conf.rmps.agent_port) - 1); continue; } } } ok = 0; failed = 1; } else if (!strcmp(buf, "rmps.client_ip")) { /* TODO */ } else if (!strcmp(buf, "rmps.client_port")) { i = strlen(tmp + 1); if (i < 6) { /* max 5 digits for network port */ if ((signed int)strspn(tmp + 1, "1234567890") == i) { i = atoi(tmp + 1); if (i > 0 && i < 65536) { strncpy(conf.rmps.client_port, tmp + 1, sizeof(conf.rmps.client_port) - 1); continue; } } } ok = 0; failed = 1; } else if (!strcmp(buf, "rmps.logfile")) { strncpy(conf.rmps.logfile, tmp + 1, sizeof(conf.rmps.logfile) - 1); /*if (fopen_and_mkdir(conf.rmps.logfile) != 0) failed = 1;*/ } else if (!strcmp(buf, "rmps.errlog")) { strncpy(conf.rmps.errlog, tmp + 1, sizeof(conf.rmps.errlog) - 1); /*if (fopen_and_mkdir(conf.rmps.errlog) != 0) failed = 1;*/ } else if (!strcmp(buf, "rmps.pidfile")) { strncpy(conf.rmps.pidfile, tmp + 1, sizeof(conf.rmps.pidfile) - 1); /*if (fopen_and_mkdir(conf.rmps.pidfile) != 0) failed = 1;*/ } else if (!strcmp(buf, "rmps.loglevel")) { if (strlen(tmp + 1) == 1 && (tmp[1] > '0' && tmp[1] < '5')) conf.rmps.loglevel = tmp[1] - '0'; else failed = 1; } else if (!strcmp(buf, "rmps.agent_tls_crt")) { if (access(tmp + 1, F_OK) == -1) { log(ERROR, "%s is missing", tmp + 1); failed = 1; } else if (access(tmp + 1, R_OK) == -1) { log(ERROR, "%s is not readable", tmp + 1); failed = 1; } else strncpy(conf.rmps.agent_tls_crt, tmp + 1, sizeof(conf.rmps.agent_tls_crt) - 1); } else if (!strcmp(buf, "rmps.agent_tls_key")) { if (access(tmp + 1, F_OK) == -1) { log(ERROR, "%s is missing", tmp + 1); failed = 1; } else if (access(tmp + 1, R_OK) == -1) { log(ERROR, "%s is not readable", tmp + 1); failed = 1; } else strncpy(conf.rmps.agent_tls_key, tmp + 1, sizeof(conf.rmps.agent_tls_key) - 1); } else if (!strcmp(buf, "rmps.cipherlist")) { strncpy(conf.rmps.cipherlist, tmp + 1, sizeof(conf.rmps.cipherlist) - 1); } else if (!strcmp(buf, "rmps.cafile")) { if (access(tmp + 1, F_OK) == -1) { log(ERROR, "%s is missing", tmp + 1); failed = 1; } else if (access(tmp + 1, R_OK) == -1) { log(ERROR, "%s is not readable\n", tmp + 1); failed = 1; } else strncpy(conf.rmps.cafile, tmp + 1, sizeof(conf.rmps.cafile) - 1); } else if (!strcmp(buf, "rmps.client_tls_crt")) { if (access(tmp + 1, F_OK) == -1) { log(ERROR, "%s is missing", tmp + 1); failed = 1; } else if (access(tmp + 1, R_OK) == -1) { log(ERROR, "%s is not readable", tmp + 1); failed = 1; } else strncpy(conf.rmps.client_tls_crt, tmp + 1, sizeof(conf.rmps.client_tls_crt) - 1); } else if (!strcmp(buf, "rmps.client_tls_key")) { if (access(tmp + 1, F_OK) == -1) { log(ERROR, "%s is missing", tmp + 1); failed = 1; } else if (access(tmp + 1, R_OK) == -1) { log(ERROR, "%s is not readable", tmp + 1); failed = 1; } else strncpy(conf.rmps.client_tls_key, tmp + 1, sizeof(conf.rmps.client_tls_key) - 1); } else log(ERROR, "Unknown config entry on line %d: %s", j, buf); if (!ok) { log(ERROR, "Invalid value for \"%s\", line %d: \"%s\"", buf, j, tmp + 1); ok = !ok; } } fclose(fp); if (failed) return 1; conf.isvalid = 1; return 0; } int confparse(char *config) { int result; result = test_conf_perms(config); if (result) return 1; /* Bad conf perms */ result = test_conf_syntax(config); if (result != 0) return 1; /* Bad conf syntax */ return 0; /* seems legit */ }