serverconfig.c
- Author: Johnny Mnemonic <johnny@themnemonic.org>
+ Author: Giovanni Giacobbi <giovanni@giacobbi.net>
Copyright (C) 1997 - 2002 Pekka Riikonen
#include "serverincludes.h"
#include "server_internal.h"
+#include <dirent.h>
#if 0
#define SERVER_CONFIG_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
}
/* parse an authdata according to its auth method */
-static bool my_parse_authdata(SilcAuthMethod auth_meth, char *p,
+static bool my_parse_authdata(SilcAuthMethod auth_meth, const char *p,
void **auth_data, SilcUInt32 *auth_data_len)
{
if (auth_meth == SILC_AUTH_PASSWORD) {
} else if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
/* p is a public key file name */
SilcPublicKey public_key;
+ SilcPublicKey cached_key;
if (!silc_pkcs_load_public_key(p, &public_key, SILC_PKCS_FILE_PEM))
if (!silc_pkcs_load_public_key(p, &public_key, SILC_PKCS_FILE_BIN)) {
return FALSE;
}
+ if (*auth_data &&
+ silc_hash_table_find_ext(*auth_data, public_key, (void **)&cached_key,
+ NULL, silc_hash_public_key, NULL,
+ silc_hash_public_key_compare, NULL)) {
+ silc_pkcs_public_key_free(public_key);
+ SILC_SERVER_LOG_WARNING(("Warning: public key file \"%s\" already "
+ "configured, ignoring this key", p));
+ return TRUE; /* non fatal error */
+ }
+
/* The auth_data is a pointer to the hash table of public keys. */
if (auth_data) {
if (*auth_data == NULL)
return TRUE;
}
+static bool my_parse_publickeydir(const char *dirname, void **auth_data)
+{
+ int total = 0;
+ struct dirent *get_file;
+ DIR *dp;
+
+ if (!(dp = opendir(dirname))) {
+ SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+ "Could not open directory \"%s\"", dirname));
+ return FALSE;
+ }
+
+ /* errors are not considered fatal */
+ while ((get_file = readdir(dp))) {
+ const char *filename = get_file->d_name;
+ char buf[1024];
+ int dirname_len = strlen(dirname), filename_len = strlen(filename);
+ struct stat check_file;
+
+ /* Ignore "." and "..", and take files only with ".pub" suffix. */
+ if (!strcmp(filename, ".") || !strcmp(filename, "..") ||
+ (filename_len < 5) || strcmp(filename + filename_len - 4, ".pub"))
+ continue;
+
+ memset(buf, 0, sizeof(buf));
+ snprintf(buf, sizeof(buf) - 1, "%s%s%s", dirname,
+ (dirname[dirname_len - 1] == '/' ? "" : "/"), filename);
+
+ if (stat(buf, &check_file) < 0) {
+ SILC_SERVER_LOG_ERROR(("Error stating file %s: %s", buf,
+ strerror(errno)));
+ } else if (S_ISREG(check_file.st_mode)) {
+ my_parse_authdata(SILC_AUTH_PUBLIC_KEY, buf, auth_data, NULL);
+ total++;
+ }
+ }
+
+ SILC_LOG_DEBUG(("Tried to load %d public keys in \"%s\"", total, dirname));
+ return TRUE;
+}
+
/* Callbacks */
SILC_CONFIG_CALLBACK(fetch_generic)
else if (!strcmp(name, "qos_limit_usec")) {
config->param.qos_limit_usec = *(SilcUInt32 *)val;
}
+ else if (!strcmp(name, "debug_string")) {
+ CONFIG_IS_DOUBLE(config->debug_string);
+ config->debug_string = (*(char *)val ? strdup((char *) val) : NULL);
+ }
else
return SILC_CONFIG_EINTERNAL;
SERVER_CONFIG_DEBUG(("Received PKCS type=%d name=\"%s\" (val=%x)",
type, name, context));
if (type == SILC_CONFIG_ARG_BLOCK) {
- /* check the temporary struct's fields */
+ /* Check the temporary struct's fields */
if (!tmp) /* discard empty sub-blocks */
return SILC_CONFIG_OK;
if (!tmp->name) {
SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigServerInfoInterface);
SilcServerConfigServerInfo *server_info = config->server_info;
- /* if there isn't the struct alloc it */
+ SERVER_CONFIG_DEBUG(("Received SERVERINFO type=%d name=\"%s\" (val=%x)",
+ type, name, context));
+
+ /* If there isn't the main struct alloc it */
if (!server_info)
config->server_info = server_info = (SilcServerConfigServerInfo *)
silc_calloc(1, sizeof(*server_info));
if (type == SILC_CONFIG_ARG_BLOCK) {
if (!strcmp(name, "primary")) {
+ if (server_info->primary) {
+ SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+ "Double primary specification."));
+ got_errno = SILC_CONFIG_EPRINTLINE;
+ goto got_err;
+ }
CONFIG_IS_DOUBLE(server_info->primary);
- if (!tmp)
- return SILC_CONFIG_OK;
+
+ /* now check the temporary struct, don't accept empty block and
+ make sure all fields are there */
+ if (!tmp || !tmp->server_ip || !tmp->port) {
+ got_errno = SILC_CONFIG_EMISSFIELDS;
+ goto got_err;
+ }
server_info->primary = tmp;
config->tmp = NULL;
return SILC_CONFIG_OK;
} else if (!strcmp(name, "secondary")) {
if (!tmp)
return SILC_CONFIG_OK;
+ if (!tmp || !tmp->server_ip || !tmp->port) {
+ got_errno = SILC_CONFIG_EMISSFIELDS;
+ goto got_err;
+ }
SILC_SERVER_CONFIG_LIST_APPENDTMP(server_info->secondary);
config->tmp = NULL;
return SILC_CONFIG_OK;
}
else if (!strcmp(name, "publickey")) {
char *file_tmp = (char *) val;
+ CONFIG_IS_DOUBLE(server_info->public_key);
- /* try to load specified file, if fail stop config parsing */
+ /* Try to load specified file, if fail stop config parsing */
if (!silc_pkcs_load_public_key(file_tmp, &server_info->public_key,
SILC_PKCS_FILE_PEM))
if (!silc_pkcs_load_public_key(file_tmp, &server_info->public_key,
}
}
else if (!strcmp(name, "privatekey")) {
+ struct stat st;
char *file_tmp = (char *) val;
+ CONFIG_IS_DOUBLE(server_info->private_key);
+
+ /* Check the private key file permissions. */
+ if ((stat(file_tmp, &st)) != -1) {
+ if ((st.st_mode & 0777) != 0600) {
+ SILC_SERVER_LOG_ERROR(("Wrong permissions in private key "
+ "file \"%s\". The permissions must be "
+ "0600.", file_tmp));
+ return SILC_CONFIG_ESILENT;
+ }
+ }
- /* try to load specified file, if fail stop config parsing */
+ /* Try to load specified file, if fail stop config parsing */
if (!silc_pkcs_load_private_key(file_tmp, &server_info->private_key,
"", 0, SILC_PKCS_FILE_BIN))
if (!silc_pkcs_load_private_key(file_tmp, &server_info->private_key,
return SILC_CONFIG_OK;
got_err:
- silc_free(tmp);
- silc_free(config->tmp);
- config->tmp = NULL;
+ /* Here we need to check if tmp exists because this function handles
+ * misc data (multiple fields and single-only fields) */
+ if (tmp) {
+ silc_free(tmp->server_ip);
+ silc_free(tmp);
+ config->tmp = NULL;
+ }
return got_errno;
}
goto got_err;
}
}
+ else if (!strcmp(name, "publickeydir")) {
+ if (!my_parse_publickeydir((char *) val, (void **)&tmp->publickeys)) {
+ got_errno = SILC_CONFIG_EPRINTLINE;
+ goto got_err;
+ }
+ }
else if (!strcmp(name, "params")) {
CONFIG_IS_DOUBLE(tmp->param);
tmp->param = my_find_param(config, (char *) val);
}
}
else if (!strcmp(name, "publickey")) {
- CONFIG_IS_DOUBLE(tmp->publickeys);
if (!my_parse_authdata(SILC_AUTH_PUBLIC_KEY, (char *) val,
(void **)&tmp->publickeys, NULL)) {
got_errno = SILC_CONFIG_EPRINTLINE;
goto got_err;
}
}
+ else if (!strcmp(name, "publickeydir")) {
+ if (!my_parse_publickeydir((char *) val, (void **)&tmp->publickeys)) {
+ got_errno = SILC_CONFIG_EPRINTLINE;
+ goto got_err;
+ }
+ }
else
return SILC_CONFIG_EINTERNAL;
return SILC_CONFIG_OK;
/* check the temporary struct's fields */
if (!tmp) /* discard empty sub-blocks */
return SILC_CONFIG_OK;
+ if (!tmp->host) {
+ got_errno = SILC_CONFIG_EMISSFIELDS;
+ goto got_err;
+ }
/* the temporary struct is ok, append it to the list */
SILC_SERVER_CONFIG_LIST_APPENDTMP(config->servers);
if (type == SILC_CONFIG_ARG_BLOCK) {
if (!tmp) /* discard empty sub-blocks */
return SILC_CONFIG_OK;
+ if (!tmp->host) {
+ got_errno = SILC_CONFIG_EMISSFIELDS;
+ goto got_err;
+ }
SILC_SERVER_CONFIG_LIST_APPENDTMP(config->routers);
config->tmp = NULL;
{ "qos_bytes_limit", SILC_CONFIG_ARG_INT, fetch_generic, NULL },
{ "qos_limit_sec", SILC_CONFIG_ARG_INT, fetch_generic, NULL },
{ "qos_limit_usec", SILC_CONFIG_ARG_INT, fetch_generic, NULL },
+ { "debug_string", SILC_CONFIG_ARG_STR, fetch_generic, NULL },
{ 0, 0, 0, 0 }
};
{ "host", SILC_CONFIG_ARG_STRE, fetch_client, NULL },
{ "passphrase", SILC_CONFIG_ARG_STR, fetch_client, NULL },
{ "publickey", SILC_CONFIG_ARG_STR, fetch_client, NULL },
+ { "publickeydir", SILC_CONFIG_ARG_STR, fetch_client, NULL },
{ "params", SILC_CONFIG_ARG_STR, fetch_client, NULL },
{ 0, 0, 0, 0 }
};
{ "nick", SILC_CONFIG_ARG_STRE, fetch_admin, NULL },
{ "passphrase", SILC_CONFIG_ARG_STR, fetch_admin, NULL },
{ "publickey", SILC_CONFIG_ARG_STR, fetch_admin, NULL },
+ { "publickeydir", SILC_CONFIG_ARG_STR, fetch_admin, NULL },
{ "port", SILC_CONFIG_ARG_INT, fetch_admin, NULL },
{ "params", SILC_CONFIG_ARG_STR, fetch_admin, NULL },
{ 0, 0, 0, 0 }
SILC_SERVER_CONNAUTH_TIMEOUT);
}
+/* Check for correctness of the configuration */
+
+static bool silc_server_config_check(SilcServerConfig config)
+{
+ bool ret = TRUE;
+ SilcServerConfigServer *s;
+ SilcServerConfigRouter *r;
+ bool b = FALSE;
+
+ /* ServerConfig is mandatory */
+ if (!config->server_info) {
+ SILC_SERVER_LOG_ERROR(("\nError: Missing mandatory block `ServerInfo'"));
+ ret = FALSE;
+ }
+
+ /* RouterConnection sanity checks */
+
+ if (config->routers && config->routers->backup_router == TRUE &&
+ !config->servers) {
+ SILC_SERVER_LOG_ERROR((
+ "\nError: First RouterConnection block must be primary router "
+ "connection. You have marked it incorrectly as backup router."));
+ ret = FALSE;
+ }
+ if (config->routers && config->routers->initiator == FALSE &&
+ config->routers->backup_router == FALSE) {
+ SILC_SERVER_LOG_ERROR((
+ "\nError: First RouterConnection block must be primary router "
+ "connection and it must be marked as Initiator."));
+ ret = FALSE;
+ }
+ if (config->routers && config->routers->backup_router == TRUE &&
+ !config->servers && !config->routers->next) {
+ SILC_SERVER_LOG_ERROR((
+ "\nError: You have configured backup router but not primary router. "
+ "If backup router is configured also primary router must be "
+ "configured."));
+ ret = FALSE;
+ }
+
+ /* Backup router sanity checks */
+
+ for (r = config->routers; r; r = r->next) {
+ if (r->backup_router && !strcmp(r->host, r->backup_replace_ip)) {
+ SILC_SERVER_LOG_ERROR((
+ "\nError: Backup router connection incorrectly configured to use "
+ "primary and backup router as same host `%s'. They must not be "
+ "same host.", r->host));
+ ret = FALSE;
+ }
+ }
+
+ /* ServerConnection sanity checks */
+
+ for (s = config->servers; s; s = s->next) {
+ if (s->backup_router) {
+ b = TRUE;
+ break;
+ }
+ }
+ if (b) {
+ for (s = config->servers; s; s = s->next) {
+ if (!s->backup_router) {
+ SILC_SERVER_LOG_ERROR((
+ "\nError: Your server is backup router but not all ServerConnection "
+ "blocks were marked as backup connections. They all must be "
+ "marked as backup connections."));
+ ret = FALSE;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
/* Allocates a new configuration object, opens configuration file and
parses it. The parsed data is returned to the newly allocated
configuration object. The SilcServerConfig must be freed by calling
SILC_SERVER_LOG_ERROR(("Error while parsing config file: %s.",
silc_config_strerror(ret)));
linebuf = silc_config_read_line(file, line);
- SILC_SERVER_LOG_ERROR((" file %s line %lu: %s\n", filename,
- line, linebuf));
- silc_free(linebuf);
+ if (linebuf) {
+ SILC_SERVER_LOG_ERROR((" file %s line %lu: %s\n", filename,
+ line, linebuf));
+ silc_free(linebuf);
+ }
}
silc_server_config_destroy(config_new);
+ silc_config_close(file);
return NULL;
}
/* close (destroy) the file object */
silc_config_close(file);
- /* If config_new is incomplete, abort the object and return NULL */
- if (!config_new->server_info) {
- SILC_SERVER_LOG_ERROR(("\nError: Missing mandatory block "
- "`server_info'"));
+ /* Check the configuration */
+ if (!silc_server_config_check(config_new)) {
silc_server_config_destroy(config_new);
return NULL;
}
/* Destroy general config stuff */
silc_free(config->module_path);
+ silc_free(config->debug_string);
silc_free(config->param.version_protocol);
silc_free(config->param.version_software);
silc_free(config->param.version_software_vendor);