diff options
-rw-r--r-- | .gitignore | 5 | ||||
-rw-r--r-- | README.md | 1 | ||||
-rw-r--r-- | bastion/CMakeLists.txt | 2 | ||||
-rw-r--r-- | bastion/bastion.conf.example.in | 25 | ||||
-rw-r--r-- | bastion/client.c | 52 | ||||
-rw-r--r-- | bastion/main.c | 3 | ||||
-rw-r--r-- | bastion/proxy.c | 5 | ||||
-rw-r--r-- | bastion/session.c | 12 | ||||
-rw-r--r-- | bastion/state.c | 12 | ||||
-rw-r--r-- | common/config.c | 4 | ||||
-rw-r--r-- | common/config.h.in | 6 | ||||
-rw-r--r-- | common/data.c | 276 | ||||
-rw-r--r-- | common/data.h | 19 | ||||
-rw-r--r-- | common/data_for_config.h | 10 | ||||
-rw-r--r-- | common/mysql.c | 188 | ||||
-rw-r--r-- | common/mysql.h | 22 |
16 files changed, 362 insertions, 280 deletions
@@ -1,6 +1,3 @@ -bastion build -id_rsa -ssh_host_*_key -test_client.c +stuff *.sw[a-p] @@ -29,7 +29,6 @@ This project has only two hard dependencies : The following are optional dependencies : - the libtty from https://github.com/kilobyte/termrec which allows session recording. - compression libraries like libbz2, liblzma, libz allow on the fly compression of session records. -- libmysql for now because it hosts the runtime config ## Manual Installation diff --git a/bastion/CMakeLists.txt b/bastion/CMakeLists.txt index 298cfe7..9fd4212 100644 --- a/bastion/CMakeLists.txt +++ b/bastion/CMakeLists.txt @@ -7,7 +7,7 @@ target_link_libraries(bastion common) if (${SESSION_RECORDING}) target_link_libraries(bastion libtty) endif() -target_link_libraries(bastion bz2 config lzma mysqlclient ssh z) +target_link_libraries(bastion bz2 config lzma ssh z) install(TARGETS bastion DESTINATION bin) diff --git a/bastion/bastion.conf.example.in b/bastion/bastion.conf.example.in new file mode 100644 index 0000000..34ac9b4 --- /dev/null +++ b/bastion/bastion.conf.example.in @@ -0,0 +1,25 @@ +port = 2222; + +keys: +{ + dsa = "@CMAKE_INSTALL_PREFIX@/etc/bastion/ssh_host_dsa_key"; + rsa = "@CMAKE_INSTALL_PREFIX@/etc/bastion/ssh_host_rsa_key"; + ecdsa = "@CMAKE_INSTALL_PREFIX@/etc/bastion/ssh_host_ecdsa_key"; +}; + +session_recording: +{ + path = "@CMAKE_INSTALL_PREFIX@/var/log/bastion/$d/$h/$u/$i.gz"; # $d : date in iso format, $h : hostname, $u : username : $i session id +}; + +hostkeys = ( + { name = "default", path = "id_rsa_default" } +); + +users = ( + { name = "julien", public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDMdBAFjENiPMTtq90GT3+NZ68nfGxQiRExaYYnLzm1ecmulCvsuA4AOpeLY6f+FWe+ludiw7nhrXzssDdsKBy0QL+XQyvjjjW4X+k9MYhP1gAWXEOGJnjJ/1ovEsMt++6fLyNKLUTA46kErbEehDs22r+rIiEKatrn0BNrJcRI94H44oEL1/ImzVam0cSBL0tPiaJxe60sBs7M76zfyFtVdMGkeuBpS7ee+FLA58fsS3/sEZmkas8MT0QdvZz1y/66MknXYbIaqDSOUACXGF4yVKpogLRRJ1SgNo1Ujo/U3VOR1O4CiQczsZOcbSdjgl0x3fJb7BaIxrZy9iW2I7G/L/chfTvRws+x1s1y5FNZOOiXMCdZjhgLaRwb6p5gMsMVn9sJbhDjmejcAkBKQDkzbvxxhfVkH225FoVXA9YF0msWLyOEyZQYbA8autLDJsAOT5RDfw/G82DQBufAPEBR/bPby0Hl5kjqW75bpSVxDvzmKwt3EpITg9iuYEhvYZ/Zq5qC1UJ54ZfOvaf0PsTUzFePty6ve/JzfxCV1XgFQ+B8l4NSz11loDfNXSUngf7lL4qu5X4aN6WmLFO1YbyFlfpvt3K1CekJmWVeE5mV9EFTUJ4ParVWRGiA4W+zaCOsHgRkcGkp4eYGyWW8gOR/lVxYU2IFl9mbMrC9bkdRbQ==" } +); + +hosts = ( + { name = "myth", address = "myth", user = "root", hostkey = "default", public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPNmO4zkfz1RvWdKGRX8AEP4FrwDkW4EnBoU/GNm6Ooe" } +); diff --git a/bastion/client.c b/bastion/client.c index fc11bb6..543603f 100644 --- a/bastion/client.c +++ b/bastion/client.c @@ -3,7 +3,7 @@ #include <stdlib.h> #include "common/config.h" -#include "common/mysql.h" +#include "common/data.h" #include "client.h" #ifdef SESSION_RECORDING #include "recording.h" @@ -88,16 +88,10 @@ struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_cha cdata->client_channel_cb = NULL; /* First we try to add the private key that the server will accept */ - struct db_host_info * info = db_get_host_info(hostname); + struct data_host_info * info = data_get_host_info(hostname); if (info == NULL) goto host_info_clean; - ssh_key privkey = NULL; - if (ssh_pki_import_privkey_base64(info->privkeytxt, NULL, NULL, NULL, &privkey) != SSH_OK) { - printf("Error importing private key"); - goto privkey_clean; - } - /* We try to connect to the remote server */ printf("Connecting to %s\n", hostname); cdata->my_session = ssh_new(); @@ -116,34 +110,26 @@ struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_cha /* We now validate the remote server's public key */ ssh_key server_pub_key = NULL; - unsigned char * hash = NULL; - size_t hlen; - char * hexa = NULL; if (ssh_get_server_publickey(cdata->my_session, &server_pub_key) != SSH_OK) { fprintf(stderr, "Error getting server publickey: %s\n", ssh_get_error(cdata->my_session)); goto pubkey_clean; } - if (ssh_get_publickey_hash(server_pub_key, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen) != SSH_OK) { - fprintf(stderr, "Error getting publickey hash: %s\n", ssh_get_error(cdata->my_session)); - goto pubkey_hash_clean; + char *pubkeystr = NULL; + if (ssh_pki_export_pubkey_base64(server_pub_key, &pubkeystr) != SSH_OK) { + fprintf(stderr, "Got invalid public key from server attempt, this shouldn't happen.\n"); + goto pubkey_clean; } - hexa = ssh_get_hexa(hash, hlen); - if (strlen(info->hostkeyhash) > 0) { - if (strcmp(hexa, info->hostkeyhash) != 0) { - fprintf(stderr, "Error invalid host key for %s\n", hostname); - goto pubkey_hexa_clean; - } - } else { - // TODO we got a broken sshportal record, we need to fix it but only - // after we completed the migration from sshportal - //db_set_host_publickey_hash(hostname, hexa); + if (strcmp(info->pubkey, pubkeystr) != 0) { + fprintf(stderr, "Error : public key of server %s doesn't match.\n", hostname); + fprintf(stderr, "got : %s\n", pubkeystr); + fprintf(stderr, "exp : %s\n", info->pubkey); + goto pubkey_clean; } - ssh_string_free_char(hexa); - ssh_clean_pubkey_hash(&hash); + free(pubkeystr); ssh_key_free(server_pub_key); /* With the server checked, we can authenticate */ - if(ssh_userauth_publickey(cdata->my_session, NULL, privkey) == SSH_AUTH_SUCCESS){ + if(ssh_userauth_publickey(cdata->my_session, NULL, info->key) == SSH_AUTH_SUCCESS){ printf("Authentication success\n"); } else { printf("Error private key was rejected\n"); @@ -186,25 +172,19 @@ struct client_channel_data_struct* client_dial(ssh_event event, struct proxy_cha } #endif - ssh_key_free(privkey); - db_free_host_info(info); + free(info); return cdata; channel_clean: ssh_channel_free(cdata->my_channel); goto session_clean; -pubkey_hexa_clean: - ssh_string_free_char(hexa); -pubkey_hash_clean: - ssh_clean_pubkey_hash(&hash); pubkey_clean: + free(pubkeystr); ssh_key_free(server_pub_key); session_clean: ssh_disconnect(cdata->my_session); ssh_free(cdata->my_session); - db_free_host_info(info); -privkey_clean: - ssh_key_free(privkey); + free(info); host_info_clean: free(cdata); return NULL; diff --git a/bastion/main.c b/bastion/main.c index 4f93652..d4fb321 100644 --- a/bastion/main.c +++ b/bastion/main.c @@ -5,7 +5,6 @@ #include <sys/wait.h> #include "common/config.h" -#include "common/mysql.h" #include "session.h" #include "state.h" @@ -28,7 +27,6 @@ __attribute__((noreturn)) static void sigint_handler(int signo) state_clean(); config_clean(); ssh_finalize(); - db_clean(); exit(0); } @@ -145,6 +143,5 @@ int main() ssh_bind_free(sshbind); config_clean(); ssh_finalize(); - db_clean(); return 0; } diff --git a/bastion/proxy.c b/bastion/proxy.c index f608d31..0cf3675 100644 --- a/bastion/proxy.c +++ b/bastion/proxy.c @@ -2,7 +2,6 @@ #include <stdio.h> #include <stdlib.h> -#include "common/mysql.h" #include "client.h" #include "proxy.h" #include "state.h" @@ -202,8 +201,8 @@ void handle_proxy_session(ssh_event event, ssh_session session, ssh_channel my_c ssh_callbacks_init(&channel_cb); ssh_set_channel_callbacks(my_channel, &channel_cb); - db_clean(); // we close the mysql connection before the main loop, as to not waste ressources - + // TODO gather what is required from data and config so that we can free + // those and not waste resources do { /* Poll the main event which takes care of the sessions and channels */ if (ssh_event_dopoll(event, -1) == SSH_ERROR) { diff --git a/bastion/session.c b/bastion/session.c index f86fe44..4c38389 100644 --- a/bastion/session.c +++ b/bastion/session.c @@ -9,7 +9,7 @@ #include <sys/wait.h> #include "common/config.h" -#include "common/mysql.h" +#include "common/data.h" #include "proxy.h" #include "session.h" #include "state.h" @@ -31,19 +31,19 @@ static int auth_pubkey(ssh_session session, const char *user, ssh_key pubkey, ch // TODO check for an invite - char * bastion_username = db_get_username_from_pubkey(pubkey); + const char * bastion_username = data_get_username_from_pubkey(pubkey); if (bastion_username != NULL) { sdata->authenticated = 1; if (state_set_ssh_destination(user) != 0) return SSH_ERROR; // TODO check access rights and host configs state_set_bastion_username(bastion_username); - unsigned long long session_id = db_init_session_and_get_id(user, bastion_username); - state_set_session_id(session_id); - free(bastion_username); + //TODO Find out how to keep session id in a spool or something - probably with the state.c and state.h stuff + //unsigned long long session_id = data_init_session_and_get_id(user, bastion_username); + //state_set_session_id(session_id); + state_set_session_id(0); return SSH_AUTH_SUCCESS; } else { - free(bastion_username); sdata->auth_attempts++; return SSH_AUTH_DENIED; } diff --git a/bastion/state.c b/bastion/state.c index 6784c0a..347e7ba 100644 --- a/bastion/state.c +++ b/bastion/state.c @@ -8,7 +8,7 @@ struct state { unsigned long long session_id; char * destination; - char * bastion_username; + const char * bastion_username; }; static struct state state = {0}; @@ -26,7 +26,7 @@ state_set_ssh_destination(const char * name) return 2; } state.destination = malloc(len+1); - strncpy(state.destination, name, len+1); + strcpy(state.destination, name); return 0; } @@ -42,13 +42,7 @@ state_set_bastion_username(const char * name) fprintf(stderr, "BUG found, attempting to overwrite state.bastion_username that has already been set\n"); return 1; } - size_t len = strnlen(name, MAX_USERNAME_LENGTH + 1); - if (len >= MAX_USERNAME_LENGTH + 1) { - fprintf(stderr, "Username too long, max length is %d.\n", MAX_USERNAME_LENGTH); - return 1; - } - state.bastion_username = malloc(len+1); - strncpy(state.bastion_username, name, len+1); + state.bastion_username = name; return 0; } diff --git a/common/config.c b/common/config.c index e8196ce..1c45e7e 100644 --- a/common/config.c +++ b/common/config.c @@ -2,6 +2,7 @@ #include <stdlib.h> #include "config.h" +#include "data_for_config.h" config_t * config = NULL; @@ -26,7 +27,7 @@ config_load(void) fprintf(stderr, "Configuration read error occured at %s:%d %s\n", config_error_file(config), config_error_line(config), config_error_text(config)); return 1; } - return 0; + return data_init(config); } int config_get_port(void) @@ -79,6 +80,7 @@ const char * config_get_session_recording_path(void) void config_clean(void) { if (config != NULL) { + data_clean(); config_destroy(config); free(config); config = NULL; diff --git a/common/config.h.in b/common/config.h.in index a1fe21b..95d43c9 100644 --- a/common/config.h.in +++ b/common/config.h.in @@ -5,18 +5,12 @@ #define CONFIG_PATH CONFIG_DIR "bastion.conf" #define MAX_HOSTNAME_LENGTH 64 -#define MAX_USERNAME_LENGTH 64 #define DEFAULT_PORT 2222 #define DEFAULT_DSAKEY_PATH "@CMAKE_INSTALL_PREFIX@/etc/bastion/ssh_host_dsa_key" #define DEFAULT_RSAKEY_PATH "@CMAKE_INSTALL_PREFIX@/etc/bastion/ssh_host_rsa_key" #define DEFAULT_ECDSAKEY_PATH "@CMAKE_INSTALL_PREFIX@/etc/bastion/ssh_host_ecdsa_key" -#define MYSQL_HOST "localhost" -#define MYSQL_USER "sshportal" -#define MYSQL_PASS "graou" -#define MYSQL_DB "sshportal" - #ifdef SESSION_RECORDING #define DEFAULT_SESSION_RECORDING_PATH "@CMAKE_INSTALL_PREFIX@/var/log/bastion/$d/$h/$u/$i.gz" #define SESSION_RECORDING_FILENAME_MAX_LEN 255 diff --git a/common/data.c b/common/data.c new file mode 100644 index 0000000..ab9710f --- /dev/null +++ b/common/data.c @@ -0,0 +1,276 @@ +#include <ctype.h> +#include <libconfig.h> +#include <libssh/libssh.h> +#include <uthash.h> + +#include "config.h" +#include "data_for_config.h" + +struct key { + const char *name; // key + ssh_key key; + UT_hash_handle hh; // makes this structure hashable +}; + +struct user { + const char *name; // key for hh_user + const char *pubkeystr; // key for hh_pubkey + UT_hash_handle hh_user, hh_pubkey; // makes this structure hashable, twice +}; + +struct host { + const char *name; + const char *address; + const char *user; + ssh_key key; + const char *pubkeystr; + UT_hash_handle hh; // makes this structure hashable +}; + +struct key * keys = NULL; +struct user * users = NULL; +struct user * pubkeys = NULL; +struct host *hosts = NULL; + +char // returns 0 if ok, something else otherwise +data_init(const config_t * config) +{ + config_setting_t *setting; + const int config_dir_len = strlen(CONFIG_DIR); + + // We load the ssh keys in the hastable + setting = config_lookup(config, "hostkeys"); + if(setting != NULL) { + int count = config_setting_length(setting); + for(int i = 0; i < count; ++i) { + config_setting_t *elt = config_setting_get_elem(setting, i); + const char *name, *key_path; + if (config_setting_lookup_string(elt, "name", &name) == CONFIG_FALSE) { + fprintf(stderr, "Invalid key entry with no name.\n"); + return 1; + } + struct key * tmp; + HASH_FIND_STR(keys, name, tmp); + if (tmp != NULL) { + fprintf(stderr, "Invalid key with duplicate name %s.\n", name); + return 1; + } + if (config_setting_lookup_string(elt, "path", &key_path) == CONFIG_FALSE) { + fprintf(stderr, "Invalid key entry %s with no path.\n", name); + return 1; + } + tmp = malloc(sizeof(struct key)); + tmp->name = name; + char * key_realpath = malloc(strlen(key_path) + config_dir_len + 1); + strcpy(key_realpath, CONFIG_DIR); + strcpy(key_realpath + config_dir_len, key_path); + switch(ssh_pki_import_privkey_file(key_realpath, NULL, NULL, NULL, &tmp->key)) { + case SSH_EOF: + fprintf(stderr, "Error importing ssh key from file %s : file doesn't exist or permission denied.\n", key_realpath); + free(key_realpath); + free(tmp); + return 1; + break; + case SSH_ERROR: + fprintf(stderr, "Error importing ssh key from file %s.\n", key_realpath); + free(key_realpath); + free(tmp); + return 1; + break; + case SSH_OK: + break; + } + HASH_ADD_KEYPTR(hh, keys, tmp->name, strlen(tmp->name), tmp); + free(key_realpath); + } + } + + // We load the users in the hastable + setting = config_lookup(config, "users"); + if(setting != NULL) { + int count = config_setting_length(setting); + for(int i = 0; i < count; ++i) { + config_setting_t *elt = config_setting_get_elem(setting, i); + const char *name, *pubkeystr; + if (config_setting_lookup_string(elt, "name", &name) == CONFIG_FALSE) { + fprintf(stderr, "Invalid user entry with no name.\n"); + return 1; + } + unsigned int name_len = strlen(name); + struct user * tmp; + HASH_FIND(hh_user, users, name, name_len, tmp); + if (tmp != NULL) { + fprintf(stderr, "Invalid user with duplicate name %s.\n", name); + return 1; + } + if (config_setting_lookup_string(elt, "public_key", &pubkeystr) == CONFIG_FALSE) { + fprintf(stderr, "Invalid user entry %s with no public_key.\n", name); + return 1; + } + // TODO support other key types + // And find a cleaner way to strip this key header before importing, and store a type flag + char * rsa = "ssh-rsa "; + pubkeystr += strlen(rsa); + unsigned int pubkeystr_len = strlen(pubkeystr); + for (unsigned int i = 0; i < pubkeystr_len; ++i) { + if (isspace(pubkeystr[i]) == 0) + continue; + fprintf(stderr, "Invalid trailing characters in public key for user %s :%s.\n", name, pubkeystr+i); + return 1; + } + HASH_FIND(hh_pubkey, pubkeys, pubkeystr, pubkeystr_len, tmp); + if (tmp != NULL) { + fprintf(stderr, "Invalid user %s with duplicate public_key with %s.\n", name, tmp->name); + return 1; + } + ssh_key tmpkey; + switch(ssh_pki_import_pubkey_base64(pubkeystr, SSH_KEYTYPE_RSA, &tmpkey)) { + case SSH_ERROR: + fprintf(stderr, "Error importing public key for user %s.\n", name); + return 1; + break; + case SSH_OK: + ssh_key_free(tmpkey); + break; + } + tmp = malloc(sizeof(struct user)); + tmp->name = name; + tmp->pubkeystr = pubkeystr; + HASH_ADD_KEYPTR(hh_user, users, tmp->name, name_len, tmp); + HASH_ADD_KEYPTR(hh_pubkey, pubkeys, tmp->pubkeystr, pubkeystr_len, tmp); + } + } + + // We load the hosts in the hastable + setting = config_lookup(config, "hosts"); + if(setting != NULL) { + int count = config_setting_length(setting); + for(int i = 0; i < count; ++i) { + config_setting_t *elt = config_setting_get_elem(setting, i); + const char *name, *address, *user, *hostkey, *pubkeystr; + if (config_setting_lookup_string(elt, "name", &name) == CONFIG_FALSE) { + fprintf(stderr, "Invalid host entry with no name.\n"); + return 1; + } + struct host * tmp; + HASH_FIND_STR(hosts, name, tmp); + if (tmp != NULL) { + fprintf(stderr, "Invalid host with duplicate name %s.\n", name); + return 1; + } + if (config_setting_lookup_string(elt, "address", &address) == CONFIG_FALSE) { + fprintf(stderr, "Invalid host entry %s with no address.\n", name); + return 1; + } + if (config_setting_lookup_string(elt, "user", &user) == CONFIG_FALSE) { + fprintf(stderr, "Invalid host entry %s with no user.\n", name); + return 1; + } + if (config_setting_lookup_string(elt, "hostkey", &hostkey) == CONFIG_FALSE) { + fprintf(stderr, "Invalid host entry %s with no ssh_key.\n", name); + return 1; + } + struct key * key; + HASH_FIND_STR(keys, hostkey, key); + if (key == NULL) { + fprintf(stderr, "Host key \"%s\" was not found for host \"%s\".\n", hostkey, name); + return 1; + } + if (config_setting_lookup_string(elt, "public_key", &pubkeystr) == CONFIG_FALSE) { + fprintf(stderr, "Invalid user entry %s with no public_key.\n", name); + return 1; + } + // TODO support other key types + // And find a cleaner way to strip this key header before importing, and store a type flag + char * ecdsa = "ssh-ed25519 "; + pubkeystr += strlen(ecdsa); + unsigned int pubkeystr_len = strlen(pubkeystr); + for (unsigned int i = 0; i < pubkeystr_len; ++i) { + if (isspace(pubkeystr[i]) == 0) + continue; + fprintf(stderr, "Invalid trailing characters in public key for user %s :%s.\n", name, pubkeystr+i); + return 1; + } + ssh_key tmpkey; + switch(ssh_pki_import_pubkey_base64(pubkeystr, SSH_KEYTYPE_ED25519, &tmpkey)) { + case SSH_ERROR: + fprintf(stderr, "Error importing public key for user %s.\n", name); + free(tmp); + return 1; + break; + case SSH_OK: + ssh_key_free(tmpkey); + break; + } + tmp = malloc(sizeof(struct host)); + tmp->name = name; + tmp->address = address; + tmp->user = user; + tmp->key = key->key; + tmp->pubkeystr = pubkeystr; + HASH_ADD_KEYPTR(hh, hosts, tmp->name, strlen(tmp->name), tmp); + } + } + + return 0; +} + +void data_clean(void) +{ + struct key *current_key, *tmp_key; + + HASH_ITER(hh, keys, current_key, tmp_key) { + HASH_DEL(keys, current_key); + ssh_key_free(current_key->key); + free(current_key); + } + + HASH_CLEAR(hh_user, users); + + struct user *current_pubkey, *tmp_pubkey; + HASH_ITER(hh_pubkey, pubkeys, current_pubkey, tmp_pubkey) { + HASH_DELETE(hh_pubkey, pubkeys, current_pubkey); + free(current_pubkey); + } + + struct host *current_host, *tmp_host; + HASH_ITER(hh, hosts, current_host, tmp_host) { + HASH_DEL(hosts, current_host); + free(current_host); + } +} + +const char * // returns NULL if no user found +data_get_username_from_pubkey(ssh_key pubkey) +{ + const char *username = NULL; + char *pubkeystr; + if (ssh_pki_export_pubkey_base64(pubkey, &pubkeystr) != SSH_OK) { + fprintf(stderr, "Got invalid public key from auth attempt, this shouldn't happen.\n"); + return NULL; + } + unsigned int pubkeystr_len = strlen(pubkeystr); + struct user *tmp; + HASH_FIND(hh_pubkey, pubkeys, pubkeystr, pubkeystr_len, tmp); + if (tmp != NULL) { + username = tmp->name; + } + free(pubkeystr); + return username; +} + +struct data_host_info * // returns NULL if no key found, this struct is to be freed from the calling code +data_get_host_info(const char * hostname) +{ + struct host * tmp; + HASH_FIND_STR(hosts, hostname, tmp); + if (tmp != NULL) { + struct data_host_info *info = malloc(sizeof(struct data_host_info)); + info->address = tmp->address; + info->username = tmp->user; + info->key = tmp->key; + info->pubkey = tmp->pubkeystr; + return info; + } + return NULL; +} diff --git a/common/data.h b/common/data.h new file mode 100644 index 0000000..1ede88b --- /dev/null +++ b/common/data.h @@ -0,0 +1,19 @@ +#ifndef COMMON_DATA_H_ +#define COMMON_DATA_H_ + +#include <libssh/server.h> + +void data_clean(void); + +struct data_host_info { + const char * address; + const char * username; + ssh_key key; + const char * pubkey; +}; + +const char * data_get_username_from_pubkey(ssh_key pubkey); +struct data_host_info * // returns NULL if no key found, this struct is to be freed from the calling code +data_get_host_info(const char * hostname); + +#endif diff --git a/common/data_for_config.h b/common/data_for_config.h new file mode 100644 index 0000000..acf0cac --- /dev/null +++ b/common/data_for_config.h @@ -0,0 +1,10 @@ +#ifndef COMMON_DATA_FOR_CONFIG_H_ +#define COMMON_DATA_FOR_CONFIG_H_ + +#include <libconfig.h> +#include "data.h" + +char // returns 0 if ok, something else otherwise +data_init(const config_t * config); + +#endif diff --git a/common/mysql.c b/common/mysql.c deleted file mode 100644 index 83a7930..0000000 --- a/common/mysql.c +++ /dev/null @@ -1,188 +0,0 @@ -#include <libssh/server.h> -#include <mysql/mysql.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include "config.h" -#include "mysql.h" - -static MYSQL *db; - -char // returns 0 if ok, greater than 0 otherwise -db_init(void) -{ - printf("MySQL client version: %s\n", mysql_get_client_info()); - db = mysql_init(NULL); - if (db == NULL) { - fprintf(stderr, "%s\n", mysql_error(db)); - return 1; - } - if (mysql_real_connect(db, MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB, 0, NULL, 0) == NULL) { - fprintf(stderr, "%s\n", mysql_error(db)); - mysql_close(db); - return 1; - } - return 0; -} - -void db_clean(void) -{ - mysql_close(db); - db = NULL; -} - -char * // returns NULL if no user found, this char * is to be freed from the calling code -db_get_username_from_pubkey(ssh_key pubkey) -{ - int res = mysql_query(db, "SELECT name, authorized_key FROM users, user_keys WHERE users.id = user_keys.user_id"); - if (res != 0) { - fprintf(stderr, "WARNING: Couldn't get usernames from database.\n"); - return NULL; - } - MYSQL_RES *result = mysql_store_result(db); - if (result == NULL) { - fprintf(stderr, "FATAL: Couldn't retrieve public keys from database.\n"); - return NULL; - } - - MYSQL_ROW row; - while ((row = mysql_fetch_row(result))) { - char * rsa = "ssh-rsa "; - if (strncmp (row[1], rsa, strlen(rsa)) != 0) { - fprintf(stderr, "Unsupported public key type for user %s : %s\n", row[0], row[1]); - } else { - ssh_key tmp_key; - if (ssh_pki_import_pubkey_base64(row[1] + strlen(rsa), SSH_KEYTYPE_RSA, &tmp_key) != SSH_OK) { - fprintf(stderr, "Error importing public key for user %s : %s\n", row[0], row[1]); - } else if (!ssh_key_cmp(pubkey, tmp_key, SSH_KEY_CMP_PUBLIC)) { - size_t len = strlen(row[0]); - char * username = malloc(len+1); - strcpy(username, row[0]); - ssh_key_free(tmp_key); - mysql_free_result(result); - return username; - } else { - ssh_key_free(tmp_key); - } - } - } - - fprintf(stderr, "ERROR: Didn't find public key in database.\n"); - mysql_free_result(result); - return NULL; -} - -struct db_host_info * // returns NULL if no key found, this char * is to be freed from the calling code -db_get_host_info(const char * hostname) -{ - char buff[255]; - sprintf(buff, "SELECT priv_key, url, host_key FROM ssh_keys, hosts WHERE ssh_keys.id = hosts.ssh_key_id and hosts.name = \"%s\"", hostname); - int res = mysql_query(db, buff); - if (res != 0) { - fprintf(stderr, "WARNING: Couldn't query db for server infos for host %s\n", hostname); - return NULL; - } - MYSQL_RES *result = mysql_store_result(db); - if (result == NULL) { - fprintf(stderr, "FATAL: Couldn't retrieve server infos for %s from database.\n", hostname); - return NULL; - } - - MYSQL_ROW row = mysql_fetch_row(result); - if (row == NULL) { - fprintf(stderr, "FATAL: Couldn't retrieve server db results for %s from database.\n", hostname); - mysql_free_result(result); - return NULL; - } - - struct db_host_info * info = malloc(sizeof(struct db_host_info)); - memset(info, 0, sizeof(struct db_host_info)); - - size_t len = strlen(row[0]); - info->privkeytxt = malloc(len+1); - strcpy(info->privkeytxt, row[0]); - - if (strncmp(row[1], "ssh://", 6) != 0) { - fprintf(stderr, "FATAL: invalid host url %s\n", row[1]); - return NULL; - } - size_t at_pos = 0; - char done = 0; - for(size_t i = 6; !done; ++i) { - switch(*(row[1]+i)) { - case '@': - info->username = malloc(i-6+1); - strncpy(info->username, row[1]+6, i-6); - info->username[i-6] = '\0'; - at_pos = i; - break; - case '\0': - info->address = malloc(i-at_pos); - strncpy(info->address, row[1]+at_pos+1, i-at_pos-1); - info->address[i-at_pos-1] = '\0'; - done = 1; - break; - } - if (i > MAX_HOSTNAME_LENGTH + MAX_USERNAME_LENGTH + 6 + 1) { - fprintf(stderr, "FATAL: Couldn't parse host url for host %s, too long.\n", hostname); - if (info->username != NULL) - free(info->username); - return NULL; - } - } - - len = strlen(row[2]); - info->hostkeyhash = malloc(len+1); - strcpy(info->hostkeyhash, row[2]); - - mysql_free_result(result); - return info; -} - -void db_set_host_publickey_hash(const char * hostname, const char * hash) -{ - char buff[255]; - sprintf(buff, "UPDATE ssh_keys, hosts SET host_key = \"%s\" WHERE ssh_keys.id = hosts.ssh_key_id and hosts.name = \"%s\"", hash, hostname); - int res = mysql_query(db, buff); - if (res != 0) { - fprintf(stderr, "WARNING: Couldn't set host key for host %s: %s\n", hostname, hash); - return; - } - res = mysql_commit(db); - if (res != 0) { - fprintf(stderr, "WARNING: Couldn't commit after setting host key for host %s: %s\n", hostname, hash); - } -} - -unsigned long long // returns 0 on error, or the session_id -db_init_session_and_get_id(const char * hostname, const char * username) -{ - char buff[255]; - sprintf(buff, "INSERT INTO sessions (created_at, status, user_id, host_id) SELECT NOW(), \"opened\", users.id, hosts.id from users, hosts WHERE users.name = \"%s\" and hosts.name = \"%s\"", username, hostname); - int res = mysql_query(db, buff); - if (res != 0) { - fprintf(stderr, "FATAL: Couldn't insert new session in database for %s to %s\n", username, hostname); - return 0; - } - unsigned long long id = mysql_insert_id(db); - if (id == 0) { - fprintf(stderr, "FATAL: Didn't get proper mysql last insert id after inserting new session for %s to %s\n", username, hostname); - return 0; - } - res = mysql_commit(db); - if (res != 0) { - fprintf(stderr, "FATAL: Couldn't commit after inserting session for %s to %s\n", username, hostname); - return 0; - } - return id; -} - -void db_free_host_info(struct db_host_info * info) -{ - free(info->privkeytxt); - free(info->address); - free(info->username); - free(info->hostkeyhash); - free(info); -} diff --git a/common/mysql.h b/common/mysql.h deleted file mode 100644 index ac9a360..0000000 --- a/common/mysql.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef COMMON_MYSQL_H_ -#define COMMON_MYSQL_H_ - -struct db_host_info { - char * privkeytxt; - char * address; - char * username; - char * hostkeyhash; -}; - -char db_init(void); -void db_clean(void); -char * // returns NULL if no user found, this char * is to be freed from the calling code -db_get_username_from_pubkey(ssh_key pubkey); -struct db_host_info * // returns NULL if no key found, this char * is to be freed from the calling code -db_get_host_info(const char * hostname); -void db_set_host_publickey_hash(const char * hostname, const char * hash); -unsigned long long // returns 0 on error, or the session_id -db_init_session_and_get_id(const char * hostname, const char * username); -void db_free_host_info(struct db_host_info * info); - -#endif |