/* * Rangitaki Sync Library * * A program for downloading and uploading files over ssh. * Written for the Rangitaki blogging engine. * * Proudly written in C and with use of libssh (libssh.org) * * Version: 0.1 * * COPYRIGHT (c) 2015 - 2016 The Rangitaki Project * COPYRIGHT (c) 2015 - 2016 Marcel Kapfer (mmk2410) * * * MIT License * */ #include #include #include #include #include #include #include #include "rangitaki-sync.h" #include "dbg.h" #define MAX_FILE_LENGTH 255 char * combine(char *begin, char *end){ char *result; check(begin != NULL, "Begin can't be NULL."); check(end != NULL, "End can't be NULL."); size_t begin_length = strnlen(begin, MAX_FILE_LENGTH - 1); size_t end_length = strnlen(end, MAX_FILE_LENGTH - 1); assert(begin_length < MAX_FILE_LENGTH && "Got the begin too long."); assert(end_length < MAX_FILE_LENGTH && "Got the end to long."); result = malloc(begin_length + end_length + 1); check(result != NULL, "Failed to define result"); strncpy(result, begin, MAX_FILE_LENGTH); check(result != NULL, "Failed to copy the string"); strncat(result, end, MAX_FILE_LENGTH * 2); int rc = strncmp(result, begin, MAX_FILE_LENGTH * 2); check(rc <= 0, "Failed to concat string."); return result; error: if (result) free(result); return NULL; } char * getFilename(char *input) { check(input != NULL, "Input is NULL."); char* string; string = strdup(input); int buffer[100]; int i = 0; int j = 0; for(i = 0; i <= strlen(string); i++) { if(string[i] == '/') { buffer[j] = i; j++; } } j--; int size = strlen(string) - buffer[j]; // Includes the \0 char *result = malloc(size); strcpy(result, &string[buffer[j]+1]); result[size] = '\0'; free(string); return result; error: return NULL; } void ssh_initialize(const char *host, const char *user, const char *password, const char *remote_dir, const char *local_dir, const unsigned int port, ssh_data *data) { data->host = strdup(host); data->user = strdup(user); data->password = strdup(password); data->remote_dir = strdup(remote_dir); data->local_dir = strdup(local_dir); data->port = port; data->verbosity = SSH_LOG_PROTOCOL; } int ssh_server_auth(ssh_session ssh) { char *hexa; int state; unsigned char *hash = NULL; size_t hlen; ssh_key srv_pubkey; int rc; state = ssh_is_server_known(ssh); rc = ssh_get_publickey(ssh, &srv_pubkey); check(rc >= 0, "Couldn't not authenticate the server."); rc = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen); ssh_key_free(srv_pubkey); check(rc >= 0, "Couldn't not authenticate the server."); switch (state) { case SSH_SERVER_KNOWN_OK: log_info("Found host key"); break; case SSH_SERVER_KNOWN_CHANGED: log_err("The host key of the server changed. Aborting connection."); ssh_print_hexa("Public key hash", hash, hlen); ssh_clean_pubkey_hash(&hash); return -1; case SSH_SERVER_FOUND_OTHER: log_err("The host key for this server was not found but an other. Aborting connection."); ssh_clean_pubkey_hash(&hash); return -1; case SSH_SERVER_FILE_NOT_FOUND: log_warn("Couldn't find known hosts file."); case SSH_SERVER_NOT_KNOWN: hexa = ssh_get_hexa(hash, hlen); log_info("Adding %s to known hosts.", hexa); free(hexa); if (ssh_write_knownhost(ssh) < 0) { log_err("Error adding server to known hosts file: %s", strerror(errno)); ssh_clean_pubkey_hash(&hash); return -1; } break; default: log_err("%s", ssh_get_error(ssh)); ssh_clean_pubkey_hash(&hash); return -1; } ssh_clean_pubkey_hash(&hash); return 0; error: ssh_clean_pubkey_hash(&hash); return -1; } ssh_session ssh_open(ssh_data *data) { int rc = 0; ssh_session ssh = ssh_new(); if(ssh == NULL) { log_err("Failed th create ssh session."); return NULL; } if(ssh_options_set(ssh, SSH_OPTIONS_HOST, data->host) < 0) { ssh_free(ssh); log_err("Failed to set host."); return NULL; } if(ssh_options_set(ssh, SSH_OPTIONS_USER, data->user) < 0) { ssh_free(ssh); log_err("Failed to set user."); return NULL; } if(ssh_options_set(ssh, SSH_OPTIONS_PORT, &data->port)) { ssh_free(ssh); log_err("Failed to set port."); return NULL; } if(ssh_options_set(ssh, SSH_OPTIONS_LOG_VERBOSITY, &data->verbosity)) { ssh_free(ssh); log_err("Failed to set verbosity."); return NULL; } if(ssh_connect(ssh)) { log_err("Connection failed: %s.", ssh_get_error(ssh)); ssh_disconnect(ssh); ssh_free(ssh); return NULL; } if(ssh_server_auth < 0) { log_err("Couldn't authenticate the server."); ssh_disconnect(ssh); ssh_free(ssh); return NULL; } rc = ssh_userauth_password(ssh, data->user, data->password); if(rc == SSH_AUTH_SUCCESS) { return ssh; } else if(rc == SSH_AUTH_DENIED) { log_err("Authentication failed."); } else { log_err("Error while authenticating"); } return NULL; } int ssh_close(ssh_session ssh) { ssh_disconnect(ssh); ssh_free(ssh); return 0; } ssh_scp scp_open(ssh_session ssh, ssh_data *data, int mode) { int rc = 0; ssh_scp scp = ssh_scp_new(ssh, mode, data->remote_dir); check(scp != NULL, "Failed to create the scp session: %s", ssh_get_error(ssh)); rc = ssh_scp_init(scp); if(rc == -1) { log_err("Failed to initialize the scp connection."); goto error; } check(rc == SSH_OK, "Failed to initialize the scp session: %s", ssh_get_error(ssh)); return scp; error: if(scp) ssh_scp_free(scp); return NULL; } int scp_close(ssh_scp scp) { ssh_scp_close(scp); ssh_scp_free(scp); return 0; } int scp_download(ssh_session ssh, ssh_scp scp, ssh_data *data) { int rc; int rv; int size, mode; char buffer[16384]; char *local_dir = strdup(data->local_dir); do { rc = ssh_scp_pull_request(scp); switch (rc) { case SSH_SCP_REQUEST_NEWFILE: size = ssh_scp_request_get_size(scp); mode = ssh_scp_request_get_permissions(scp); char *filename = strdup(ssh_scp_request_get_filename(scp)); check(filename != NULL, "Couldn't get the filename."); printf("DOWNLOAD: %s %d %d\n", filename, size, mode); char *dir = combine(local_dir, filename); FILE *output = fopen(dir, "w+"); check(output != NULL, "FILE couldn't be created"); ssh_scp_accept_request(scp); rc = ssh_scp_read(scp, buffer, sizeof(buffer)); check(rc != SSH_ERROR, "Error downloading the file. %s", ssh_get_error(ssh)); rv = fwrite(buffer, size, 1, output); check(rv != 0, "Failed to write file"); rv = fclose(output); free(dir); free(filename); check(rv == 0, "Failed to close FILE"); printf("Done.\n"); break; case SSH_ERROR: log_err("Error: %s", ssh_get_error(ssh)); goto error; case SSH_SCP_REQUEST_WARNING: log_warn("Warning: %s", ssh_scp_request_get_warning(scp)); break; case SSH_SCP_REQUEST_NEWDIR: filename = strdup(ssh_scp_request_get_filename(scp)); mode = ssh_scp_request_get_permissions(scp); printf("DOWNLOAD: %s %d\n", filename, mode); ssh_scp_accept_request(scp); break; case SSH_SCP_REQUEST_ENDDIR: break; case SSH_SCP_REQUEST_EOF: log_info("DONE"); return 0; } } while (1); error: return -1; } int scp_upload(ssh_session ssh, ssh_scp scp, ssh_data *data) { int rc = 0; FILE *fisz = fopen(data->local_dir, "rb"); fseek(fisz, 0L, SEEK_END); int size = ftell(fisz); fclose(fisz); rc = ssh_scp_push_file64(scp, getFilename(data->local_dir), size, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); check(rc == SSH_OK, "Failed to create file: %s", ssh_get_error(ssh)); char *buffer = malloc(size + 100); FILE *file = fopen(data->local_dir, "r"); check(file != NULL, "Error opening local file."); rc = 0; rc = fread(buffer, size, 1, file); rc = ssh_scp_write(scp, buffer, size); check(rc == SSH_OK, "Error while writing remote file: %s", ssh_get_error(ssh)); return 0; error: return -1; }