517 lines
15 KiB
C
517 lines
15 KiB
C
/* This program is free software, licensed under the terms of the GNU GPL.
|
|
* See the Bongo COPYING file for full details
|
|
* Copyright (c) 2007 Alex Hudson
|
|
*/
|
|
|
|
#include <xpl.h>
|
|
#include <stdio.h>
|
|
#include <nmlib.h>
|
|
#include <msgapi.h>
|
|
#include <bongo-buildinfo.h>
|
|
#include <bongostore.h>
|
|
#include <bongoagent.h>
|
|
#include <gnutls/gnutls.h>
|
|
#include <gnutls/x509.h>
|
|
#include <gcrypt.h>
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include "config.h"
|
|
|
|
BongoConfig config;
|
|
|
|
void
|
|
usage(void) {
|
|
char *text =
|
|
"bongo-config - Configure parts of the Bongo system.\n\n"
|
|
"Usage: bongo-config [options] [command]\n\n"
|
|
"Options:\n"
|
|
" -v, --verbose verbose output\n"
|
|
" -s, --silent non-interactive mode\n\n"
|
|
"Commands:\n"
|
|
" install do the initial Bongo install\n"
|
|
" crypto regenerate data needed for encryption\n"
|
|
" checkversion see if a newer Bongo is available\n"
|
|
" tzcache regenerate cached timezone information\n"
|
|
"";
|
|
|
|
XplConsolePrintf("%s", text);
|
|
}
|
|
|
|
void
|
|
RunAsBongoUser() {
|
|
if (XplSetRealUser(MsgGetUnprivilegedUser()) < 0) {
|
|
printf("bongo-config: Could not drop to unpriviliged user '%s'\n", MsgGetUnprivilegedUser());
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
NMAPSimpleCommand(StoreClient *client, char *command) {
|
|
CCode ccode;
|
|
|
|
NMAPSendCommandF(client->conn, command);
|
|
ccode = NMAPReadAnswer(client->conn, client->buffer, sizeof(client->buffer), TRUE);
|
|
if (1000 == ccode)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
SetAdminRights(StoreClient *client, char *document) {
|
|
CCode ccode;
|
|
char *rights = "grant user:admin all;";
|
|
|
|
ccode = NMAPRunCommandF(client->conn, client->buffer, sizeof(client->buffer),
|
|
"PROPSET %s nmap.access-control %d\r\n", document, strlen(rights));
|
|
if (ccode == 2002) {
|
|
NMAPSendCommandF(client->conn, rights);
|
|
ccode = NMAPReadAnswer(client->conn, client->buffer,
|
|
sizeof(client->buffer), TRUE);
|
|
if (ccode == 1000)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
PutOrReplaceConfig(StoreClient *client, char *collection, char *filename, char *content) {
|
|
int len;
|
|
CCode ccode;
|
|
char command[1024];
|
|
|
|
len = strlen(content);
|
|
|
|
snprintf(command, 1000, "INFO %s/%s\r\n", collection, filename);
|
|
ccode = NMAPSendCommandF(client->conn, command);
|
|
ccode = NMAPReadAnswer(client->conn, client->buffer, sizeof(client->buffer), TRUE);
|
|
while (ccode == 2001)
|
|
ccode = NMAPReadAnswer(client->conn, client->buffer, sizeof(client->buffer), TRUE);
|
|
|
|
if (ccode == 1000) {
|
|
// INFO returned OK, document already exists
|
|
snprintf(command, 1000, "REPLACE %s/%s %d\r\n",
|
|
collection, filename, len);
|
|
} else {
|
|
// INFO errored, so document doesn't exist
|
|
snprintf(command, 1000, "WRITE %s %d %d \"F%s\"\r\n",
|
|
collection, STORE_DOCTYPE_CONFIG, len, filename);
|
|
}
|
|
|
|
NMAPSendCommandF(client->conn, command);
|
|
ccode = NMAPReadAnswer(client->conn, client->buffer, sizeof(client->buffer), TRUE);
|
|
|
|
if (ccode == 2002) {
|
|
ConnWrite(client->conn, content, len);
|
|
ConnFlush(client->conn);
|
|
if (NMAPReadAnswer(client->conn, client->buffer,
|
|
sizeof(client->buffer), TRUE) == 1000) {
|
|
return TRUE;
|
|
}
|
|
XplConsolePrintf("ERROR: Couldn't write data\n");
|
|
} else {
|
|
XplConsolePrintf("ERROR: Wouldn't accept data\n");
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
InitialStoreConfiguration() {
|
|
char path[XPL_MAX_PATH];
|
|
int store_pid;
|
|
char *args[3];
|
|
BOOL cal_success;
|
|
|
|
// attempt to initialise cal lib. This creates cached data, and can be time
|
|
// consuming: do it now, rather than in the store (for now, at least)
|
|
XplConsolePrintf("Initializing calendar data...\n");
|
|
cal_success = BongoCalInit(XPL_DEFAULT_DBF_DIR);
|
|
if (! cal_success) {
|
|
XplConsolePrintf("ERROR: Couldn't initialise calendar library\n");
|
|
exit(1);
|
|
}
|
|
|
|
XplConsolePrintf("Initializing store...\n");
|
|
store_pid = fork();
|
|
if (store_pid == 0) { // child
|
|
static char *install = "--install";
|
|
|
|
snprintf(path, XPL_MAX_PATH, "%s/%s", XPL_DEFAULT_BIN_DIR,
|
|
"bongostore");
|
|
args[0] = path;
|
|
args[1] = install;
|
|
args[2] = NULL;
|
|
|
|
execv(path, args);
|
|
XplConsolePrintf("ERROR: Couldn't start the store!\n");
|
|
exit(1);
|
|
} else { // parent
|
|
StoreClient *client;
|
|
int result = 0;
|
|
|
|
XplDelay(1000); // FIXME: small hack, wait for store to start.
|
|
|
|
client = malloc(sizeof(StoreClient));
|
|
if (!client) {
|
|
XplConsolePrintf("ERROR: Out of memory\n");
|
|
goto storecleanup;
|
|
}
|
|
client->conn = NMAPConnect("127.0.0.1", NULL);
|
|
if (!client->conn) {
|
|
XplConsolePrintf("ERROR: Could not connect to store\n");
|
|
goto storecleanup;
|
|
}
|
|
if (!NMAPAuthenticateToStore(client->conn, client->buffer, sizeof(client->buffer))) {
|
|
XplConsolePrintf("ERROR: Could not authenticate to store\n");
|
|
goto nmapcleanup;
|
|
}
|
|
|
|
if (!NMAPSimpleCommand(client, "STORE _system\r\n")) {
|
|
XplConsolePrintf("ERROR: Couldn't access store\n");
|
|
goto nmapcleanup;
|
|
}
|
|
result = NMAPSimpleCommand(client, "CREATE /config\r\n");
|
|
if ((result==1000) || (result==4226)) {
|
|
// collection can't be created and doesn't exist
|
|
XplConsolePrintf("ERROR: Couldn't create collection\n");
|
|
goto nmapcleanup;
|
|
}
|
|
if (! SetAdminRights(client, "/config")) {
|
|
XplConsolePrintf("ERROR: Couldn't set acls on /config\n");
|
|
goto nmapcleanup;
|
|
}
|
|
|
|
XplConsolePrintf("Setting default agent configuration...\n");
|
|
if (!PutOrReplaceConfig(client, "/config", "manager", bongo_manager_config)) {
|
|
XplConsolePrintf("ERROR: couldn't write /config/manager\n");
|
|
}
|
|
if (! SetAdminRights(client, "/config/manager")) {
|
|
XplConsolePrintf("ERROR: Couldn't set acls on /config/manager\n");
|
|
goto nmapcleanup;
|
|
}
|
|
if (!PutOrReplaceConfig(client, "/config", "antivirus", bongo_avirus_config)) {
|
|
XplConsolePrintf("ERROR: couldn't write /config/antivirus\n");
|
|
}
|
|
if (! SetAdminRights(client, "/config/antivirus")) {
|
|
XplConsolePrintf("ERROR: Couldn't set acls on /config/antivirus\n");
|
|
goto nmapcleanup;
|
|
}
|
|
if (!PutOrReplaceConfig(client, "/config", "antispam", bongo_aspam_config)) {
|
|
XplConsolePrintf("ERROR: couldn't write /config/antispam\n");
|
|
}
|
|
if (! SetAdminRights(client, "/config/antispam")) {
|
|
XplConsolePrintf("ERROR: Couldn't set acls on /config/antispam\n");
|
|
goto nmapcleanup;
|
|
}
|
|
|
|
XplConsolePrintf("Complete.\n");
|
|
|
|
nmapcleanup:
|
|
NMAPQuit(client->conn);
|
|
ConnFree(client->conn);
|
|
storecleanup:
|
|
if (kill(store_pid, SIGTERM) != 0) {
|
|
XplConsolePrintf("ERROR: Couldn't shut down store\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
GetInteractiveData(char *description, char **data, char *def) {
|
|
int bytes;
|
|
int size;
|
|
|
|
if (*data != NULL)
|
|
// don't ask for information we already have
|
|
return;
|
|
|
|
size = 100;
|
|
XplConsolePrintf("%s [%s]: ",
|
|
description, def);
|
|
*data = (char *)MemMalloc(sizeof(char)*(size+1));
|
|
bytes = getline (&*data, &size, stdin);
|
|
if (bytes <= 1) {
|
|
MemFree(data);
|
|
*data = def;
|
|
}
|
|
}
|
|
|
|
#define CERTSIZE 10240
|
|
|
|
BOOL
|
|
GenerateCryptoData() {
|
|
gnutls_dh_params_t dh_params;
|
|
gnutls_rsa_params_t rsa_params;
|
|
gnutls_x509_privkey key;
|
|
gnutls_x509_crt certificate;
|
|
unsigned char dhdata[CERTSIZE], rsadata[CERTSIZE], private_key[CERTSIZE], public_cert[CERTSIZE];
|
|
int ret;
|
|
size_t dsize = sizeof(unsigned char) * (CERTSIZE);
|
|
int cert_version = 1;
|
|
mode_t private = S_IRUSR | S_IWUSR;
|
|
FILE *params;
|
|
static const char *org = "Bongo Project";
|
|
static const char *unit = "Fake Certs Dept.";
|
|
static const char *state = "None";
|
|
static const char *country = "US";
|
|
static const char *blank = "";
|
|
|
|
gnutls_global_init();
|
|
|
|
// hack: we need to ensure the files below can be saved...
|
|
if (0 != mkdir(XPL_DEFAULT_DBF_DIR, 0755)) {
|
|
XplConsolePrintf("Couldn't create data directory!\n");
|
|
}
|
|
|
|
GetInteractiveData("IP address to run on", &config.ip, "127.0.0.1");
|
|
GetInteractiveData("DNS name to use:", &config.dns, "localhost");
|
|
|
|
// save a random seed for faster Xpl startup in future.
|
|
XplConsolePrintf("Creating random seed...\n");
|
|
XplSaveRandomSeed();
|
|
|
|
// various magic params
|
|
XplConsolePrintf("Creating DH parameters...\n");
|
|
gnutls_dh_params_init(&dh_params);
|
|
gnutls_dh_params_generate2(dh_params, 1024);
|
|
dsize = CERTSIZE;
|
|
gnutls_dh_params_export_pkcs3 (dh_params, GNUTLS_X509_FMT_PEM, dhdata, &dsize);
|
|
params = fopen(XPL_DEFAULT_DHPARAMS_PATH, "w");
|
|
chmod(XPL_DEFAULT_DHPARAMS_PATH, private);
|
|
if (params) {
|
|
fprintf(params, "%s\n", dhdata);
|
|
fclose(params);
|
|
} else {
|
|
XplConsolePrintf("ERROR: Couldn't write DH params to %s\n", XPL_DEFAULT_DHPARAMS_PATH);
|
|
return FALSE;
|
|
}
|
|
|
|
XplConsolePrintf("Creating RSA parameters...\n");
|
|
gnutls_rsa_params_init(&rsa_params);
|
|
gnutls_rsa_params_generate2(rsa_params, 512);
|
|
dsize = CERTSIZE;
|
|
gnutls_rsa_params_export_pkcs1 (rsa_params, GNUTLS_X509_FMT_PEM, rsadata, &dsize);
|
|
params = fopen(XPL_DEFAULT_RSAPARAMS_PATH, "w");
|
|
if (params) {
|
|
chmod(XPL_DEFAULT_RSAPARAMS_PATH, private);
|
|
fprintf(params, "%s\n", dhdata);
|
|
fclose(params);
|
|
} else {
|
|
XplConsolePrintf("ERROR: Couldn't write RSA params to %s\n", XPL_DEFAULT_DHPARAMS_PATH);
|
|
return FALSE;
|
|
}
|
|
|
|
// create private key
|
|
XplConsolePrintf("Creating private key...\n");
|
|
ret = gnutls_x509_privkey_init(&key);
|
|
if (ret < 0) {
|
|
XplConsolePrintf("ERROR: %s\n", gnutls_strerror (ret));
|
|
return FALSE;
|
|
}
|
|
ret = gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 1024, 0);
|
|
if (ret < 0) {
|
|
XplConsolePrintf("ERROR: Couldn't create private key. %s\n", gnutls_strerror(ret));
|
|
return FALSE;
|
|
}
|
|
dsize = CERTSIZE;
|
|
ret = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, &private_key, &dsize);
|
|
if (ret < 0) {
|
|
XplConsolePrintf("ERROR: Couldn't export private key. %s\n", gnutls_strerror(ret));
|
|
XplConsolePrintf(" Size: %d, Ret: %d\n", dsize, ret);
|
|
return FALSE;
|
|
} else {
|
|
params = fopen(XPL_DEFAULT_KEY_PATH, "w");
|
|
if (params) {
|
|
chmod(XPL_DEFAULT_KEY_PATH, private);
|
|
fprintf(params, "%s\n", private_key);
|
|
fclose(params);
|
|
} else {
|
|
XplConsolePrintf("ERROR: Couldn't write private key to %s\n", XPL_DEFAULT_KEY_PATH);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// create certificate
|
|
XplConsolePrintf("Creating SSL/TLS certificate...\n");
|
|
ret = gnutls_x509_crt_init(&certificate);
|
|
if (ret < 0) {
|
|
XplConsolePrintf("ERROR: %s\n", gnutls_strerror(ret));
|
|
return FALSE;
|
|
}
|
|
gnutls_x509_crt_set_activation_time(certificate, time(NULL));
|
|
gnutls_x509_crt_set_expiration_time(certificate, time(NULL) + (700 * 24 * 60 * 60));
|
|
gnutls_x509_crt_set_key(certificate, key);
|
|
gnutls_x509_crt_set_version(certificate, 1);
|
|
gnutls_x509_crt_set_serial(certificate, &cert_version, sizeof(int));
|
|
|
|
gnutls_x509_crt_set_dn_by_oid(certificate,
|
|
GNUTLS_OID_X520_ORGANIZATION_NAME, 0, org, strlen(org));
|
|
gnutls_x509_crt_set_dn_by_oid(certificate,
|
|
GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, unit, strlen(unit));
|
|
gnutls_x509_crt_set_dn_by_oid(certificate,
|
|
GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, state, strlen(state));
|
|
gnutls_x509_crt_set_dn_by_oid(certificate,
|
|
GNUTLS_OID_X520_COUNTRY_NAME, 0, country, strlen(country));
|
|
gnutls_x509_crt_set_dn_by_oid(certificate,
|
|
GNUTLS_OID_X520_LOCALITY_NAME, 0, blank, strlen(blank));
|
|
gnutls_x509_crt_set_dn_by_oid(certificate,
|
|
GNUTLS_OID_X520_COMMON_NAME, 0, config.dns, strlen(config.dns));
|
|
gnutls_x509_crt_set_dn_by_oid(certificate,
|
|
GNUTLS_OID_LDAP_UID, 0, blank, strlen(blank));
|
|
gnutls_x509_crt_set_subject_alternative_name (certificate,
|
|
GNUTLS_SAN_DNSNAME, config.dns);
|
|
|
|
gnutls_x509_crt_set_key_usage(certificate, GNUTLS_KEY_DIGITAL_SIGNATURE |
|
|
GNUTLS_KEY_DATA_ENCIPHERMENT);
|
|
|
|
ret = gnutls_x509_crt_sign2(certificate, certificate, key, GNUTLS_DIG_SHA1, 0);
|
|
if (ret < 0) {
|
|
XplConsolePrintf("ERROR: Couldn't self-sign certificate. %s\n", gnutls_strerror(ret));
|
|
return FALSE;
|
|
}
|
|
|
|
dsize = CERTSIZE;
|
|
ret = gnutls_x509_crt_export(certificate, GNUTLS_X509_FMT_PEM, &public_cert, &dsize);
|
|
if (ret < 0) {
|
|
XplConsolePrintf("ERROR: Couldn't create certificate. %s\n", gnutls_strerror(ret));
|
|
return FALSE;
|
|
}
|
|
params = fopen(XPL_DEFAULT_CERT_PATH, "w");
|
|
if (params) {
|
|
fprintf(params, "%s\n", public_cert);
|
|
fclose(params);
|
|
} else {
|
|
XplConsolePrintf("ERROR: Couldn't write certificate to %s\n", XPL_DEFAULT_CERT_PATH);
|
|
return FALSE;
|
|
}
|
|
|
|
gnutls_x509_crt_deinit(certificate);
|
|
gnutls_x509_privkey_deinit(key);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
CheckVersion() {
|
|
int current_version, available_version;
|
|
BOOL custom_version;
|
|
|
|
|
|
if (!MsgGetBuildVersion(¤t_version, &custom_version)) {
|
|
printf("Can't get information on current build!\n");
|
|
return;
|
|
} else {
|
|
printf("Installed build: %s %s%d\n", BONGO_BUILD_BRANCH, BONGO_BUILD_VSTR, current_version);
|
|
if (custom_version)
|
|
printf("This is modified software.\n");
|
|
}
|
|
printf("Version currently available: ");
|
|
if (!MsgGetAvailableVersion(&available_version)) {
|
|
printf("unable to request online information.\n");
|
|
} else {
|
|
printf("%s%d\n", BONGO_BUILD_VSTR, available_version);
|
|
}
|
|
}
|
|
|
|
void
|
|
TzCache() {
|
|
char path[PATH_MAX];
|
|
char *dbfdir = XPL_DEFAULT_DBF_DIR;
|
|
|
|
snprintf(path, PATH_MAX, "%s/%s", dbfdir, "bongo-tz.cache");
|
|
unlink(path);
|
|
|
|
BongoCalInit(dbfdir);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[]) {
|
|
int next_arg = 0;
|
|
int command = 0;
|
|
int startup = 0;
|
|
BongoAgent configtool;
|
|
config.verbose = 0;
|
|
|
|
if (!MemoryManagerOpen("bongo-config")) {
|
|
XplConsolePrintf("ERROR: Failed to initialize memory manager\n");
|
|
return 1;
|
|
}
|
|
|
|
// parse options
|
|
while (++next_arg < argc && argv[next_arg][0] == '-') {
|
|
char *arg = argv[next_arg];
|
|
if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) {
|
|
config.verbose = TRUE;
|
|
} else if (!strcmp(arg, "-s") || !strcmp(arg, "--silent")) {
|
|
config.interactive = FALSE;
|
|
} else if (!strcmp(arg, "--ip")) {
|
|
next_arg++;
|
|
config.ip = argv[next_arg];
|
|
} else if (!strcmp(arg, "--domain")) {
|
|
next_arg++;
|
|
config.dns = argv[next_arg];
|
|
} else {
|
|
printf("Unrecognized option: %s\n", argv[next_arg]);
|
|
}
|
|
}
|
|
|
|
// parse command
|
|
if (next_arg < argc) {
|
|
if (!strcmp(argv[next_arg], "install")) {
|
|
command = 1;
|
|
} else if (!strcmp(argv[next_arg], "crypto")) {
|
|
command = 2;
|
|
} else if (!strcmp(argv[next_arg], "checkversion")) {
|
|
command = 3;
|
|
} else if (!strcmp(argv[next_arg], "tzcache")) {
|
|
command = 4;
|
|
} else {
|
|
printf("Unrecognized command: %s\n", argv[next_arg]);
|
|
}
|
|
} else {
|
|
printf("ERROR: No command specified\n");
|
|
usage();
|
|
}
|
|
|
|
// unusual libgcrypt options must occur before XplInit()
|
|
if (command == 2) {
|
|
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
|
|
}
|
|
XplInit();
|
|
|
|
// we don't want to setup libraries unless we need to (e.g., to run an agent)
|
|
if (command == 1) {
|
|
startup = BA_STARTUP_CONNIO;
|
|
if (-1 == BongoAgentInit(&configtool, "bongoconfig", "", DEFAULT_CONNECTION_TIMEOUT, startup)) {
|
|
XplConsolePrintf("ERROR : Couldn't initialize Bongo libraries\n");
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
// don't give away privileges if we're going to need them later.
|
|
if (command != 1) {
|
|
RunAsBongoUser();
|
|
}
|
|
|
|
switch(command) {
|
|
case 1:
|
|
InitialStoreConfiguration();
|
|
break;
|
|
case 2:
|
|
GenerateCryptoData();
|
|
break;
|
|
case 3:
|
|
CheckVersion();
|
|
break;
|
|
case 4:
|
|
TzCache();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
MemoryManagerClose("bongo-config");
|
|
exit(0);
|
|
}
|