Removed mysql dependency and fetch all configuration from configuration file.
This commit is contained in:
parent
3b22502cbd
commit
455172075b
16 changed files with 362 additions and 280 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -1,6 +1,3 @@
|
||||||
bastion
|
|
||||||
build
|
build
|
||||||
id_rsa
|
stuff
|
||||||
ssh_host_*_key
|
|
||||||
test_client.c
|
|
||||||
*.sw[a-p]
|
*.sw[a-p]
|
||||||
|
|
|
@ -29,7 +29,6 @@ This project has only two hard dependencies :
|
||||||
The following are optional dependencies :
|
The following are optional dependencies :
|
||||||
- the libtty from https://github.com/kilobyte/termrec which allows session recording.
|
- 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.
|
- 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
|
## Manual Installation
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ target_link_libraries(bastion common)
|
||||||
if (${SESSION_RECORDING})
|
if (${SESSION_RECORDING})
|
||||||
target_link_libraries(bastion libtty)
|
target_link_libraries(bastion libtty)
|
||||||
endif()
|
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)
|
install(TARGETS bastion DESTINATION bin)
|
||||||
|
|
||||||
|
|
25
bastion/bastion.conf.example.in
Normal file
25
bastion/bastion.conf.example.in
Normal file
|
@ -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" }
|
||||||
|
);
|
|
@ -3,7 +3,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/mysql.h"
|
#include "common/data.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#ifdef SESSION_RECORDING
|
#ifdef SESSION_RECORDING
|
||||||
#include "recording.h"
|
#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;
|
cdata->client_channel_cb = NULL;
|
||||||
|
|
||||||
/* First we try to add the private key that the server will accept */
|
/* 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)
|
if (info == NULL)
|
||||||
goto host_info_clean;
|
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 */
|
/* We try to connect to the remote server */
|
||||||
printf("Connecting to %s\n", hostname);
|
printf("Connecting to %s\n", hostname);
|
||||||
cdata->my_session = ssh_new();
|
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 */
|
/* We now validate the remote server's public key */
|
||||||
ssh_key server_pub_key = NULL;
|
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) {
|
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));
|
fprintf(stderr, "Error getting server publickey: %s\n", ssh_get_error(cdata->my_session));
|
||||||
goto pubkey_clean;
|
goto pubkey_clean;
|
||||||
}
|
}
|
||||||
if (ssh_get_publickey_hash(server_pub_key, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen) != SSH_OK) {
|
char *pubkeystr = NULL;
|
||||||
fprintf(stderr, "Error getting publickey hash: %s\n", ssh_get_error(cdata->my_session));
|
if (ssh_pki_export_pubkey_base64(server_pub_key, &pubkeystr) != SSH_OK) {
|
||||||
goto pubkey_hash_clean;
|
fprintf(stderr, "Got invalid public key from server attempt, this shouldn't happen.\n");
|
||||||
|
goto pubkey_clean;
|
||||||
}
|
}
|
||||||
hexa = ssh_get_hexa(hash, hlen);
|
if (strcmp(info->pubkey, pubkeystr) != 0) {
|
||||||
if (strlen(info->hostkeyhash) > 0) {
|
fprintf(stderr, "Error : public key of server %s doesn't match.\n", hostname);
|
||||||
if (strcmp(hexa, info->hostkeyhash) != 0) {
|
fprintf(stderr, "got : %s\n", pubkeystr);
|
||||||
fprintf(stderr, "Error invalid host key for %s\n", hostname);
|
fprintf(stderr, "exp : %s\n", info->pubkey);
|
||||||
goto pubkey_hexa_clean;
|
goto pubkey_clean;
|
||||||
}
|
}
|
||||||
} else {
|
free(pubkeystr);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
ssh_string_free_char(hexa);
|
|
||||||
ssh_clean_pubkey_hash(&hash);
|
|
||||||
ssh_key_free(server_pub_key);
|
ssh_key_free(server_pub_key);
|
||||||
|
|
||||||
/* With the server checked, we can authenticate */
|
/* 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");
|
printf("Authentication success\n");
|
||||||
} else {
|
} else {
|
||||||
printf("Error private key was rejected\n");
|
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
|
#endif
|
||||||
|
|
||||||
ssh_key_free(privkey);
|
free(info);
|
||||||
db_free_host_info(info);
|
|
||||||
return cdata;
|
return cdata;
|
||||||
|
|
||||||
channel_clean:
|
channel_clean:
|
||||||
ssh_channel_free(cdata->my_channel);
|
ssh_channel_free(cdata->my_channel);
|
||||||
goto session_clean;
|
goto session_clean;
|
||||||
pubkey_hexa_clean:
|
|
||||||
ssh_string_free_char(hexa);
|
|
||||||
pubkey_hash_clean:
|
|
||||||
ssh_clean_pubkey_hash(&hash);
|
|
||||||
pubkey_clean:
|
pubkey_clean:
|
||||||
|
free(pubkeystr);
|
||||||
ssh_key_free(server_pub_key);
|
ssh_key_free(server_pub_key);
|
||||||
session_clean:
|
session_clean:
|
||||||
ssh_disconnect(cdata->my_session);
|
ssh_disconnect(cdata->my_session);
|
||||||
ssh_free(cdata->my_session);
|
ssh_free(cdata->my_session);
|
||||||
db_free_host_info(info);
|
free(info);
|
||||||
privkey_clean:
|
|
||||||
ssh_key_free(privkey);
|
|
||||||
host_info_clean:
|
host_info_clean:
|
||||||
free(cdata);
|
free(cdata);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/mysql.h"
|
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
|
||||||
|
@ -28,7 +27,6 @@ __attribute__((noreturn)) static void sigint_handler(int signo)
|
||||||
state_clean();
|
state_clean();
|
||||||
config_clean();
|
config_clean();
|
||||||
ssh_finalize();
|
ssh_finalize();
|
||||||
db_clean();
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,6 +143,5 @@ int main()
|
||||||
ssh_bind_free(sshbind);
|
ssh_bind_free(sshbind);
|
||||||
config_clean();
|
config_clean();
|
||||||
ssh_finalize();
|
ssh_finalize();
|
||||||
db_clean();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "common/mysql.h"
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
#include "state.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_callbacks_init(&channel_cb);
|
||||||
ssh_set_channel_callbacks(my_channel, &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 {
|
do {
|
||||||
/* Poll the main event which takes care of the sessions and channels */
|
/* Poll the main event which takes care of the sessions and channels */
|
||||||
if (ssh_event_dopoll(event, -1) == SSH_ERROR) {
|
if (ssh_event_dopoll(event, -1) == SSH_ERROR) {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/mysql.h"
|
#include "common/data.h"
|
||||||
#include "proxy.h"
|
#include "proxy.h"
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "state.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
|
// 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) {
|
if (bastion_username != NULL) {
|
||||||
sdata->authenticated = 1;
|
sdata->authenticated = 1;
|
||||||
if (state_set_ssh_destination(user) != 0)
|
if (state_set_ssh_destination(user) != 0)
|
||||||
return SSH_ERROR;
|
return SSH_ERROR;
|
||||||
// TODO check access rights and host configs
|
// TODO check access rights and host configs
|
||||||
state_set_bastion_username(bastion_username);
|
state_set_bastion_username(bastion_username);
|
||||||
unsigned long long session_id = db_init_session_and_get_id(user, bastion_username);
|
//TODO Find out how to keep session id in a spool or something - probably with the state.c and state.h stuff
|
||||||
state_set_session_id(session_id);
|
//unsigned long long session_id = data_init_session_and_get_id(user, bastion_username);
|
||||||
free(bastion_username);
|
//state_set_session_id(session_id);
|
||||||
|
state_set_session_id(0);
|
||||||
return SSH_AUTH_SUCCESS;
|
return SSH_AUTH_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
free(bastion_username);
|
|
||||||
sdata->auth_attempts++;
|
sdata->auth_attempts++;
|
||||||
return SSH_AUTH_DENIED;
|
return SSH_AUTH_DENIED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
struct state {
|
struct state {
|
||||||
unsigned long long session_id;
|
unsigned long long session_id;
|
||||||
char * destination;
|
char * destination;
|
||||||
char * bastion_username;
|
const char * bastion_username;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct state state = {0};
|
static struct state state = {0};
|
||||||
|
@ -26,7 +26,7 @@ state_set_ssh_destination(const char * name)
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
state.destination = malloc(len+1);
|
state.destination = malloc(len+1);
|
||||||
strncpy(state.destination, name, len+1);
|
strcpy(state.destination, name);
|
||||||
return 0;
|
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");
|
fprintf(stderr, "BUG found, attempting to overwrite state.bastion_username that has already been set\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
size_t len = strnlen(name, MAX_USERNAME_LENGTH + 1);
|
state.bastion_username = name;
|
||||||
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);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "data_for_config.h"
|
||||||
|
|
||||||
config_t * config = NULL;
|
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));
|
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 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return data_init(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
int config_get_port(void)
|
int config_get_port(void)
|
||||||
|
@ -79,6 +80,7 @@ const char * config_get_session_recording_path(void)
|
||||||
void config_clean(void)
|
void config_clean(void)
|
||||||
{
|
{
|
||||||
if (config != NULL) {
|
if (config != NULL) {
|
||||||
|
data_clean();
|
||||||
config_destroy(config);
|
config_destroy(config);
|
||||||
free(config);
|
free(config);
|
||||||
config = NULL;
|
config = NULL;
|
||||||
|
|
|
@ -5,18 +5,12 @@
|
||||||
#define CONFIG_PATH CONFIG_DIR "bastion.conf"
|
#define CONFIG_PATH CONFIG_DIR "bastion.conf"
|
||||||
|
|
||||||
#define MAX_HOSTNAME_LENGTH 64
|
#define MAX_HOSTNAME_LENGTH 64
|
||||||
#define MAX_USERNAME_LENGTH 64
|
|
||||||
|
|
||||||
#define DEFAULT_PORT 2222
|
#define DEFAULT_PORT 2222
|
||||||
#define DEFAULT_DSAKEY_PATH "@CMAKE_INSTALL_PREFIX@/etc/bastion/ssh_host_dsa_key"
|
#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_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 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
|
#ifdef SESSION_RECORDING
|
||||||
#define DEFAULT_SESSION_RECORDING_PATH "@CMAKE_INSTALL_PREFIX@/var/log/bastion/$d/$h/$u/$i.gz"
|
#define DEFAULT_SESSION_RECORDING_PATH "@CMAKE_INSTALL_PREFIX@/var/log/bastion/$d/$h/$u/$i.gz"
|
||||||
#define SESSION_RECORDING_FILENAME_MAX_LEN 255
|
#define SESSION_RECORDING_FILENAME_MAX_LEN 255
|
||||||
|
|
276
common/data.c
Normal file
276
common/data.c
Normal file
|
@ -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;
|
||||||
|
}
|
19
common/data.h
Normal file
19
common/data.h
Normal file
|
@ -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
|
10
common/data_for_config.h
Normal file
10
common/data_for_config.h
Normal file
|
@ -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
|
188
common/mysql.c
188
common/mysql.c
|
@ -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);
|
|
||||||
}
|
|
|
@ -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
|
|
Reference in a new issue