GUACAMOLE-169: Move common-ssh headers to namespaced directory.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/d371f2d9 Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/d371f2d9 Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/d371f2d9 Branch: refs/heads/staging/0.9.12-incubating Commit: d371f2d9eee689d717dcd0eae4238812a835258c Parents: a808a6b Author: Michael Jumper <[email protected]> Authored: Mon Feb 27 14:28:23 2017 -0800 Committer: Michael Jumper <[email protected]> Committed: Mon Feb 27 14:28:23 2017 -0800 ---------------------------------------------------------------------- src/common-ssh/Makefile.am | 24 +- src/common-ssh/buffer.c | 135 ++++++ src/common-ssh/common-ssh/buffer.h | 134 ++++++ src/common-ssh/common-ssh/key.h | 170 +++++++ src/common-ssh/common-ssh/sftp.h | 242 ++++++++++ src/common-ssh/common-ssh/ssh.h | 114 +++++ src/common-ssh/common-ssh/user.h | 108 +++++ src/common-ssh/guac_sftp.c | 783 -------------------------------- src/common-ssh/guac_sftp.h | 242 ---------- src/common-ssh/guac_ssh.c | 544 ---------------------- src/common-ssh/guac_ssh.h | 114 ----- src/common-ssh/guac_ssh_buffer.c | 135 ------ src/common-ssh/guac_ssh_buffer.h | 134 ------ src/common-ssh/guac_ssh_key.c | 217 --------- src/common-ssh/guac_ssh_key.h | 170 ------- src/common-ssh/guac_ssh_user.c | 82 ---- src/common-ssh/guac_ssh_user.h | 108 ----- src/common-ssh/key.c | 217 +++++++++ src/common-ssh/sftp.c | 783 ++++++++++++++++++++++++++++++++ src/common-ssh/ssh.c | 544 ++++++++++++++++++++++ src/common-ssh/user.c | 82 ++++ src/protocols/rdp/client.c | 6 +- src/protocols/rdp/rdp.c | 6 +- src/protocols/rdp/rdp.h | 6 +- src/protocols/rdp/sftp.c | 2 +- src/protocols/ssh/client.c | 2 +- src/protocols/ssh/sftp.c | 2 +- src/protocols/ssh/ssh.c | 4 +- src/protocols/ssh/ssh.h | 6 +- src/protocols/vnc/client.c | 6 +- src/protocols/vnc/sftp.c | 2 +- src/protocols/vnc/vnc.c | 4 +- src/protocols/vnc/vnc.h | 6 +- 33 files changed, 2567 insertions(+), 2567 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/Makefile.am ---------------------------------------------------------------------- diff --git a/src/common-ssh/Makefile.am b/src/common-ssh/Makefile.am index a53f2cf..c05f264 100644 --- a/src/common-ssh/Makefile.am +++ b/src/common-ssh/Makefile.am @@ -23,18 +23,18 @@ ACLOCAL_AMFLAGS = -I m4 noinst_LTLIBRARIES = libguac_common_ssh.la libguac_common_ssh_la_SOURCES = \ - guac_sftp.c \ - guac_ssh.c \ - guac_ssh_buffer.c \ - guac_ssh_key.c \ - guac_ssh_user.c - -noinst_HEADERS = \ - guac_sftp.h \ - guac_ssh.h \ - guac_ssh_buffer.h \ - guac_ssh_key.h \ - guac_ssh_user.h + buffer.c \ + sftp.c \ + ssh.c \ + key.c \ + user.c + +noinst_HEADERS = \ + common-ssh/buffer.h \ + common-ssh/key.h \ + common-ssh/sftp.h \ + common-ssh/ssh.h \ + common-ssh/user.h libguac_common_ssh_la_CFLAGS = \ -Werror -Wall -pedantic \ http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/buffer.c ---------------------------------------------------------------------- diff --git a/src/common-ssh/buffer.c b/src/common-ssh/buffer.c new file mode 100644 index 0000000..b3d8d57 --- /dev/null +++ b/src/common-ssh/buffer.c @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" + +#include <openssl/bn.h> +#include <openssl/ossl_typ.h> + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> + +void guac_common_ssh_buffer_write_byte(char** buffer, uint8_t value) { + + uint8_t* data = (uint8_t*) *buffer; + *data = value; + + (*buffer)++; + +} + +void guac_common_ssh_buffer_write_uint32(char** buffer, uint32_t value) { + + uint8_t* data = (uint8_t*) *buffer; + + data[0] = (value & 0xFF000000) >> 24; + data[1] = (value & 0x00FF0000) >> 16; + data[2] = (value & 0x0000FF00) >> 8; + data[3] = value & 0x000000FF; + + *buffer += 4; + +} + +void guac_common_ssh_buffer_write_data(char** buffer, const char* data, + int length) { + memcpy(*buffer, data, length); + *buffer += length; +} + +void guac_common_ssh_buffer_write_bignum(char** buffer, BIGNUM* value) { + + unsigned char* bn_buffer; + int length; + + /* If zero, just write zero length */ + if (BN_is_zero(value)) { + guac_common_ssh_buffer_write_uint32(buffer, 0); + return; + } + + /* Allocate output buffer, add padding byte */ + length = BN_num_bytes(value); + bn_buffer = malloc(length); + + /* Convert BIGNUM */ + BN_bn2bin(value, bn_buffer); + + /* If first byte has high bit set, write padding byte */ + if (bn_buffer[0] & 0x80) { + guac_common_ssh_buffer_write_uint32(buffer, length+1); + guac_common_ssh_buffer_write_byte(buffer, 0); + } + else + guac_common_ssh_buffer_write_uint32(buffer, length); + + /* Write data */ + memcpy(*buffer, bn_buffer, length); + *buffer += length; + + free(bn_buffer); + +} + +void guac_common_ssh_buffer_write_string(char** buffer, const char* string, + int length) { + guac_common_ssh_buffer_write_uint32(buffer, length); + guac_common_ssh_buffer_write_data(buffer, string, length); +} + +uint8_t guac_common_ssh_buffer_read_byte(char** buffer) { + + uint8_t* data = (uint8_t*) *buffer; + uint8_t value = *data; + + (*buffer)++; + + return value; + +} + +uint32_t guac_common_ssh_buffer_read_uint32(char** buffer) { + + uint8_t* data = (uint8_t*) *buffer; + uint32_t value = + (data[0] << 24) + | (data[1] << 16) + | (data[2] << 8) + | data[3]; + + *buffer += 4; + + return value; + +} + +char* guac_common_ssh_buffer_read_string(char** buffer, int* length) { + + char* value; + + *length = guac_common_ssh_buffer_read_uint32(buffer); + value = *buffer; + + *buffer += *length; + + return value; + +} + http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/common-ssh/buffer.h ---------------------------------------------------------------------- diff --git a/src/common-ssh/common-ssh/buffer.h b/src/common-ssh/common-ssh/buffer.h new file mode 100644 index 0000000..b42a2ac --- /dev/null +++ b/src/common-ssh/common-ssh/buffer.h @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_COMMON_SSH_BUFFER_H +#define GUAC_COMMON_SSH_BUFFER_H + +#include "config.h" + +#include <openssl/bn.h> +#include <stdint.h> + +/** + * Writes the given byte to the given buffer, advancing the buffer pointer by + * one byte. + * + * @param buffer + * The buffer to write to. + * + * @param value + * The value to write. + */ +void guac_common_ssh_buffer_write_byte(char** buffer, uint8_t value); + +/** + * Writes the given integer to the given buffer, advancing the buffer pointer + * four bytes. + * + * @param buffer + * The buffer to write to. + * + * @param value + * The value to write. + */ +void guac_common_ssh_buffer_write_uint32(char** buffer, uint32_t value); + +/** + * Writes the given string and its length to the given buffer, advancing the + * buffer pointer by the size of the length (four bytes) and the size of the + * string. + * + * @param buffer + * The buffer to write to. + * + * @param string + * The string value to write. + * + * @param length + * The length of the string to write, in bytes. + */ +void guac_common_ssh_buffer_write_string(char** buffer, const char* string, + int length); + +/** + * Writes the given BIGNUM the given buffer, advancing the buffer pointer by + * the size of the length (four bytes) and the size of the BIGNUM. + * + * @param buffer + * The buffer to write to. + * + * @param value + * The value to write. + */ +void guac_common_ssh_buffer_write_bignum(char** buffer, BIGNUM* value); + +/** + * Writes the given data the given buffer, advancing the buffer pointer by the + * given length. + * + * @param data + * The arbitrary data to write. + * + * @param length + * The length of data to write, in bytes. + */ +void guac_common_ssh_buffer_write_data(char** buffer, const char* data, int length); + +/** + * Reads a single byte from the given buffer, advancing the buffer by one byte. + * + * @param buffer + * The buffer to read from. + * + * @return + * The value read from the buffer. + */ +uint8_t guac_common_ssh_buffer_read_byte(char** buffer); + +/** + * Reads an integer from the given buffer, advancing the buffer by four bytes. + * + * @param buffer + * The buffer to read from. + * + * @return + * The value read from the buffer. + */ +uint32_t guac_common_ssh_buffer_read_uint32(char** buffer); + +/** + * Reads a string and its length from the given buffer, advancing the buffer + * by the size of the length (four bytes) and the size of the string, and + * returning a pointer to the buffer. The length of the string is stored in + * the given int. + * + * @param buffer + * The buffer to read from. + * + * @param length + * A pointer to an integer into which the length of the read string will + * be stored. + * + * @return + * A pointer to the value within the buffer. + */ +char* guac_common_ssh_buffer_read_string(char** buffer, int* length); + +#endif + http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/common-ssh/key.h ---------------------------------------------------------------------- diff --git a/src/common-ssh/common-ssh/key.h b/src/common-ssh/common-ssh/key.h new file mode 100644 index 0000000..1754dea --- /dev/null +++ b/src/common-ssh/common-ssh/key.h @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_COMMON_SSH_KEY_H +#define GUAC_COMMON_SSH_KEY_H + +#include "config.h" + +#include <openssl/ossl_typ.h> + +/** + * The expected header of RSA private keys. + */ +#define SSH_RSA_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----" + +/** + * The expected header of DSA private keys. + */ +#define SSH_DSA_KEY_HEADER "-----BEGIN DSA PRIVATE KEY-----" + +/** + * The size of single number within a DSA signature, in bytes. + */ +#define DSA_SIG_NUMBER_SIZE 20 + +/** + * The size of a DSA signature, in bytes. + */ +#define DSA_SIG_SIZE DSA_SIG_NUMBER_SIZE*2 + +/** + * The type of an SSH key. + */ +typedef enum guac_common_ssh_key_type { + + /** + * RSA key. + */ + SSH_KEY_RSA, + + /** + * DSA key. + */ + SSH_KEY_DSA + +} guac_common_ssh_key_type; + +/** + * Abstraction of a key used for SSH authentication. + */ +typedef struct guac_common_ssh_key { + + /** + * The type of this key. + */ + guac_common_ssh_key_type type; + + /** + * Underlying RSA private key, if any. + */ + RSA* rsa; + + /** + * Underlying DSA private key, if any. + */ + DSA* dsa; + + /** + * The associated public key, encoded as necessary for SSH. + */ + char* public_key; + + /** + * The length of the public key, in bytes. + */ + int public_key_length; + + /** + * The private key, encoded as necessary for SSH. + */ + char* private_key; + + /** + * The length of the private key, in bytes. + */ + int private_key_length; + +} guac_common_ssh_key; + +/** + * Allocates a new key containing the given private key data and specified + * passphrase. If unable to read the key, NULL is returned. + * + * @param data + * The base64-encoded data to decode when reading the key. + * + * @param length + * The length of the provided data, in bytes. + * + * @param passphrase + * The passphrase to use when decrypting the key, if any, or an empty + * string or NULL if no passphrase is needed. + * + * @return + * The decoded, decrypted private key, or NULL if the key could not be + * decoded. + */ +guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length, + char* passphrase); + +/** + * Returns a statically-allocated string describing the most recent SSH key + * error. + * + * @return + * A statically-allocated string describing the most recent SSH key error. + */ +const char* guac_common_ssh_key_error(); + +/** + * Frees all memory associated with the given key. + * + * @param key + * The key to free. + */ +void guac_common_ssh_key_free(guac_common_ssh_key* key); + +/** + * Signs the given data using the given key, returning the length of the + * signature in bytes, or a value less than zero on error. + * + * @param key + * The key to use when signing the given data. + * + * @param data + * The arbitrary data to sign. + * + * @param length + * The length of the arbitrary data being signed, in bytes. + * + * @param sig + * The buffer into which the signature should be written. The buffer must + * be at least DSA_SIG_SIZE for DSA keys. For RSA keys, the signature size + * is dependent only on key size, and is equal to the length of the + * modulus, in bytes. + * + * @return + * The number of bytes in the resulting signature. + */ +int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data, + int length, unsigned char* sig); + +#endif + http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/common-ssh/sftp.h ---------------------------------------------------------------------- diff --git a/src/common-ssh/common-ssh/sftp.h b/src/common-ssh/common-ssh/sftp.h new file mode 100644 index 0000000..038a577 --- /dev/null +++ b/src/common-ssh/common-ssh/sftp.h @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_COMMON_SSH_SFTP_H +#define GUAC_COMMON_SSH_SFTP_H + +#include "common/json.h" +#include "ssh.h" + +#include <guacamole/object.h> +#include <guacamole/user.h> +#include <libssh2.h> +#include <libssh2_sftp.h> + +/** + * Maximum number of bytes per path. + */ +#define GUAC_COMMON_SSH_SFTP_MAX_PATH 2048 + +/** + * Representation of an SFTP-driven filesystem object. Unlike guac_object, this + * structure is not tied to any particular user. + */ +typedef struct guac_common_ssh_sftp_filesystem { + + /** + * The human-readable display name of this filesystem. + */ + char* name; + + /** + * The distinct SSH session used for SFTP. + */ + guac_common_ssh_session* ssh_session; + + /** + * SFTP session, used for file transfers. + */ + LIBSSH2_SFTP* sftp_session; + + /** + * The path files will be sent to, if uploaded directly via a "file" + * instruction. + */ + char upload_path[GUAC_COMMON_SSH_SFTP_MAX_PATH]; + +} guac_common_ssh_sftp_filesystem; + +/** + * The current state of a directory listing operation. + */ +typedef struct guac_common_ssh_sftp_ls_state { + + /** + * The SFTP filesystem being listed. + */ + guac_common_ssh_sftp_filesystem* filesystem; + + /** + * Reference to the directory currently being listed over SFTP. This + * directory must already be open from a call to libssh2_sftp_opendir(). + */ + LIBSSH2_SFTP_HANDLE* directory; + + /** + * The absolute path of the directory being listed. + */ + char directory_name[GUAC_COMMON_SSH_SFTP_MAX_PATH]; + + /** + * The current state of the JSON directory object being written. + */ + guac_common_json_state json_state; + +} guac_common_ssh_sftp_ls_state; + +/** + * Creates a new Guacamole filesystem object which provides access to files + * and directories via SFTP using the given SSH session. When the filesystem + * will no longer be used, it must be explicitly destroyed with + * guac_common_ssh_destroy_sftp_filesystem(). The resulting object is not + * automatically exposed to users of the connection - filesystem operations + * must be mediated either through various handlers or through exposing a + * filesystem guac_object via guac_common_ssh_alloc_sftp_filesystem_object(). + * + * @param session + * The session to use to provide SFTP. This session will automatically be + * destroyed when this filesystem is destroyed. + * + * @param name + * The name to send as the name of the filesystem whenever it is exposed + * to a user. + * + * @return + * A new SFTP filesystem object, not yet exposed to users. + */ +guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem( + guac_common_ssh_session* session, const char* name); + +/** + * Destroys the given filesystem object, disconnecting from SFTP and freeing + * and associated resources. Any associated session or user objects must be + * explicitly destroyed. + * + * @param filesystem + * The filesystem object to destroy. + */ +void guac_common_ssh_destroy_sftp_filesystem( + guac_common_ssh_sftp_filesystem* filesystem); + +/** + * Creates and exposes a new filesystem guac_object to the given user, + * providing access to the files within the given SFTP filesystem. The + * allocated guac_object must eventually be freed via guac_user_free_object(). + * + * @param filesystem + * The filesystem object to expose. + * + * @param user + * The user that the SFTP filesystem should be exposed to. + * + * @return + * A new Guacamole filesystem object, configured to use SFTP for uploading + * and downloading files. + */ +guac_object* guac_common_ssh_alloc_sftp_filesystem_object( + guac_common_ssh_sftp_filesystem* filesystem, guac_user* user); + +/** + * Allocates a new filesystem guac_object for the given user, returning the + * resulting guac_object. This function is provided for convenience, as it is + * can be used as the callback for guac_client_foreach_user() or + * guac_client_for_owner(). Note that this guac_object will be tracked + * internally by libguac, will be provided to us in the parameters of handlers + * related to that guac_object, and will automatically be freed when the + * associated guac_user is freed, so the return value of this function can + * safely be ignored. + * + * If either the given user or the given filesystem are NULL, then this + * function has no effect. + * + * @param user + * The use to expose the filesystem to, or NULL if nothing should be + * exposed. + * + * @param data + * A pointer to the guac_common_ssh_sftp_filesystem instance to expose + * to the given user, or NULL if nothing should be exposed. + * + * @return + * The guac_object allocated for the newly-exposed filesystem, or NULL if + * no filesystem object could be allocated. + */ +void* guac_common_ssh_expose_sftp_filesystem(guac_user* user, void* data); + +/** + * Initiates an SFTP file download to the user via the Guacamole "file" + * instruction. The download will be automatically monitored and continued + * after this function terminates in response to "ack" instructions received by + * the user. + * + * @param filesystem + * The filesystem containing the file to be downloaded. + * + * @param user + * The user that should receive the file (via a "file" instruction). + * + * @param filename + * The filename of the file to download, relative to the given filesystem. + * + * @return + * The file stream created for the file download, already configured to + * properly handle "ack" responses, etc. from the user. + */ +guac_stream* guac_common_ssh_sftp_download_file( + guac_common_ssh_sftp_filesystem* filesystem, guac_user* user, + char* filename); + +/** + * Handles an incoming stream from a Guacamole "file" instruction, saving the + * contents of that stream to the file having the given name within the + * upload directory set by guac_common_ssh_sftp_set_upload_path(). + * + * @param filesystem + * The filesystem that should receive the uploaded file. + * + * @param user + * The user who is attempting to open the file stream (the user that sent + * the "file" instruction). + * + * @param stream + * The stream through which the uploaded file data will be received. + * + * @param mimetype + * The mimetype of the data being received. + * + * @param filename + * The filename of the file to write to. This filename will always be taken + * relative to the upload path set by + * guac_common_ssh_sftp_set_upload_path(). + * + * @return + * Zero if the incoming stream has been handled successfully, non-zero on + * failure. + */ +int guac_common_ssh_sftp_handle_file_stream( + guac_common_ssh_sftp_filesystem* filesystem, guac_user* user, + guac_stream* stream, char* mimetype, char* filename); + +/** + * Set the destination directory for future uploads submitted via + * guac_common_ssh_sftp_handle_file_stream(). This function has no bearing + * on the destination directories of files uploaded with "put" instructions. + * + * @param filesystem + * The filesystem to set the upload path of. + * + * @param path + * The path to use for future uploads submitted via the + * guac_common_ssh_sftp_handle_file_stream() function. + */ +void guac_common_ssh_sftp_set_upload_path( + guac_common_ssh_sftp_filesystem* filesystem, const char* path); + +#endif + http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/common-ssh/ssh.h ---------------------------------------------------------------------- diff --git a/src/common-ssh/common-ssh/ssh.h b/src/common-ssh/common-ssh/ssh.h new file mode 100644 index 0000000..d96ce44 --- /dev/null +++ b/src/common-ssh/common-ssh/ssh.h @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_COMMON_SSH_H +#define GUAC_COMMON_SSH_H + +#include "user.h" + +#include <guacamole/client.h> +#include <libssh2.h> + +/** + * An SSH session, backed by libssh2 and associated with a particular + * Guacamole client. + */ +typedef struct guac_common_ssh_session { + + /** + * The Guacamole client using this SSH session. + */ + guac_client* client; + + /** + * The user that will be authenticating via SSH. + */ + guac_common_ssh_user* user; + + /** + * The underlying SSH session from libssh2. + */ + LIBSSH2_SESSION* session; + + /** + * The file descriptor of the socket being used for the SSH connection. + */ + int fd; + +} guac_common_ssh_session; + +/** + * Initializes the underlying SSH and encryption libraries used by Guacamole. + * This function must be called before any other guac_common_ssh_*() functions + * are called. + * + * @param client + * The Guacamole client that will be using SSH. + * + * @return + * Zero if initialization, or non-zero if an error occurs. + */ +int guac_common_ssh_init(guac_client* client); + +/** + * Cleans up the underlying SSH and encryption libraries used by Guacamole. + * This function must be called once no other guac_common_ssh_*() functions + * will be used. + */ +void guac_common_ssh_uninit(); + +/** + * Connects to the SSH server running at the given hostname and port, and + * authenticates as the given user. If an error occurs while connecting or + * authenticating, the Guacamole client will automatically and fatally abort. + * The user object provided must eventually be explicitly destroyed, but should + * not be destroyed until this session is destroyed, assuming the session is + * successfully created. + * + * @param client + * The Guacamole client that will be using SSH. + * + * @param hostname + * The hostname of the SSH server to connect to. + * + * @param port + * The port to connect to on the given hostname. + * + * @param user + * The user to authenticate as, once connected. + * + * @return + * A new SSH session if the connection and authentication succeed, or NULL + * if the connection or authentication were not successful. + */ +guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, + const char* hostname, const char* port, guac_common_ssh_user* user); + +/** + * Disconnects and destroys the given SSH session, freeing all associated + * resources. Any associated user must be explicitly destroyed, and will not + * be destroyed automatically. + * + * @param session + * The SSH session to destroy. + */ +void guac_common_ssh_destroy_session(guac_common_ssh_session* session); + +#endif + http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/common-ssh/user.h ---------------------------------------------------------------------- diff --git a/src/common-ssh/common-ssh/user.h b/src/common-ssh/common-ssh/user.h new file mode 100644 index 0000000..745728f --- /dev/null +++ b/src/common-ssh/common-ssh/user.h @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_COMMON_SSH_USER_H +#define GUAC_COMMON_SSH_USER_H + +#include "key.h" + +/** + * Data describing an SSH user, including their credentials. + */ +typedef struct guac_common_ssh_user { + + /** + * The username of this user. + */ + char* username; + + /** + * The password which should be used to authenticate this user, if any, or + * NULL if a private key will be used instead. + */ + char* password; + + /** + * The private key which should be used to authenticate this user, if any, + * or NULL if a password will be used instead. + */ + guac_common_ssh_key* private_key; + +} guac_common_ssh_user; + +/** + * Creates a new SSH user with the given username. When additionally populated + * with a password or private key, this user can then be used for + * authentication. + * + * @param username + * The username of the user being created. + * + * @return + * A new SSH user having the given username, but no associated password + * or private key. + */ +guac_common_ssh_user* guac_common_ssh_create_user(const char* username); + +/** + * Destroys the given user object, releasing all associated resources. + * + * @param user + * The user to destroy. + */ +void guac_common_ssh_destroy_user(guac_common_ssh_user* user); + +/** + * Associates the given user with the given password, such that that password + * is used for future authentication attempts. + * + * @param user + * The user to associate with the given password. + * + * @param password + * The password to associate with the given user. + */ +void guac_common_ssh_user_set_password(guac_common_ssh_user* user, + const char* password); + +/** + * Imports the given private key, associating that key with the given user. If + * necessary to decrypt the key, a passphrase may be specified. The private key + * must be provided in base64 form. If the private key is imported + * successfully, it will be used for future authentication attempts. + * + * @param user + * The user to associate with the given private key. + * + * @param private_key + * The base64-encoded private key to import. + * + * @param passphrase + * The passphrase to use to decrypt the given private key, or NULL if no + * passphrase should be used. + * + * @return + * Zero if the private key is successfully imported, or non-zero if the + * private key could not be imported due to an error. + */ +int guac_common_ssh_user_import_key(guac_common_ssh_user* user, + char* private_key, char* passphrase); + +#endif + http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/guac_sftp.c ---------------------------------------------------------------------- diff --git a/src/common-ssh/guac_sftp.c b/src/common-ssh/guac_sftp.c deleted file mode 100644 index e7314d2..0000000 --- a/src/common-ssh/guac_sftp.c +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "guac_sftp.h" -#include "guac_ssh.h" - -#include <guacamole/client.h> -#include <guacamole/object.h> -#include <guacamole/protocol.h> -#include <guacamole/socket.h> -#include <guacamole/user.h> -#include <libssh2.h> - -#include <fcntl.h> -#include <libgen.h> -#include <stdlib.h> -#include <string.h> - -/** - * Translates the last error message received by the SFTP layer of an SSH - * session into a Guacamole protocol status code. - * - * @param filesystem - * The object (not guac_object) defining the filesystem associated with the - * SFTP and SSH sessions. - * - * @return - * The Guacamole protocol status code corresponding to the last reported - * error of the SFTP layer, if nay, or GUAC_PROTOCOL_STATUS_SUCCESS if no - * error has occurred. - */ -static guac_protocol_status guac_sftp_get_status( - guac_common_ssh_sftp_filesystem* filesystem) { - - /* Get libssh2 objects */ - LIBSSH2_SFTP* sftp = filesystem->sftp_session; - LIBSSH2_SESSION* session = filesystem->ssh_session->session; - - /* Return success code if no error occurred */ - if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_SFTP_PROTOCOL) - return GUAC_PROTOCOL_STATUS_SUCCESS; - - /* Translate SFTP error codes defined by - * https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02 (the most - * commonly-implemented standard) */ - switch (libssh2_sftp_last_error(sftp)) { - - /* SSH_FX_OK (not an error) */ - case 0: - return GUAC_PROTOCOL_STATUS_SUCCESS; - - /* SSH_FX_EOF (technically not an error) */ - case 1: - return GUAC_PROTOCOL_STATUS_SUCCESS; - - /* SSH_FX_NO_SUCH_FILE */ - case 2: - return GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND; - - /* SSH_FX_PERMISSION_DENIED */ - case 3: - return GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN; - - /* SSH_FX_FAILURE */ - case 4: - return GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; - - /* SSH_FX_BAD_MESSAGE */ - case 5: - return GUAC_PROTOCOL_STATUS_SERVER_ERROR; - - /* SSH_FX_NO_CONNECTION / SSH_FX_CONNECTION_LOST */ - case 6: - case 7: - return GUAC_PROTOCOL_STATUS_UPSTREAM_TIMEOUT; - - /* SSH_FX_OP_UNSUPPORTED */ - case 8: - return GUAC_PROTOCOL_STATUS_UNSUPPORTED; - - /* Return generic error if cause unknown */ - default: - return GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR; - - } - -} - -/** - * Concatenates the given filename with the given path, separating the two - * with a single forward slash. The full result must be no more than - * GUAC_COMMON_SSH_SFTP_MAX_PATH bytes long, counting null terminator. - * - * @param fullpath - * The buffer to store the result within. This buffer must be at least - * GUAC_COMMON_SSH_SFTP_MAX_PATH bytes long. - * - * @param path - * The path to append the filename to. - * - * @param filename - * The filename to append to the path. - * - * @return - * Non-zero if the filename is valid and was successfully appended to the - * path, zero otherwise. - */ -static int guac_ssh_append_filename(char* fullpath, const char* path, - const char* filename) { - - int i; - - /* Disallow "." as a filename */ - if (strcmp(filename, ".") == 0) - return 0; - - /* Disallow ".." as a filename */ - if (strcmp(filename, "..") == 0) - return 0; - - /* Copy path, append trailing slash */ - for (i=0; i<GUAC_COMMON_SSH_SFTP_MAX_PATH; i++) { - - /* - * Append trailing slash only if: - * 1) Trailing slash is not already present - * 2) Path is non-empty - */ - - char c = path[i]; - if (c == '\0') { - if (i > 0 && path[i-1] != '/') - fullpath[i++] = '/'; - break; - } - - /* Copy character if not end of string */ - fullpath[i] = c; - - } - - /* Append filename */ - for (; i<GUAC_COMMON_SSH_SFTP_MAX_PATH; i++) { - - char c = *(filename++); - if (c == '\0') - break; - - /* Filenames may not contain slashes */ - if (c == '\\' || c == '/') - return 0; - - /* Append each character within filename */ - fullpath[i] = c; - - } - - /* Verify path length is within maximum */ - if (i == GUAC_COMMON_SSH_SFTP_MAX_PATH) - return 0; - - /* Terminate path string */ - fullpath[i] = '\0'; - - /* Append was successful */ - return 1; - -} - -/** - * Handler for blob messages which continue an inbound SFTP data transfer - * (upload). The data associated with the given stream is expected to be a - * pointer to an open LIBSSH2_SFTP_HANDLE for the file to which the data - * should be written. - * - * @param user - * The user receiving the blob message. - * - * @param stream - * The Guacamole protocol stream associated with the received blob message. - * - * @param data - * The data received within the blob. - * - * @param length - * The length of the received data, in bytes. - * - * @return - * Zero if the blob is handled successfully, or non-zero on error. - */ -static int guac_common_ssh_sftp_blob_handler(guac_user* user, - guac_stream* stream, void* data, int length) { - - /* Pull file from stream */ - LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data; - - /* Attempt write */ - if (libssh2_sftp_write(file, data, length) == length) { - guac_user_log(user, GUAC_LOG_DEBUG, "%i bytes written", length); - guac_protocol_send_ack(user->socket, stream, "SFTP: OK", - GUAC_PROTOCOL_STATUS_SUCCESS); - guac_socket_flush(user->socket); - } - - /* Inform of any errors */ - else { - guac_user_log(user, GUAC_LOG_INFO, "Unable to write to file"); - guac_protocol_send_ack(user->socket, stream, "SFTP: Write failed", - GUAC_PROTOCOL_STATUS_SERVER_ERROR); - guac_socket_flush(user->socket); - } - - return 0; - -} - -/** - * Handler for end messages which terminate an inbound SFTP data transfer - * (upload). The data associated with the given stream is expected to be a - * pointer to an open LIBSSH2_SFTP_HANDLE for the file to which the data - * has been written and which should now be closed. - * - * @param user - * The user receiving the end message. - * - * @param stream - * The Guacamole protocol stream associated with the received end message. - * - * @return - * Zero if the file is closed successfully, or non-zero on error. - */ -static int guac_common_ssh_sftp_end_handler(guac_user* user, - guac_stream* stream) { - - /* Pull file from stream */ - LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data; - - /* Attempt to close file */ - if (libssh2_sftp_close(file) == 0) { - guac_user_log(user, GUAC_LOG_DEBUG, "File closed"); - guac_protocol_send_ack(user->socket, stream, "SFTP: OK", - GUAC_PROTOCOL_STATUS_SUCCESS); - guac_socket_flush(user->socket); - } - else { - guac_user_log(user, GUAC_LOG_INFO, "Unable to close file"); - guac_protocol_send_ack(user->socket, stream, "SFTP: Close failed", - GUAC_PROTOCOL_STATUS_SERVER_ERROR); - guac_socket_flush(user->socket); - } - - return 0; - -} - -int guac_common_ssh_sftp_handle_file_stream( - guac_common_ssh_sftp_filesystem* filesystem, guac_user* user, - guac_stream* stream, char* mimetype, char* filename) { - - char fullpath[GUAC_COMMON_SSH_SFTP_MAX_PATH]; - LIBSSH2_SFTP_HANDLE* file; - - /* Concatenate filename with path */ - if (!guac_ssh_append_filename(fullpath, filesystem->upload_path, - filename)) { - - guac_user_log(user, GUAC_LOG_DEBUG, - "Filename \"%s\" is invalid or resulting path is too long", - filename); - - /* Abort transfer - invalid filename */ - guac_protocol_send_ack(user->socket, stream, - "SFTP: Illegal filename", - GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST); - - guac_socket_flush(user->socket); - return 0; - } - - /* Open file via SFTP */ - file = libssh2_sftp_open(filesystem->sftp_session, fullpath, - LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, - S_IRUSR | S_IWUSR); - - /* Inform of status */ - if (file != NULL) { - - guac_user_log(user, GUAC_LOG_DEBUG, - "File \"%s\" opened", - fullpath); - - guac_protocol_send_ack(user->socket, stream, "SFTP: File opened", - GUAC_PROTOCOL_STATUS_SUCCESS); - guac_socket_flush(user->socket); - } - else { - guac_user_log(user, GUAC_LOG_INFO, - "Unable to open file \"%s\"", fullpath); - guac_protocol_send_ack(user->socket, stream, "SFTP: Open failed", - guac_sftp_get_status(filesystem)); - guac_socket_flush(user->socket); - } - - /* Set handlers for file stream */ - stream->blob_handler = guac_common_ssh_sftp_blob_handler; - stream->end_handler = guac_common_ssh_sftp_end_handler; - - /* Store file within stream */ - stream->data = file; - return 0; - -} - -/** - * Handler for ack messages which continue an outbound SFTP data transfer - * (download), signalling the current status and requesting additional data. - * The data associated with the given stream is expected to be a pointer to an - * open LIBSSH2_SFTP_HANDLE for the file from which the data is to be read. - * - * @param user - * The user receiving the ack message. - * - * @param stream - * The Guacamole protocol stream associated with the received ack message. - * - * @param message - * An arbitrary human-readable message describing the nature of the - * success or failure denoted by the ack message. - * - * @param status - * The status code associated with the ack message, which may indicate - * success or an error. - * - * @return - * Zero if the file is read from successfully, or non-zero on error. - */ -static int guac_common_ssh_sftp_ack_handler(guac_user* user, - guac_stream* stream, char* message, guac_protocol_status status) { - - /* Pull file from stream */ - LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data; - - /* If successful, read data */ - if (status == GUAC_PROTOCOL_STATUS_SUCCESS) { - - /* Attempt read into buffer */ - char buffer[4096]; - int bytes_read = libssh2_sftp_read(file, buffer, sizeof(buffer)); - - /* If bytes read, send as blob */ - if (bytes_read > 0) { - guac_protocol_send_blob(user->socket, stream, - buffer, bytes_read); - - guac_user_log(user, GUAC_LOG_DEBUG, "%i bytes sent to user", - bytes_read); - - } - - /* If EOF, send end */ - else if (bytes_read == 0) { - guac_user_log(user, GUAC_LOG_DEBUG, "File sent"); - guac_protocol_send_end(user->socket, stream); - guac_user_free_stream(user, stream); - } - - /* Otherwise, fail stream */ - else { - guac_user_log(user, GUAC_LOG_INFO, "Error reading file"); - guac_protocol_send_end(user->socket, stream); - guac_user_free_stream(user, stream); - } - - guac_socket_flush(user->socket); - - } - - /* Otherwise, return stream to user */ - else - guac_user_free_stream(user, stream); - - return 0; -} - -guac_stream* guac_common_ssh_sftp_download_file( - guac_common_ssh_sftp_filesystem* filesystem, guac_user* user, - char* filename) { - - guac_stream* stream; - LIBSSH2_SFTP_HANDLE* file; - - /* Attempt to open file for reading */ - file = libssh2_sftp_open(filesystem->sftp_session, filename, - LIBSSH2_FXF_READ, 0); - if (file == NULL) { - guac_user_log(user, GUAC_LOG_INFO, - "Unable to read file \"%s\"", filename); - return NULL; - } - - /* Allocate stream */ - stream = guac_user_alloc_stream(user); - stream->ack_handler = guac_common_ssh_sftp_ack_handler; - stream->data = file; - - /* Send stream start, strip name */ - filename = basename(filename); - guac_protocol_send_file(user->socket, stream, - "application/octet-stream", filename); - guac_socket_flush(user->socket); - - guac_user_log(user, GUAC_LOG_DEBUG, "Sending file \"%s\"", filename); - return stream; - -} - -void guac_common_ssh_sftp_set_upload_path( - guac_common_ssh_sftp_filesystem* filesystem, const char* path) { - - guac_client* client = filesystem->ssh_session->client; - - /* Ignore requests which exceed maximum-allowed path */ - int length = strnlen(path, GUAC_COMMON_SSH_SFTP_MAX_PATH)+1; - if (length > GUAC_COMMON_SSH_SFTP_MAX_PATH) { - guac_client_log(client, GUAC_LOG_ERROR, - "Submitted path exceeds limit of %i bytes", - GUAC_COMMON_SSH_SFTP_MAX_PATH); - return; - } - - /* Copy path */ - memcpy(filesystem->upload_path, path, length); - guac_client_log(client, GUAC_LOG_DEBUG, "Upload path set to \"%s\"", path); - -} - -/** - * Handler for ack messages received due to receipt of a "body" or "blob" - * instruction associated with a SFTP directory list operation. - * - * @param user - * The user receiving the ack message. - * - * @param stream - * The Guacamole protocol stream associated with the received ack message. - * - * @param message - * An arbitrary human-readable message describing the nature of the - * success or failure denoted by this ack message. - * - * @param status - * The status code associated with this ack message, which may indicate - * success or an error. - * - * @return - * Zero on success, non-zero on error. - */ -static int guac_common_ssh_sftp_ls_ack_handler(guac_user* user, - guac_stream* stream, char* message, guac_protocol_status status) { - - int bytes_read; - int blob_written = 0; - - char filename[GUAC_COMMON_SSH_SFTP_MAX_PATH]; - LIBSSH2_SFTP_ATTRIBUTES attributes; - - guac_common_ssh_sftp_ls_state* list_state = - (guac_common_ssh_sftp_ls_state*) stream->data; - - guac_common_ssh_sftp_filesystem* filesystem = list_state->filesystem; - - LIBSSH2_SFTP* sftp = filesystem->sftp_session; - - /* If unsuccessful, free stream and abort */ - if (status != GUAC_PROTOCOL_STATUS_SUCCESS) { - libssh2_sftp_closedir(list_state->directory); - guac_user_free_stream(user, stream); - free(list_state); - return 0; - } - - /* While directory entries remain */ - while ((bytes_read = libssh2_sftp_readdir(list_state->directory, - filename, sizeof(filename), &attributes)) > 0 - && !blob_written) { - - char absolute_path[GUAC_COMMON_SSH_SFTP_MAX_PATH]; - - /* Skip current and parent directory entries */ - if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) - continue; - - /* Concatenate into absolute path - skip if invalid */ - if (!guac_ssh_append_filename(absolute_path, - list_state->directory_name, filename)) { - - guac_user_log(user, GUAC_LOG_DEBUG, - "Skipping filename \"%s\" - filename is invalid or " - "resulting path is too long", filename); - - continue; - } - - /* Stat explicitly if symbolic link (might point to directory) */ - if (LIBSSH2_SFTP_S_ISLNK(attributes.permissions)) - libssh2_sftp_stat(sftp, absolute_path, &attributes); - - /* Determine mimetype */ - const char* mimetype; - if (LIBSSH2_SFTP_S_ISDIR(attributes.permissions)) - mimetype = GUAC_USER_STREAM_INDEX_MIMETYPE; - else - mimetype = "application/octet-stream"; - - /* Write entry */ - blob_written |= guac_common_json_write_property(user, stream, - &list_state->json_state, absolute_path, mimetype); - - } - - /* Complete JSON and cleanup at end of directory */ - if (bytes_read <= 0) { - - /* Complete JSON object */ - guac_common_json_end_object(user, stream, &list_state->json_state); - guac_common_json_flush(user, stream, &list_state->json_state); - - /* Clean up resources */ - libssh2_sftp_closedir(list_state->directory); - free(list_state); - - /* Signal of stream */ - guac_protocol_send_end(user->socket, stream); - guac_user_free_stream(user, stream); - - } - - guac_socket_flush(user->socket); - return 0; - -} - -/** - * Handler for get messages. In context of SFTP and the filesystem exposed via - * the Guacamole protocol, get messages request the body of a file within the - * filesystem. - * - * @param user - * The user who sent the get message. - * - * @param object - * The Guacamole protocol object associated with the get request itself. - * - * @param name - * The name of the input stream (file) being requested. - * - * @return - * Zero on success, non-zero on error. - */ -static int guac_common_ssh_sftp_get_handler(guac_user* user, - guac_object* object, char* name) { - - guac_common_ssh_sftp_filesystem* filesystem = - (guac_common_ssh_sftp_filesystem*) object->data; - - LIBSSH2_SFTP* sftp = filesystem->sftp_session; - LIBSSH2_SFTP_ATTRIBUTES attributes; - - /* Attempt to read file information */ - if (libssh2_sftp_stat(sftp, name, &attributes)) { - guac_user_log(user, GUAC_LOG_INFO, "Unable to read file \"%s\"", - name); - return 0; - } - - /* If directory, send contents of directory */ - if (LIBSSH2_SFTP_S_ISDIR(attributes.permissions)) { - - /* Open as directory */ - LIBSSH2_SFTP_HANDLE* dir = libssh2_sftp_opendir(sftp, name); - if (dir == NULL) { - guac_user_log(user, GUAC_LOG_INFO, - "Unable to read directory \"%s\"", name); - return 0; - } - - /* Init directory listing state */ - guac_common_ssh_sftp_ls_state* list_state = - malloc(sizeof(guac_common_ssh_sftp_ls_state)); - - list_state->directory = dir; - list_state->filesystem = filesystem; - strncpy(list_state->directory_name, name, - sizeof(list_state->directory_name) - 1); - - /* Allocate stream for body */ - guac_stream* stream = guac_user_alloc_stream(user); - stream->ack_handler = guac_common_ssh_sftp_ls_ack_handler; - stream->data = list_state; - - /* Init JSON object state */ - guac_common_json_begin_object(user, stream, &list_state->json_state); - - /* Associate new stream with get request */ - guac_protocol_send_body(user->socket, object, stream, - GUAC_USER_STREAM_INDEX_MIMETYPE, name); - - } - - /* Otherwise, send file contents */ - else { - - /* Open as normal file */ - LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, name, - LIBSSH2_FXF_READ, 0); - if (file == NULL) { - guac_user_log(user, GUAC_LOG_INFO, - "Unable to read file \"%s\"", name); - return 0; - } - - /* Allocate stream for body */ - guac_stream* stream = guac_user_alloc_stream(user); - stream->ack_handler = guac_common_ssh_sftp_ack_handler; - stream->data = file; - - /* Associate new stream with get request */ - guac_protocol_send_body(user->socket, object, stream, - "application/octet-stream", name); - - } - - guac_socket_flush(user->socket); - return 0; -} - -/** - * Handler for put messages. In context of SFTP and the filesystem exposed via - * the Guacamole protocol, put messages request write access to a file within - * the filesystem. - * - * @param user - * The user who sent the put message. - * - * @param object - * The Guacamole protocol object associated with the put request itself. - * - * @param stream - * The Guacamole protocol stream along which the user will be sending - * file data. - * - * @param mimetype - * The mimetype of the data being send along the stream. - * - * @param name - * The name of the input stream (file) being requested. - * - * @return - * Zero on success, non-zero on error. - */ -static int guac_common_ssh_sftp_put_handler(guac_user* user, - guac_object* object, guac_stream* stream, char* mimetype, char* name) { - - guac_common_ssh_sftp_filesystem* filesystem = - (guac_common_ssh_sftp_filesystem*) object->data; - - LIBSSH2_SFTP* sftp = filesystem->sftp_session; - - /* Open file via SFTP */ - LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, name, - LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, - S_IRUSR | S_IWUSR); - - /* Acknowledge stream if successful */ - if (file != NULL) { - guac_user_log(user, GUAC_LOG_DEBUG, "File \"%s\" opened", name); - guac_protocol_send_ack(user->socket, stream, "SFTP: File opened", - GUAC_PROTOCOL_STATUS_SUCCESS); - } - - /* Abort on failure */ - else { - guac_user_log(user, GUAC_LOG_INFO, - "Unable to open file \"%s\"", name); - guac_protocol_send_ack(user->socket, stream, "SFTP: Open failed", - guac_sftp_get_status(filesystem)); - } - - /* Set handlers for file stream */ - stream->blob_handler = guac_common_ssh_sftp_blob_handler; - stream->end_handler = guac_common_ssh_sftp_end_handler; - - /* Store file within stream */ - stream->data = file; - - guac_socket_flush(user->socket); - return 0; -} - -void* guac_common_ssh_expose_sftp_filesystem(guac_user* user, void* data) { - - guac_common_ssh_sftp_filesystem* filesystem = - (guac_common_ssh_sftp_filesystem*) data; - - /* No need to expose if there is no filesystem or the user has left */ - if (user == NULL || filesystem == NULL) - return NULL; - - /* Allocate and expose filesystem object for user */ - return guac_common_ssh_alloc_sftp_filesystem_object(filesystem, user); - -} - -guac_object* guac_common_ssh_alloc_sftp_filesystem_object( - guac_common_ssh_sftp_filesystem* filesystem, guac_user* user) { - - /* Init filesystem */ - guac_object* fs_object = guac_user_alloc_object(user); - fs_object->get_handler = guac_common_ssh_sftp_get_handler; - fs_object->put_handler = guac_common_ssh_sftp_put_handler; - fs_object->data = filesystem; - - /* Send filesystem to user */ - guac_protocol_send_filesystem(user->socket, fs_object, filesystem->name); - guac_socket_flush(user->socket); - - return fs_object; - -} - -guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem( - guac_common_ssh_session* session, const char* name) { - - /* Request SFTP */ - LIBSSH2_SFTP* sftp_session = libssh2_sftp_init(session->session); - if (sftp_session == NULL) - return NULL; - - /* Allocate data for SFTP session */ - guac_common_ssh_sftp_filesystem* filesystem = - malloc(sizeof(guac_common_ssh_sftp_filesystem)); - - /* Associate SSH session with SFTP data and user */ - filesystem->name = strdup(name); - filesystem->ssh_session = session; - filesystem->sftp_session = sftp_session; - - /* Initially upload files to current directory */ - strcpy(filesystem->upload_path, "."); - - /* Return allocated filesystem */ - return filesystem; - -} - -void guac_common_ssh_destroy_sftp_filesystem( - guac_common_ssh_sftp_filesystem* filesystem) { - - /* Shutdown SFTP session */ - libssh2_sftp_shutdown(filesystem->sftp_session); - - /* Free associated memory */ - free(filesystem->name); - free(filesystem); - -} - http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/guac_sftp.h ---------------------------------------------------------------------- diff --git a/src/common-ssh/guac_sftp.h b/src/common-ssh/guac_sftp.h deleted file mode 100644 index fdc1e72..0000000 --- a/src/common-ssh/guac_sftp.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifndef GUAC_COMMON_SSH_SFTP_H -#define GUAC_COMMON_SSH_SFTP_H - -#include "common/json.h" -#include "guac_ssh.h" - -#include <guacamole/object.h> -#include <guacamole/user.h> -#include <libssh2.h> -#include <libssh2_sftp.h> - -/** - * Maximum number of bytes per path. - */ -#define GUAC_COMMON_SSH_SFTP_MAX_PATH 2048 - -/** - * Representation of an SFTP-driven filesystem object. Unlike guac_object, this - * structure is not tied to any particular user. - */ -typedef struct guac_common_ssh_sftp_filesystem { - - /** - * The human-readable display name of this filesystem. - */ - char* name; - - /** - * The distinct SSH session used for SFTP. - */ - guac_common_ssh_session* ssh_session; - - /** - * SFTP session, used for file transfers. - */ - LIBSSH2_SFTP* sftp_session; - - /** - * The path files will be sent to, if uploaded directly via a "file" - * instruction. - */ - char upload_path[GUAC_COMMON_SSH_SFTP_MAX_PATH]; - -} guac_common_ssh_sftp_filesystem; - -/** - * The current state of a directory listing operation. - */ -typedef struct guac_common_ssh_sftp_ls_state { - - /** - * The SFTP filesystem being listed. - */ - guac_common_ssh_sftp_filesystem* filesystem; - - /** - * Reference to the directory currently being listed over SFTP. This - * directory must already be open from a call to libssh2_sftp_opendir(). - */ - LIBSSH2_SFTP_HANDLE* directory; - - /** - * The absolute path of the directory being listed. - */ - char directory_name[GUAC_COMMON_SSH_SFTP_MAX_PATH]; - - /** - * The current state of the JSON directory object being written. - */ - guac_common_json_state json_state; - -} guac_common_ssh_sftp_ls_state; - -/** - * Creates a new Guacamole filesystem object which provides access to files - * and directories via SFTP using the given SSH session. When the filesystem - * will no longer be used, it must be explicitly destroyed with - * guac_common_ssh_destroy_sftp_filesystem(). The resulting object is not - * automatically exposed to users of the connection - filesystem operations - * must be mediated either through various handlers or through exposing a - * filesystem guac_object via guac_common_ssh_alloc_sftp_filesystem_object(). - * - * @param session - * The session to use to provide SFTP. This session will automatically be - * destroyed when this filesystem is destroyed. - * - * @param name - * The name to send as the name of the filesystem whenever it is exposed - * to a user. - * - * @return - * A new SFTP filesystem object, not yet exposed to users. - */ -guac_common_ssh_sftp_filesystem* guac_common_ssh_create_sftp_filesystem( - guac_common_ssh_session* session, const char* name); - -/** - * Destroys the given filesystem object, disconnecting from SFTP and freeing - * and associated resources. Any associated session or user objects must be - * explicitly destroyed. - * - * @param filesystem - * The filesystem object to destroy. - */ -void guac_common_ssh_destroy_sftp_filesystem( - guac_common_ssh_sftp_filesystem* filesystem); - -/** - * Creates and exposes a new filesystem guac_object to the given user, - * providing access to the files within the given SFTP filesystem. The - * allocated guac_object must eventually be freed via guac_user_free_object(). - * - * @param filesystem - * The filesystem object to expose. - * - * @param user - * The user that the SFTP filesystem should be exposed to. - * - * @return - * A new Guacamole filesystem object, configured to use SFTP for uploading - * and downloading files. - */ -guac_object* guac_common_ssh_alloc_sftp_filesystem_object( - guac_common_ssh_sftp_filesystem* filesystem, guac_user* user); - -/** - * Allocates a new filesystem guac_object for the given user, returning the - * resulting guac_object. This function is provided for convenience, as it is - * can be used as the callback for guac_client_foreach_user() or - * guac_client_for_owner(). Note that this guac_object will be tracked - * internally by libguac, will be provided to us in the parameters of handlers - * related to that guac_object, and will automatically be freed when the - * associated guac_user is freed, so the return value of this function can - * safely be ignored. - * - * If either the given user or the given filesystem are NULL, then this - * function has no effect. - * - * @param user - * The use to expose the filesystem to, or NULL if nothing should be - * exposed. - * - * @param data - * A pointer to the guac_common_ssh_sftp_filesystem instance to expose - * to the given user, or NULL if nothing should be exposed. - * - * @return - * The guac_object allocated for the newly-exposed filesystem, or NULL if - * no filesystem object could be allocated. - */ -void* guac_common_ssh_expose_sftp_filesystem(guac_user* user, void* data); - -/** - * Initiates an SFTP file download to the user via the Guacamole "file" - * instruction. The download will be automatically monitored and continued - * after this function terminates in response to "ack" instructions received by - * the user. - * - * @param filesystem - * The filesystem containing the file to be downloaded. - * - * @param user - * The user that should receive the file (via a "file" instruction). - * - * @param filename - * The filename of the file to download, relative to the given filesystem. - * - * @return - * The file stream created for the file download, already configured to - * properly handle "ack" responses, etc. from the user. - */ -guac_stream* guac_common_ssh_sftp_download_file( - guac_common_ssh_sftp_filesystem* filesystem, guac_user* user, - char* filename); - -/** - * Handles an incoming stream from a Guacamole "file" instruction, saving the - * contents of that stream to the file having the given name within the - * upload directory set by guac_common_ssh_sftp_set_upload_path(). - * - * @param filesystem - * The filesystem that should receive the uploaded file. - * - * @param user - * The user who is attempting to open the file stream (the user that sent - * the "file" instruction). - * - * @param stream - * The stream through which the uploaded file data will be received. - * - * @param mimetype - * The mimetype of the data being received. - * - * @param filename - * The filename of the file to write to. This filename will always be taken - * relative to the upload path set by - * guac_common_ssh_sftp_set_upload_path(). - * - * @return - * Zero if the incoming stream has been handled successfully, non-zero on - * failure. - */ -int guac_common_ssh_sftp_handle_file_stream( - guac_common_ssh_sftp_filesystem* filesystem, guac_user* user, - guac_stream* stream, char* mimetype, char* filename); - -/** - * Set the destination directory for future uploads submitted via - * guac_common_ssh_sftp_handle_file_stream(). This function has no bearing - * on the destination directories of files uploaded with "put" instructions. - * - * @param filesystem - * The filesystem to set the upload path of. - * - * @param path - * The path to use for future uploads submitted via the - * guac_common_ssh_sftp_handle_file_stream() function. - */ -void guac_common_ssh_sftp_set_upload_path( - guac_common_ssh_sftp_filesystem* filesystem, const char* path); - -#endif - http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/guac_ssh.c ---------------------------------------------------------------------- diff --git a/src/common-ssh/guac_ssh.c b/src/common-ssh/guac_ssh.c deleted file mode 100644 index 488eea2..0000000 --- a/src/common-ssh/guac_ssh.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#include "guac_ssh.h" -#include "guac_ssh_key.h" -#include "guac_ssh_user.h" - -#include <guacamole/client.h> -#include <libssh2.h> - -#ifdef LIBSSH2_USES_GCRYPT -#include <gcrypt.h> -#endif - -#include <openssl/err.h> -#include <openssl/ssl.h> - -#include <errno.h> -#include <netdb.h> -#include <netinet/in.h> -#include <pthread.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <sys/socket.h> -#include <unistd.h> - -#ifdef LIBSSH2_USES_GCRYPT -GCRY_THREAD_OPTION_PTHREAD_IMPL; -#endif - -/** - * Array of mutexes, used by OpenSSL. - */ -static pthread_mutex_t* guac_common_ssh_openssl_locks = NULL; - -/** - * Called by OpenSSL when locking or unlocking the Nth mutex. - * - * @param mode - * A bitmask denoting the action to be taken on the Nth lock, such as - * CRYPTO_LOCK or CRYPTO_UNLOCK. - * - * @param n - * The index of the lock to lock or unlock. - * - * @param file - * The filename of the function setting the lock, for debugging purposes. - * - * @param line - * The line number of the function setting the lock, for debugging - * purposes. - */ -static void guac_common_ssh_openssl_locking_callback(int mode, int n, - const char* file, int line){ - - /* Lock given mutex upon request */ - if (mode & CRYPTO_LOCK) - pthread_mutex_lock(&(guac_common_ssh_openssl_locks[n])); - - /* Unlock given mutex upon request */ - else if (mode & CRYPTO_UNLOCK) - pthread_mutex_unlock(&(guac_common_ssh_openssl_locks[n])); - -} - -/** - * Called by OpenSSL when determining the current thread ID. - * - * @return - * An ID which uniquely identifies the current thread. - */ -static unsigned long guac_common_ssh_openssl_id_callback() { - return (unsigned long) pthread_self(); -} - -/** - * Creates the given number of mutexes, such that OpenSSL will have at least - * this number of mutexes at its disposal. - * - * @param count - * The number of mutexes (locks) to create. - */ -static void guac_common_ssh_openssl_init_locks(int count) { - - int i; - - /* Allocate required number of locks */ - guac_common_ssh_openssl_locks = - malloc(sizeof(pthread_mutex_t) * count); - - /* Initialize each lock */ - for (i=0; i < count; i++) - pthread_mutex_init(&(guac_common_ssh_openssl_locks[i]), NULL); - -} - -/** - * Frees the given number of mutexes. - * - * @param count - * The number of mutexes (locks) to free. - */ -static void guac_common_ssh_openssl_free_locks(int count) { - - int i; - - /* SSL lock array was not initialized */ - if (guac_common_ssh_openssl_locks == NULL) - return; - - /* Free all locks */ - for (i=0; i < count; i++) - pthread_mutex_destroy(&(guac_common_ssh_openssl_locks[i])); - - /* Free lock array */ - free(guac_common_ssh_openssl_locks); - -} - -int guac_common_ssh_init(guac_client* client) { - -#ifdef LIBSSH2_USES_GCRYPT - /* Init threadsafety in libgcrypt */ - gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); - if (!gcry_check_version(GCRYPT_VERSION)) { - guac_client_log(client, GUAC_LOG_ERROR, "libgcrypt version mismatch."); - return 1; - } -#endif - - /* Init threadsafety in OpenSSL */ - guac_common_ssh_openssl_init_locks(CRYPTO_num_locks()); - CRYPTO_set_id_callback(guac_common_ssh_openssl_id_callback); - CRYPTO_set_locking_callback(guac_common_ssh_openssl_locking_callback); - - /* Init OpenSSL */ - SSL_library_init(); - ERR_load_crypto_strings(); - - /* Init libssh2 */ - libssh2_init(0); - - /* Success */ - return 0; - -} - -void guac_common_ssh_uninit() { - guac_common_ssh_openssl_free_locks(CRYPTO_num_locks()); -} - -/** - * Callback invoked by libssh2 when libssh2_userauth_publickkey() is invoked. - * This callback must sign the given data, returning the signature as newly- - * allocated buffer space. - * - * @param session - * The SSH session for which the signature is being generated. - * - * @param sig - * A pointer to the buffer space containing the signature. This callback - * MUST allocate and assign this space. - * - * @param sig_len - * The length of the signature within the allocated buffer space, in bytes. - * This value must be set to the size of the signature after the signing - * operation completes. - * - * @param data - * The arbitrary data that must be signed. - * - * @param data_len - * The length of the arbitrary data to be signed, in bytes. - * - * @param abstract - * The value of the abstract parameter provided with the corresponding call - * to libssh2_userauth_publickey(). - * - * @return - * Zero on success, non-zero if the signing operation failed. - */ -static int guac_common_ssh_sign_callback(LIBSSH2_SESSION* session, - unsigned char** sig, size_t* sig_len, - const unsigned char* data, size_t data_len, void **abstract) { - - guac_common_ssh_key* key = (guac_common_ssh_key*) abstract; - int length; - - /* Allocate space for signature */ - *sig = malloc(4096); - - /* Sign with key */ - length = guac_common_ssh_key_sign(key, (const char*) data, data_len, *sig); - if (length < 0) - return 1; - - *sig_len = length; - return 0; -} - -/** - * Callback for the keyboard-interactive authentication method. Currently - * supports just one prompt for the password. This callback is invoked as - * needed to fullfill a call to libssh2_userauth_keyboard_interactive(). - * - * @param name - * An arbitrary name which should be printed to the terminal for the - * benefit of the user. This is currently ignored. - * - * @param name_len - * The length of the name string, in bytes. - * - * @param instruction - * Arbitrary instructions which should be printed to the terminal for the - * benefit of the user. This is currently ignored. - * - * @param instruction_len - * The length of the instruction string, in bytes. - * - * @param num_prompts - * The number of keyboard-interactive prompts for which responses are - * requested. This callback currently only supports one prompt, and assumes - * that this prompt is requesting the password. - * - * @param prompts - * An array of all keyboard-interactive prompts for which responses are - * requested. - * - * @param responses - * A parallel array into which all prompt responses should be stored. Each - * entry within this array corresponds to the entry in the prompts array - * with the same index. - * - * @param abstract - * The value of the abstract parameter provided when the SSH session was - * created with libssh2_session_init_ex(). - */ -static void guac_common_ssh_kbd_callback(const char *name, int name_len, - const char *instruction, int instruction_len, int num_prompts, - const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, - LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, - void **abstract) { - - guac_common_ssh_session* common_session = - (guac_common_ssh_session*) *abstract; - - guac_client* client = common_session->client; - - /* Send password if only one prompt */ - if (num_prompts == 1) { - char* password = common_session->user->password; - responses[0].text = strdup(password); - responses[0].length = strlen(password); - } - - /* If more than one prompt, a single password is not enough */ - else - guac_client_log(client, GUAC_LOG_WARNING, - "Unsupported number of keyboard-interactive prompts: %i", - num_prompts); - -} - -/** - * Authenticates the user associated with the given session over SSH. All - * required credentials must already be present within the user object - * associated with the given session. - * - * @param session - * The session associated with the user to be authenticated. - * - * @return - * Zero if authentication succeeds, or non-zero if authentication has - * failed. - */ -static int guac_common_ssh_authenticate(guac_common_ssh_session* common_session) { - - guac_client* client = common_session->client; - guac_common_ssh_user* user = common_session->user; - LIBSSH2_SESSION* session = common_session->session; - - /* Get user credentials */ - char* username = user->username; - char* password = user->password; - guac_common_ssh_key* key = user->private_key; - - /* Validate username provided */ - if (username == NULL) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "SSH authentication requires a username."); - return 1; - } - - /* Get list of supported authentication methods */ - char* user_authlist = libssh2_userauth_list(session, username, - strlen(username)); - guac_client_log(client, GUAC_LOG_DEBUG, - "Supported authentication methods: %s", user_authlist); - - /* Authenticate with private key, if provided */ - if (key != NULL) { - - /* Check if public key auth is supported on the server */ - if (strstr(user_authlist, "publickey") == NULL) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Public key authentication is not supported by " - "the SSH server"); - return 1; - } - - /* Attempt public key auth */ - if (libssh2_userauth_publickey(session, username, - (unsigned char*) key->public_key, key->public_key_length, - guac_common_ssh_sign_callback, (void**) key)) { - - /* Abort on failure */ - char* error_message; - libssh2_session_last_error(session, &error_message, NULL, 0); - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Public key authentication failed: %s", error_message); - - return 1; - - } - - /* Private key authentication succeeded */ - return 0; - - } - - /* Authenticate with password, if provided */ - else if (password != NULL) { - - /* Check if password auth is supported on the server */ - if (strstr(user_authlist, "password") != NULL) { - - /* Attempt password authentication */ - if (libssh2_userauth_password(session, username, password)) { - - /* Abort on failure */ - char* error_message; - libssh2_session_last_error(session, &error_message, NULL, 0); - guac_client_abort(client, - GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Password authentication failed: %s", error_message); - - return 1; - } - - /* Password authentication succeeded */ - return 0; - - } - - /* Check if keyboard-interactive auth is supported on the server */ - if (strstr(user_authlist, "keyboard-interactive") != NULL) { - - /* Attempt keyboard-interactive auth using provided password */ - if (libssh2_userauth_keyboard_interactive(session, username, - &guac_common_ssh_kbd_callback)) { - - /* Abort on failure */ - char* error_message; - libssh2_session_last_error(session, &error_message, NULL, 0); - guac_client_abort(client, - GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Keyboard-interactive authentication failed: %s", - error_message); - - return 1; - } - - /* Keyboard-interactive authentication succeeded */ - return 0; - - } - - /* No known authentication types available */ - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "Password and keyboard-interactive authentication are not " - "supported by the SSH server"); - return 1; - - } - - /* No credentials provided */ - guac_client_abort(client, GUAC_PROTOCOL_STATUS_CLIENT_UNAUTHORIZED, - "SSH authentication requires either a private key or a password."); - return 1; - -} - -guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, - const char* hostname, const char* port, guac_common_ssh_user* user) { - - int retval; - - int fd; - struct addrinfo* addresses; - struct addrinfo* current_address; - - char connected_address[1024]; - char connected_port[64]; - - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP - }; - - /* Get socket */ - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd < 0) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, - "Unable to create socket: %s", strerror(errno)); - return NULL; - } - - /* Get addresses connection */ - if ((retval = getaddrinfo(hostname, port, &hints, &addresses))) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, - "Error parsing given address or port: %s", - gai_strerror(retval)); - close(fd); - return NULL; - } - - /* Attempt connection to each address until success */ - current_address = addresses; - while (current_address != NULL) { - - /* Resolve hostname */ - if ((retval = getnameinfo(current_address->ai_addr, - current_address->ai_addrlen, - connected_address, sizeof(connected_address), - connected_port, sizeof(connected_port), - NI_NUMERICHOST | NI_NUMERICSERV))) - guac_client_log(client, GUAC_LOG_DEBUG, - "Unable to resolve host: %s", gai_strerror(retval)); - - /* Connect */ - if (connect(fd, current_address->ai_addr, - current_address->ai_addrlen) == 0) { - - guac_client_log(client, GUAC_LOG_DEBUG, - "Successfully connected to host %s, port %s", - connected_address, connected_port); - - /* Done if successful connect */ - break; - - } - - /* Otherwise log information regarding bind failure */ - else - guac_client_log(client, GUAC_LOG_DEBUG, "Unable to connect to " - "host %s, port %s: %s", - connected_address, connected_port, strerror(errno)); - - current_address = current_address->ai_next; - - } - - /* Free addrinfo */ - freeaddrinfo(addresses); - - /* If unable to connect to anything, fail */ - if (current_address == NULL) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, - "Unable to connect to any addresses."); - close(fd); - return NULL; - } - - /* Allocate new session */ - guac_common_ssh_session* common_session = - malloc(sizeof(guac_common_ssh_session)); - - /* Open SSH session */ - LIBSSH2_SESSION* session = libssh2_session_init_ex(NULL, NULL, - NULL, common_session); - if (session == NULL) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, - "Session allocation failed."); - free(common_session); - close(fd); - return NULL; - } - - /* Perform handshake */ - if (libssh2_session_handshake(session, fd)) { - guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, - "SSH handshake failed."); - free(common_session); - close(fd); - return NULL; - } - - /* Store basic session data */ - common_session->client = client; - common_session->user = user; - common_session->session = session; - common_session->fd = fd; - - /* Attempt authentication */ - if (guac_common_ssh_authenticate(common_session)) { - free(common_session); - close(fd); - return NULL; - } - - /* Return created session */ - return common_session; - -} - -void guac_common_ssh_destroy_session(guac_common_ssh_session* session) { - - /* Disconnect and clean up libssh2 */ - libssh2_session_disconnect(session->session, "Bye"); - libssh2_session_free(session->session); - - /* Free all other data */ - free(session); - -} - http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/guac_ssh.h ---------------------------------------------------------------------- diff --git a/src/common-ssh/guac_ssh.h b/src/common-ssh/guac_ssh.h deleted file mode 100644 index 36ad176..0000000 --- a/src/common-ssh/guac_ssh.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -#ifndef GUAC_COMMON_SSH_H -#define GUAC_COMMON_SSH_H - -#include "guac_ssh_user.h" - -#include <guacamole/client.h> -#include <libssh2.h> - -/** - * An SSH session, backed by libssh2 and associated with a particular - * Guacamole client. - */ -typedef struct guac_common_ssh_session { - - /** - * The Guacamole client using this SSH session. - */ - guac_client* client; - - /** - * The user that will be authenticating via SSH. - */ - guac_common_ssh_user* user; - - /** - * The underlying SSH session from libssh2. - */ - LIBSSH2_SESSION* session; - - /** - * The file descriptor of the socket being used for the SSH connection. - */ - int fd; - -} guac_common_ssh_session; - -/** - * Initializes the underlying SSH and encryption libraries used by Guacamole. - * This function must be called before any other guac_common_ssh_*() functions - * are called. - * - * @param client - * The Guacamole client that will be using SSH. - * - * @return - * Zero if initialization, or non-zero if an error occurs. - */ -int guac_common_ssh_init(guac_client* client); - -/** - * Cleans up the underlying SSH and encryption libraries used by Guacamole. - * This function must be called once no other guac_common_ssh_*() functions - * will be used. - */ -void guac_common_ssh_uninit(); - -/** - * Connects to the SSH server running at the given hostname and port, and - * authenticates as the given user. If an error occurs while connecting or - * authenticating, the Guacamole client will automatically and fatally abort. - * The user object provided must eventually be explicitly destroyed, but should - * not be destroyed until this session is destroyed, assuming the session is - * successfully created. - * - * @param client - * The Guacamole client that will be using SSH. - * - * @param hostname - * The hostname of the SSH server to connect to. - * - * @param port - * The port to connect to on the given hostname. - * - * @param user - * The user to authenticate as, once connected. - * - * @return - * A new SSH session if the connection and authentication succeed, or NULL - * if the connection or authentication were not successful. - */ -guac_common_ssh_session* guac_common_ssh_create_session(guac_client* client, - const char* hostname, const char* port, guac_common_ssh_user* user); - -/** - * Disconnects and destroys the given SSH session, freeing all associated - * resources. Any associated user must be explicitly destroyed, and will not - * be destroyed automatically. - * - * @param session - * The SSH session to destroy. - */ -void guac_common_ssh_destroy_session(guac_common_ssh_session* session); - -#endif -
