This is a start for a sftp module.

This commit is contained in:
Andreas Schneider 2008-08-06 18:16:54 +02:00
parent 05253be461
commit ecf1027a54
3 changed files with 693 additions and 0 deletions

View File

@ -0,0 +1,76 @@
# - Try to find LIBSSH
# Once done this will define
#
# LIBSSH_FOUND - system has LIBSSH
# LIBSSH_INCLUDE_DIRS - the LIBSSH include directory
# LIBSSH_LIBRARIES - Link these to use LIBSSH
# LIBSSH_DEFINITIONS - Compiler switches required for using LIBSSH
#
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
#
# Redistribution and use is allowed according to the terms of the New
# BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
if (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS)
# in cache already
set(LIBSSH_FOUND TRUE)
else (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS)
find_path(LIBSSH_INCLUDE_DIR
NAMES
libssh/libssh.h
PATHS
/usr/include
/usr/local/include
/opt/local/include
/sw/include
)
mark_as_advanced(LIBSSH_INCLUDE_DIR)
find_library(SSH_LIBRARY
NAMES
ssh
PATHS
/usr/lib
/usr/local/lib
/opt/local/lib
/sw/lib
)
mark_as_advanced(SSH_LIBRARY)
if (SSH_LIBRARY)
set(SSH_FOUND TRUE)
endif (SSH_LIBRARY)
set(LIBSSH_INCLUDE_DIRS
${LIBSSH_INCLUDE_DIR}
)
if (SSH_FOUND)
set(LIBSSH_LIBRARIES
${LIBSSH_LIBRARIES}
${SSH_LIBRARY}
)
endif (SSH_FOUND)
if (LIBSSH_INCLUDE_DIRS AND LIBSSH_LIBRARIES)
set(LIBSSH_FOUND TRUE)
endif (LIBSSH_INCLUDE_DIRS AND LIBSSH_LIBRARIES)
if (LIBSSH_FOUND)
if (NOT LIBSSH_FIND_QUIETLY)
message(STATUS "Found LIBSSH: ${LIBSSH_LIBRARIES}")
endif (NOT LIBSSH_FIND_QUIETLY)
else (LIBSSH_FOUND)
if (LIBSSH_FIND_REQUIRED)
message(FATAL_ERROR "Could not find LIBSSH")
endif (LIBSSH_FIND_REQUIRED)
endif (LIBSSH_FOUND)
# show the LIBSSH_INCLUDE_DIRS and LIBSSH_LIBRARIES variables only in the advanced view
mark_as_advanced(LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES)
endif (LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS)

View File

@ -1,6 +1,7 @@
project(modules)
find_package(Libsmbclient REQUIRED)
find_package(LIBSSH REQUIRED)
set(MODULES_PUBLIC_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
@ -17,6 +18,10 @@ set(SMB_PLUGIN
csync_smb
)
set(SFTP_PLUGIN
csync_sftp
)
include_directories(
${MODULES_PUBLIC_INCLUDE_DIRS}
${MODULES_PRIVATE_INCLUDE_DIRS}
@ -27,10 +32,19 @@ target_link_libraries(${SMB_PLUGIN} ${LIBSMBCLIENT_LIBRARIES} ${CSTDLIB_LIBRARY}
macro_add_plugin(csync_dummy csync_dummy.c)
macro_add_plugin(${SFTP_PLUGIN} csync_sftp.c)
target_link_libraries(${SFTP_PLUGIN} ${LIBSSH_LIBRARIES} ${CSTDLIB_LIBRARY})
INSTALL(
TARGETS
${SMB_PLUGIN}
${SFTP_PLUGIN}
DESTINATION
${PLUGIN_INSTALL_DIR}
)
# create test file as bad plugin for the vio testcase
file(WRITE
${CMAKE_CURRENT_BINARY_DIR}/csync_bad.so
"bad plugin"
)

603
modules/csync_sftp.c Normal file
View File

@ -0,0 +1,603 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2008 by Andreas Schneider <mail@cynapses.org>
*
* This program is free software = NULL, you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation = NULL, either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY = NULL, without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program = NULL, if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* vim: ts=2 sw=2 et cindent
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libssh/sftp.h>
#include "c_lib.h"
#include "vio/csync_vio_module.h"
#include "vio/csync_vio_file_stat.h"
#ifdef NDEBUG
#define DEBUG_SFTP(x)
#else
#define DEBUG_SFTP(x) printf x
#endif
int connected;
SSH_SESSION *ssh_session;
SFTP_SESSION *sftp_session;
/* parse_uri */
static const char *uri_prefix = "sftp:";
static int parse_uri(const char *uri, char **user, char **passwd,
char **hostname, char **port, char **path) {
const char *p = NULL;
const char *r = NULL;
const char *q = NULL;
char *s = NULL;
size_t len = 0;
/* Ensure these returns are at least valid pointers. */
*hostname = c_strdup("");
*port = c_strdup("");
*path = c_strdup("");
*user = c_strdup("");
*passwd = c_strdup("");
if (*hostname == NULL || *port == NULL || *path == NULL ||
*user == NULL || *passwd == NULL) {
return -1;
}
s = c_strdup(uri);
if (s == NULL) {
return -1;
}
len = strlen(uri_prefix);
/* check if we have the right prefix */
if (strncmp(s, uri_prefix, len) || (s[len] != '/' && s[len] != '\0')) {
goto err;
}
p = s + len;
if (strncmp(p, "//", 2) == 2) {
DEBUG_SFTP(("Invalid path (does not begin with sftp://\n"));
goto err;
}
/* Skip the double slash */
p += 2;
if (*p == '\0') {
goto decoding;
}
/* check that '@' occurs before '/', if '/' exists at all */
q = strchr(p, '@');
r = strchr(p, '/');
if (q && (!r || q < r)) {
char *userinfo = NULL;
const char *u;
userinfo = c_strndup(p, q - p);
if (userinfo == NULL) {
goto err;
}
u = strchr(userinfo, ':');
if (u) {
SAFE_FREE(*user);
*user = c_strndup(userinfo, u - userinfo);
if (*user == NULL) {
SAFE_FREE(userinfo);
goto err;
}
SAFE_FREE(*passwd);
*passwd = c_strdup(u + 1);
if (*passwd == NULL) {
SAFE_FREE(userinfo);
goto err;
}
} else {
SAFE_FREE(*user);
*user = c_strdup(userinfo);
if (*user == NULL) {
SAFE_FREE(userinfo);
goto err;
}
}
len = strlen(userinfo) + 1;
SAFE_FREE(userinfo);
p += len;
}
DEBUG_SFTP(("user: %s\n", *user));
DEBUG_SFTP(("passwd: %s\n", *passwd));
if (*p == '\0') {
goto decoding;
}
/* check if we have a path */
r = strchr(p, '/');
if (r) {
char *h = NULL;
h = c_strndup(p, r - p);
if (h == NULL) {
goto err;
}
/* look for port */
q = strchr(h, ':');
if (q) {
SAFE_FREE(*port);
*port = c_strdup(q + 1);
if (*port == NULL) {
SAFE_FREE(h);
goto err;
}
SAFE_FREE(*hostname);
*hostname = c_strndup(h, q - h);
if (*hostname == NULL) {
SAFE_FREE(h);
goto err;
}
} else {
SAFE_FREE(*hostname);
*hostname = c_strdup(h);
if (*hostname == NULL) {
SAFE_FREE(h);
goto err;
}
}
len = strlen(h) + 1;
SAFE_FREE(h);
} else {
SAFE_FREE(*hostname);
*hostname = c_strdup(p);
if (*hostname == NULL) {
goto err;
}
len = strlen(p);
}
DEBUG_SFTP(("hostname: %s\n", *hostname));
DEBUG_SFTP(("port: %s\n", *port));
p += len;
if (*p == '\0') {
goto decoding;
}
SAFE_FREE(*path);
*path = c_strdup(p);
if (*path == NULL) {
goto err;
}
if (**path) {
char *d = NULL;
if (asprintf(&d, "/%s", *path) < 0) {
return -1;
}
SAFE_FREE(*path);
*path = d;
DEBUG_SFTP(("path: %s\n", *path));
}
goto decoding;
err:
SAFE_FREE(s);
return -1;
decoding:
return 0;
}
static int _sftp_connect(const char *uri) {
SSH_OPTIONS *options = NULL;
char *user = NULL;
char *passwd = NULL;
char *host = NULL;
char *port = NULL;
char *path = NULL;
unsigned char hash[MD5_DIGEST_LEN];
int rc = -1;
int auth = SSH_AUTH_ERROR;
int state = SSH_SERVER_ERROR;
if (connected) {
return 0;
}
rc = parse_uri(uri, &user, &passwd, &host, &port, &path);
if (rc < 0) {
goto out;
}
options = ssh_options_new();
if (options == NULL) {
rc = -1;
goto out;
}
/* set the option to connect to the server */
ssh_options_set_host(options, host);
if (*port) {
ssh_options_set_port(options, atoi(port));
}
if (*user) {
ssh_options_set_username(options, user);
}
/* connect to the server */
ssh_session = ssh_new();
ssh_set_options(ssh_session, options);
rc = ssh_connect(ssh_session);
if (rc < 0) {
ssh_disconnect(ssh_session);
ssh_session = NULL;
ssh_finalize();
goto out;
}
/* check the server public key hash */
state = ssh_is_server_known(ssh_session);
switch (state) {
case SSH_SERVER_KNOWN_OK:
break;
case SSH_SERVER_KNOWN_CHANGED:
fprintf(stderr, "Host key for server changed : server's one is now :\n");
ssh_get_pubkey_hash(ssh_session, hash);
ssh_print_hexa("Public key hash", hash, MD5_DIGEST_LEN);
fprintf(stderr,"For security reason, connection will be stopped\n");
ssh_disconnect(ssh_session);
ssh_finalize();
rc = -1;
goto out;
break;
case SSH_SERVER_FOUND_OTHER:
fprintf(stderr, "The host key for this server was not found but an other "
"type of key exists.\n");
fprintf(stderr, "An attacker might change the default server key to "
"confuse your client into thinking the key does not exist\n");
ssh_disconnect(ssh_session);
ssh_finalize();
rc = -1;
goto out;
break;
case SSH_SERVER_NOT_KNOWN:
fprintf(stderr,"The server is unknown. Connect manually to the host "
"first to retrieve the public key hash\n");
ssh_disconnect(ssh_session);
ssh_finalize();
rc = -1;
goto out;
break;
case SSH_SERVER_ERROR:
fprintf(stderr, "%s", ssh_get_error(ssh_session));
ssh_disconnect(ssh_session);
ssh_finalize();
rc = -1;
goto out;
break;
default:
break;
}
/* authenticate with the server */
if (*passwd) {
auth = ssh_userauth_password(ssh_session, user, passwd);
/*
char username[256] = {0};
char password[256] = {0};
*/
} else {
auth = ssh_userauth_autopubkey(ssh_session);
if (auth == SSH_AUTH_ERROR) {
fprintf(stderr, "Authenticating with pubkey: %s\n", ssh_get_error(ssh_session));
ssh_finalize();
rc = -1;
goto out;
}
}
/* FIXME: call callback */
/* start the sftp session */
sftp_session = sftp_new(ssh_session);
if (sftp_session == NULL) {
rc = -1;
goto out;
}
rc = sftp_init(sftp_session);
if (rc < 0) {
return rc;
}
connected = 1;
rc = 0;
out:
SAFE_FREE(user);
SAFE_FREE(passwd);
SAFE_FREE(host);
SAFE_FREE(port);
SAFE_FREE(path);
return rc;
}
csync_vio_method_handle_t *mh = NULL;
csync_vio_file_stat_t fs;
/*
* file functions
*/
static csync_vio_method_handle_t *_open(const char *durl, int flags, mode_t mode) {
csync_vio_method_handle_t *mh = NULL;
char *user = NULL;
char *passwd = NULL;
char *host = NULL;
char *port = NULL;
char *path = NULL;
/* FIXME: implement mode with SFTP_ATTRIBUTES */
(void) mode;
if (_sftp_connect(durl) < 0) {
return NULL;
}
if (parse_uri(durl, &user, &passwd, &host, &port, &path) < 0) {
return NULL;
}
mh = (csync_vio_method_handle_t *) sftp_open(sftp_session, path, flags, NULL);
SAFE_FREE(user);
SAFE_FREE(passwd);
SAFE_FREE(host);
SAFE_FREE(port);
SAFE_FREE(path);
return mh;
}
static csync_vio_method_handle_t *_creat(const char *durl, mode_t mode) {
csync_vio_method_handle_t *mh = NULL;
char *user = NULL;
char *passwd = NULL;
char *host = NULL;
char *port = NULL;
char *path = NULL;
(void) mode;
if (_sftp_connect(durl) < 0) {
return NULL;
}
if (parse_uri(durl, &user, &passwd, &host, &port, &path) < 0) {
return NULL;
}
mh = (csync_vio_method_handle_t *) sftp_open(sftp_session, path, O_CREAT|O_WRONLY|O_TRUNC, NULL);
SAFE_FREE(user);
SAFE_FREE(passwd);
SAFE_FREE(host);
SAFE_FREE(port);
SAFE_FREE(path);
return mh;
}
static int _close(csync_vio_method_handle_t *fhandle) {
return sftp_file_close(fhandle);
}
static ssize_t _read(csync_vio_method_handle_t *fhandle, void *buf, size_t count) {
return sftp_read(fhandle, buf, count);
}
static ssize_t _write(csync_vio_method_handle_t *fhandle, const void *buf, size_t count) {
return sftp_write(fhandle, buf, count);
}
static off_t _lseek(csync_vio_method_handle_t *fhandle, off_t offset, int whence) {
/* FIXME: really implement seek for lseek? */
(void) whence;
sftp_seek(fhandle, offset);
return 0;
}
/*
* directory functions
*/
static csync_vio_method_handle_t *_opendir(const char *durl) {
csync_vio_method_handle_t *mh = NULL;
char *user = NULL;
char *passwd = NULL;
char *host = NULL;
char *port = NULL;
char *path = NULL;
if (_sftp_connect(durl) < 0) {
return NULL;
}
if (parse_uri(durl, &user, &passwd, &host, &port, &path) < 0) {
return NULL;
}
mh = (csync_vio_method_handle_t *) sftp_opendir(sftp_session, path);
SAFE_FREE(user);
SAFE_FREE(passwd);
SAFE_FREE(host);
SAFE_FREE(port);
SAFE_FREE(path);
return mh;
}
static int _closedir(csync_vio_method_handle_t *dhandle) {
return sftp_dir_close(dhandle);
}
static csync_vio_file_stat_t *_readdir(csync_vio_method_handle_t *dhandle) {
SFTP_ATTRIBUTES *dirent = NULL;
csync_vio_file_stat_t *fs = NULL;
/* TODO: consider adding the _sftp_connect function */
dirent = sftp_readdir(sftp_session, dhandle);
if (dirent == NULL) {
return NULL;
}
fs = c_malloc(sizeof(csync_vio_file_stat_t));
if (fs == NULL) {
return NULL;
}
fs->name = c_strdup(dirent->name);
fs->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
switch (dirent->type) {
case SSH_FILEXFER_TYPE_REGULAR:
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
fs->type = CSYNC_VIO_FILE_TYPE_REGULAR;
break;
case SSH_FILEXFER_TYPE_DIRECTORY:
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
fs->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
break;
case SSH_FILEXFER_TYPE_SYMLINK:
case SSH_FILEXFER_TYPE_SPECIAL:
case SSH_FILEXFER_TYPE_UNKNOWN:
break;
}
return fs;
}
static int _mkdir(const char *uri, mode_t mode) {
(void) uri;
(void) mode;
return 0;
}
static int _rmdir(const char *uri) {
(void) uri;
return 0;
}
static int _stat(const char *uri, csync_vio_file_stat_t *buf) {
time_t now;
buf->name = c_basename(uri);
if (buf->name == NULL) {
csync_vio_file_stat_destroy(buf);
}
buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
time(&now);
buf->mtime = now;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
return 0;
}
static int _rename(const char *olduri, const char *newuri) {
(void) olduri;
(void) newuri;
return 0;
}
static int _unlink(const char *uri) {
(void) uri;
return 0;
}
static int _chmod(const char *uri, mode_t mode) {
(void) uri;
(void) mode;
return 0;
}
static int _chown(const char *uri, uid_t owner, gid_t group) {
(void) uri;
(void) owner;
(void) group;
return 0;
}
static int _utimes(const char *uri, const struct timeval *times) {
(void) uri;
(void) times;
return 0;
}
csync_vio_method_t _method = {
.method_table_size = sizeof(csync_vio_method_t),
.open = _open,
.creat = _creat,
.close = _close,
.read = _read,
.write = _write,
.lseek = _lseek,
.opendir = _opendir,
.closedir = _closedir,
.readdir = _readdir,
.mkdir = _mkdir,
.rmdir = _rmdir,
.stat = _stat,
.rename = _rename,
.unlink = _unlink,
.chmod = _chmod,
.chown = _chown,
.utimes = _utimes
};
csync_vio_method_t *vio_module_init(const char *method_name, const char *args, csync_auth_callback cb) {
DEBUG_SFTP(("csync_sftp - method_name: %s\n", method_name));
DEBUG_SFTP(("csync_sftp - args: %s\n", args));
(void) method_name;
(void) args;
return &_method;
}
void vio_module_shutdown(csync_vio_method_t *method) {
(void) method;
}