Repository: incubator-guacamole-server
Updated Branches:
  refs/heads/staging/0.9.12-incubating a808a6b17 -> 3ff832315


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/guac_ssh_buffer.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/guac_ssh_buffer.c b/src/common-ssh/guac_ssh_buffer.c
deleted file mode 100644
index b3d8d57..0000000
--- a/src/common-ssh/guac_ssh_buffer.c
+++ /dev/null
@@ -1,135 +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 "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/guac_ssh_buffer.h
----------------------------------------------------------------------
diff --git a/src/common-ssh/guac_ssh_buffer.h b/src/common-ssh/guac_ssh_buffer.h
deleted file mode 100644
index b42a2ac..0000000
--- a/src/common-ssh/guac_ssh_buffer.h
+++ /dev/null
@@ -1,134 +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_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/guac_ssh_key.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/guac_ssh_key.c b/src/common-ssh/guac_ssh_key.c
deleted file mode 100644
index 7594b84..0000000
--- a/src/common-ssh/guac_ssh_key.c
+++ /dev/null
@@ -1,217 +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 "config.h"
-
-#include "guac_ssh_buffer.h"
-#include "guac_ssh_key.h"
-
-#include <openssl/bio.h>
-#include <openssl/bn.h>
-#include <openssl/dsa.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/obj_mac.h>
-#include <openssl/pem.h>
-#include <openssl/rsa.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length,
-        char* passphrase) {
-
-    guac_common_ssh_key* key;
-    BIO* key_bio;
-
-    char* public_key;
-    char* pos;
-
-    /* Create BIO for reading key from memory */
-    key_bio = BIO_new_mem_buf(data, length);
-
-    /* If RSA key, load RSA */
-    if (length > sizeof(SSH_RSA_KEY_HEADER)-1
-            && memcmp(SSH_RSA_KEY_HEADER, data,
-                      sizeof(SSH_RSA_KEY_HEADER)-1) == 0) {
-
-        RSA* rsa_key;
-
-        /* Read key */
-        rsa_key = PEM_read_bio_RSAPrivateKey(key_bio, NULL, NULL, passphrase);
-        if (rsa_key == NULL)
-            return NULL;
-
-        /* Allocate key */
-        key = malloc(sizeof(guac_common_ssh_key));
-        key->rsa = rsa_key;
-
-        /* Set type */
-        key->type = SSH_KEY_RSA;
-
-        /* Allocate space for public key */
-        public_key = malloc(4096);
-        pos = public_key;
-
-        /* Derive public key */
-        guac_common_ssh_buffer_write_string(&pos, "ssh-rsa", 
sizeof("ssh-rsa")-1);
-        guac_common_ssh_buffer_write_bignum(&pos, rsa_key->e);
-        guac_common_ssh_buffer_write_bignum(&pos, rsa_key->n);
-
-        /* Save public key to structure */
-        key->public_key = public_key;
-        key->public_key_length = pos - public_key;
-
-    }
-
-    /* If DSA key, load DSA */
-    else if (length > sizeof(SSH_DSA_KEY_HEADER)-1
-            && memcmp(SSH_DSA_KEY_HEADER, data,
-                      sizeof(SSH_DSA_KEY_HEADER)-1) == 0) {
-
-        DSA* dsa_key;
-
-        /* Read key */
-        dsa_key = PEM_read_bio_DSAPrivateKey(key_bio, NULL, NULL, passphrase);
-        if (dsa_key == NULL)
-            return NULL;
-
-        /* Allocate key */
-        key = malloc(sizeof(guac_common_ssh_key));
-        key->dsa = dsa_key;
-
-        /* Set type */
-        key->type = SSH_KEY_DSA;
-
-        /* Allocate space for public key */
-        public_key = malloc(4096);
-        pos = public_key;
-
-        /* Derive public key */
-        guac_common_ssh_buffer_write_string(&pos, "ssh-dss", 
sizeof("ssh-dss")-1);
-        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->p);
-        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->q);
-        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->g);
-        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->pub_key);
-
-        /* Save public key to structure */
-        key->public_key = public_key;
-        key->public_key_length = pos - public_key;
-
-    }
-
-    /* Otherwise, unsupported type */
-    else {
-        BIO_free(key_bio);
-        return NULL;
-    }
-
-    /* Copy private key to structure */
-    key->private_key_length = length;
-    key->private_key = malloc(length);
-    memcpy(key->private_key, data, length);
-
-    BIO_free(key_bio);
-    return key;
-
-}
-
-const char* guac_common_ssh_key_error() {
-
-    /* Return static error string */
-    return ERR_reason_error_string(ERR_get_error());
-
-}
-
-void guac_common_ssh_key_free(guac_common_ssh_key* key) {
-
-    /* Free key-specific data */
-    if (key->type == SSH_KEY_RSA)
-        RSA_free(key->rsa);
-    else if (key->type == SSH_KEY_DSA)
-        DSA_free(key->dsa);
-
-    free(key->private_key);
-    free(key->public_key);
-    free(key);
-}
-
-int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
-        int length, unsigned char* sig) {
-
-    const EVP_MD* md;
-    EVP_MD_CTX md_ctx;
-
-    unsigned char digest[EVP_MAX_MD_SIZE];
-    unsigned int dlen, len;
-
-    /* Get SHA1 digest */
-    if ((md = EVP_get_digestbynid(NID_sha1)) == NULL)
-        return -1;
-
-    /* Digest data */
-    EVP_DigestInit(&md_ctx, md);
-    EVP_DigestUpdate(&md_ctx, data, length);
-    EVP_DigestFinal(&md_ctx, digest, &dlen);
-
-    /* Sign with key */
-    switch (key->type) {
-
-        case SSH_KEY_RSA:
-            if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1)
-                return len;
-            break;
-
-        case SSH_KEY_DSA: {
-
-            DSA_SIG* dsa_sig = DSA_do_sign(digest, dlen, key->dsa);
-            if (dsa_sig != NULL) {
-
-                /* Compute size of each half of signature */
-                int rlen = BN_num_bytes(dsa_sig->r);
-                int slen = BN_num_bytes(dsa_sig->s);
-
-                /* Ensure each number is within the required size */
-                if (rlen > DSA_SIG_NUMBER_SIZE || slen > DSA_SIG_NUMBER_SIZE)
-                    return -1;
-
-                /* Init to all zeroes */
-                memset(sig, 0, DSA_SIG_SIZE);
-
-                /* Add R at the end of the first block of the signature */
-                BN_bn2bin(dsa_sig->r, sig + DSA_SIG_SIZE
-                                          - DSA_SIG_NUMBER_SIZE - rlen);
-
-                /* Add S at the end of the second block of the signature */
-                BN_bn2bin(dsa_sig->s, sig + DSA_SIG_SIZE - slen);
-
-                /* Done */
-                DSA_SIG_free(dsa_sig);
-                return DSA_SIG_SIZE;
-
-            }
-
-        }
-
-    }
-
-    return -1;
-
-}
-

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/guac_ssh_key.h
----------------------------------------------------------------------
diff --git a/src/common-ssh/guac_ssh_key.h b/src/common-ssh/guac_ssh_key.h
deleted file mode 100644
index 1754dea..0000000
--- a/src/common-ssh/guac_ssh_key.h
+++ /dev/null
@@ -1,170 +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_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/guac_ssh_user.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/guac_ssh_user.c b/src/common-ssh/guac_ssh_user.c
deleted file mode 100644
index 73319c0..0000000
--- a/src/common-ssh/guac_ssh_user.c
+++ /dev/null
@@ -1,82 +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_key.h"
-#include "guac_ssh_user.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-guac_common_ssh_user* guac_common_ssh_create_user(const char* username) {
-
-    guac_common_ssh_user* user = malloc(sizeof(guac_common_ssh_user));
-
-    /* Init user */
-    user->username = strdup(username);
-    user->password = NULL;
-    user->private_key = NULL;
-
-    return user;
-
-}
-
-void guac_common_ssh_destroy_user(guac_common_ssh_user* user) {
-
-    /* Free private key, if present */
-    if (user->private_key != NULL)
-        guac_common_ssh_key_free(user->private_key);
-
-    /* Free all other data */
-    free(user->password);
-    free(user->username);
-    free(user);
-
-}
-
-void guac_common_ssh_user_set_password(guac_common_ssh_user* user,
-        const char* password) {
-
-    /* Replace current password with given value */
-    free(user->password);
-    user->password = strdup(password);
-
-}
-
-int guac_common_ssh_user_import_key(guac_common_ssh_user* user,
-        char* private_key, char* passphrase) {
-
-    /* Free existing private key, if present */
-    if (user->private_key != NULL)
-        guac_common_ssh_key_free(user->private_key);
-
-    /* Attempt to read key without passphrase if none given */
-    if (passphrase == NULL)
-        user->private_key = guac_common_ssh_key_alloc(private_key,
-                strlen(private_key), "");
-
-    /* Otherwise, use provided passphrase */
-    else
-        user->private_key = guac_common_ssh_key_alloc(private_key,
-                strlen(private_key), passphrase);
-
-    /* Fail if key could not be read */
-    return user->private_key == NULL;
-
-}
-

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/guac_ssh_user.h
----------------------------------------------------------------------
diff --git a/src/common-ssh/guac_ssh_user.h b/src/common-ssh/guac_ssh_user.h
deleted file mode 100644
index eeaa460..0000000
--- a/src/common-ssh/guac_ssh_user.h
+++ /dev/null
@@ -1,108 +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_USER_H
-#define GUAC_COMMON_SSH_USER_H
-
-#include "guac_ssh_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/key.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/key.c b/src/common-ssh/key.c
new file mode 100644
index 0000000..82a3252
--- /dev/null
+++ b/src/common-ssh/key.c
@@ -0,0 +1,217 @@
+/*
+ * 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 "common-ssh/buffer.h"
+#include "common-ssh/key.h"
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/dsa.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+guac_common_ssh_key* guac_common_ssh_key_alloc(char* data, int length,
+        char* passphrase) {
+
+    guac_common_ssh_key* key;
+    BIO* key_bio;
+
+    char* public_key;
+    char* pos;
+
+    /* Create BIO for reading key from memory */
+    key_bio = BIO_new_mem_buf(data, length);
+
+    /* If RSA key, load RSA */
+    if (length > sizeof(SSH_RSA_KEY_HEADER)-1
+            && memcmp(SSH_RSA_KEY_HEADER, data,
+                      sizeof(SSH_RSA_KEY_HEADER)-1) == 0) {
+
+        RSA* rsa_key;
+
+        /* Read key */
+        rsa_key = PEM_read_bio_RSAPrivateKey(key_bio, NULL, NULL, passphrase);
+        if (rsa_key == NULL)
+            return NULL;
+
+        /* Allocate key */
+        key = malloc(sizeof(guac_common_ssh_key));
+        key->rsa = rsa_key;
+
+        /* Set type */
+        key->type = SSH_KEY_RSA;
+
+        /* Allocate space for public key */
+        public_key = malloc(4096);
+        pos = public_key;
+
+        /* Derive public key */
+        guac_common_ssh_buffer_write_string(&pos, "ssh-rsa", 
sizeof("ssh-rsa")-1);
+        guac_common_ssh_buffer_write_bignum(&pos, rsa_key->e);
+        guac_common_ssh_buffer_write_bignum(&pos, rsa_key->n);
+
+        /* Save public key to structure */
+        key->public_key = public_key;
+        key->public_key_length = pos - public_key;
+
+    }
+
+    /* If DSA key, load DSA */
+    else if (length > sizeof(SSH_DSA_KEY_HEADER)-1
+            && memcmp(SSH_DSA_KEY_HEADER, data,
+                      sizeof(SSH_DSA_KEY_HEADER)-1) == 0) {
+
+        DSA* dsa_key;
+
+        /* Read key */
+        dsa_key = PEM_read_bio_DSAPrivateKey(key_bio, NULL, NULL, passphrase);
+        if (dsa_key == NULL)
+            return NULL;
+
+        /* Allocate key */
+        key = malloc(sizeof(guac_common_ssh_key));
+        key->dsa = dsa_key;
+
+        /* Set type */
+        key->type = SSH_KEY_DSA;
+
+        /* Allocate space for public key */
+        public_key = malloc(4096);
+        pos = public_key;
+
+        /* Derive public key */
+        guac_common_ssh_buffer_write_string(&pos, "ssh-dss", 
sizeof("ssh-dss")-1);
+        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->p);
+        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->q);
+        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->g);
+        guac_common_ssh_buffer_write_bignum(&pos, dsa_key->pub_key);
+
+        /* Save public key to structure */
+        key->public_key = public_key;
+        key->public_key_length = pos - public_key;
+
+    }
+
+    /* Otherwise, unsupported type */
+    else {
+        BIO_free(key_bio);
+        return NULL;
+    }
+
+    /* Copy private key to structure */
+    key->private_key_length = length;
+    key->private_key = malloc(length);
+    memcpy(key->private_key, data, length);
+
+    BIO_free(key_bio);
+    return key;
+
+}
+
+const char* guac_common_ssh_key_error() {
+
+    /* Return static error string */
+    return ERR_reason_error_string(ERR_get_error());
+
+}
+
+void guac_common_ssh_key_free(guac_common_ssh_key* key) {
+
+    /* Free key-specific data */
+    if (key->type == SSH_KEY_RSA)
+        RSA_free(key->rsa);
+    else if (key->type == SSH_KEY_DSA)
+        DSA_free(key->dsa);
+
+    free(key->private_key);
+    free(key->public_key);
+    free(key);
+}
+
+int guac_common_ssh_key_sign(guac_common_ssh_key* key, const char* data,
+        int length, unsigned char* sig) {
+
+    const EVP_MD* md;
+    EVP_MD_CTX md_ctx;
+
+    unsigned char digest[EVP_MAX_MD_SIZE];
+    unsigned int dlen, len;
+
+    /* Get SHA1 digest */
+    if ((md = EVP_get_digestbynid(NID_sha1)) == NULL)
+        return -1;
+
+    /* Digest data */
+    EVP_DigestInit(&md_ctx, md);
+    EVP_DigestUpdate(&md_ctx, data, length);
+    EVP_DigestFinal(&md_ctx, digest, &dlen);
+
+    /* Sign with key */
+    switch (key->type) {
+
+        case SSH_KEY_RSA:
+            if (RSA_sign(NID_sha1, digest, dlen, sig, &len, key->rsa) == 1)
+                return len;
+            break;
+
+        case SSH_KEY_DSA: {
+
+            DSA_SIG* dsa_sig = DSA_do_sign(digest, dlen, key->dsa);
+            if (dsa_sig != NULL) {
+
+                /* Compute size of each half of signature */
+                int rlen = BN_num_bytes(dsa_sig->r);
+                int slen = BN_num_bytes(dsa_sig->s);
+
+                /* Ensure each number is within the required size */
+                if (rlen > DSA_SIG_NUMBER_SIZE || slen > DSA_SIG_NUMBER_SIZE)
+                    return -1;
+
+                /* Init to all zeroes */
+                memset(sig, 0, DSA_SIG_SIZE);
+
+                /* Add R at the end of the first block of the signature */
+                BN_bn2bin(dsa_sig->r, sig + DSA_SIG_SIZE
+                                          - DSA_SIG_NUMBER_SIZE - rlen);
+
+                /* Add S at the end of the second block of the signature */
+                BN_bn2bin(dsa_sig->s, sig + DSA_SIG_SIZE - slen);
+
+                /* Done */
+                DSA_SIG_free(dsa_sig);
+                return DSA_SIG_SIZE;
+
+            }
+
+        }
+
+    }
+
+    return -1;
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/common-ssh/sftp.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/sftp.c b/src/common-ssh/sftp.c
new file mode 100644
index 0000000..79db35e
--- /dev/null
+++ b/src/common-ssh/sftp.c
@@ -0,0 +1,783 @@
+/*
+ * 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 "common-ssh/sftp.h"
+#include "common-ssh/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/ssh.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/ssh.c b/src/common-ssh/ssh.c
new file mode 100644
index 0000000..9d3de19
--- /dev/null
+++ b/src/common-ssh/ssh.c
@@ -0,0 +1,544 @@
+/*
+ * 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 "common-ssh/key.h"
+#include "common-ssh/ssh.h"
+#include "common-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/user.c
----------------------------------------------------------------------
diff --git a/src/common-ssh/user.c b/src/common-ssh/user.c
new file mode 100644
index 0000000..92c8d96
--- /dev/null
+++ b/src/common-ssh/user.c
@@ -0,0 +1,82 @@
+/*
+ * 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 "common-ssh/key.h"
+#include "common-ssh/user.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+guac_common_ssh_user* guac_common_ssh_create_user(const char* username) {
+
+    guac_common_ssh_user* user = malloc(sizeof(guac_common_ssh_user));
+
+    /* Init user */
+    user->username = strdup(username);
+    user->password = NULL;
+    user->private_key = NULL;
+
+    return user;
+
+}
+
+void guac_common_ssh_destroy_user(guac_common_ssh_user* user) {
+
+    /* Free private key, if present */
+    if (user->private_key != NULL)
+        guac_common_ssh_key_free(user->private_key);
+
+    /* Free all other data */
+    free(user->password);
+    free(user->username);
+    free(user);
+
+}
+
+void guac_common_ssh_user_set_password(guac_common_ssh_user* user,
+        const char* password) {
+
+    /* Replace current password with given value */
+    free(user->password);
+    user->password = strdup(password);
+
+}
+
+int guac_common_ssh_user_import_key(guac_common_ssh_user* user,
+        char* private_key, char* passphrase) {
+
+    /* Free existing private key, if present */
+    if (user->private_key != NULL)
+        guac_common_ssh_key_free(user->private_key);
+
+    /* Attempt to read key without passphrase if none given */
+    if (passphrase == NULL)
+        user->private_key = guac_common_ssh_key_alloc(private_key,
+                strlen(private_key), "");
+
+    /* Otherwise, use provided passphrase */
+    else
+        user->private_key = guac_common_ssh_key_alloc(private_key,
+                strlen(private_key), passphrase);
+
+    /* Fail if key could not be read */
+    return user->private_key == NULL;
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/rdp/client.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c
index 2dcb401..d12efba 100644
--- a/src/protocols/rdp/client.c
+++ b/src/protocols/rdp/client.c
@@ -27,9 +27,9 @@
 #include "user.h"
 
 #ifdef ENABLE_COMMON_SSH
-#include <guac_sftp.h>
-#include <guac_ssh.h>
-#include <guac_ssh_user.h>
+#include "common-ssh/sftp.h"
+#include "common-ssh/ssh.h"
+#include "common-ssh/user.h"
 #endif
 
 #include <freerdp/cache/cache.h>

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/rdp/rdp.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c
index a48230a..8d49c83 100644
--- a/src/protocols/rdp/rdp.c
+++ b/src/protocols/rdp/rdp.c
@@ -40,9 +40,9 @@
 #include "rdp_svc.h"
 
 #ifdef ENABLE_COMMON_SSH
-#include <guac_sftp.h>
-#include <guac_ssh.h>
-#include <guac_ssh_user.h>
+#include "common-ssh/sftp.h"
+#include "common-ssh/ssh.h"
+#include "common-ssh/user.h"
 #endif
 
 #include <freerdp/cache/bitmap.h>

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/rdp/rdp.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/rdp.h b/src/protocols/rdp/rdp.h
index e3e3311..943155d 100644
--- a/src/protocols/rdp/rdp.h
+++ b/src/protocols/rdp/rdp.h
@@ -39,9 +39,9 @@
 #include <guacamole/client.h>
 
 #ifdef ENABLE_COMMON_SSH
-#include "guac_sftp.h"
-#include "guac_ssh.h"
-#include "guac_ssh_user.h"
+#include "common-ssh/sftp.h"
+#include "common-ssh/ssh.h"
+#include "common-ssh/user.h"
 #endif
 
 #include <pthread.h>

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/rdp/sftp.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/sftp.c b/src/protocols/rdp/sftp.c
index efda47d..ecfe35f 100644
--- a/src/protocols/rdp/sftp.c
+++ b/src/protocols/rdp/sftp.c
@@ -19,7 +19,7 @@
 
 #include "config.h"
 
-#include "guac_sftp.h"
+#include "common-ssh/sftp.h"
 #include "rdp.h"
 #include "sftp.h"
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/ssh/client.c
----------------------------------------------------------------------
diff --git a/src/protocols/ssh/client.c b/src/protocols/ssh/client.c
index 13dd1c8..2110ba9 100644
--- a/src/protocols/ssh/client.c
+++ b/src/protocols/ssh/client.c
@@ -20,7 +20,7 @@
 #include "config.h"
 
 #include "client.h"
-#include "guac_sftp.h"
+#include "common-ssh/sftp.h"
 #include "ssh.h"
 #include "terminal.h"
 #include "user.h"

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/ssh/sftp.c
----------------------------------------------------------------------
diff --git a/src/protocols/ssh/sftp.c b/src/protocols/ssh/sftp.c
index 9fddc30..f4173a6 100644
--- a/src/protocols/ssh/sftp.c
+++ b/src/protocols/ssh/sftp.c
@@ -19,7 +19,7 @@
 
 #include "config.h"
 
-#include "guac_sftp.h"
+#include "common-ssh/sftp.h"
 #include "sftp.h"
 #include "ssh.h"
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/ssh/ssh.c
----------------------------------------------------------------------
diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c
index 53835c8..dd0a30f 100644
--- a/src/protocols/ssh/ssh.c
+++ b/src/protocols/ssh/ssh.c
@@ -20,8 +20,8 @@
 #include "config.h"
 
 #include "common/recording.h"
-#include "guac_sftp.h"
-#include "guac_ssh.h"
+#include "common-ssh/sftp.h"
+#include "common-ssh/ssh.h"
 #include "settings.h"
 #include "sftp.h"
 #include "ssh.h"

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/ssh/ssh.h
----------------------------------------------------------------------
diff --git a/src/protocols/ssh/ssh.h b/src/protocols/ssh/ssh.h
index 44027ee..d6dbdaf 100644
--- a/src/protocols/ssh/ssh.h
+++ b/src/protocols/ssh/ssh.h
@@ -22,9 +22,9 @@
 
 #include "config.h"
 
-#include "guac_sftp.h"
-#include "guac_ssh.h"
-#include "guac_ssh_user.h"
+#include "common-ssh/sftp.h"
+#include "common-ssh/ssh.h"
+#include "common-ssh/user.h"
 #include "settings.h"
 #include "terminal.h"
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/vnc/client.c
----------------------------------------------------------------------
diff --git a/src/protocols/vnc/client.c b/src/protocols/vnc/client.c
index 516d93f..00b9015 100644
--- a/src/protocols/vnc/client.c
+++ b/src/protocols/vnc/client.c
@@ -24,9 +24,9 @@
 #include "vnc.h"
 
 #ifdef ENABLE_COMMON_SSH
-#include "guac_sftp.h"
-#include "guac_ssh.h"
-#include "sftp.h"
+#include "common-ssh/sftp.h"
+#include "common-ssh/ssh.h"
+#include "common-ssh/user.h"
 #endif
 
 #ifdef ENABLE_PULSE

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/vnc/sftp.c
----------------------------------------------------------------------
diff --git a/src/protocols/vnc/sftp.c b/src/protocols/vnc/sftp.c
index fc90668..e5e768a 100644
--- a/src/protocols/vnc/sftp.c
+++ b/src/protocols/vnc/sftp.c
@@ -19,7 +19,7 @@
 
 #include "config.h"
 
-#include "guac_sftp.h"
+#include "common-ssh/sftp.h"
 #include "sftp.h"
 #include "vnc.h"
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/vnc/vnc.c
----------------------------------------------------------------------
diff --git a/src/protocols/vnc/vnc.c b/src/protocols/vnc/vnc.c
index 9b0bd76..710372f 100644
--- a/src/protocols/vnc/vnc.c
+++ b/src/protocols/vnc/vnc.c
@@ -37,8 +37,8 @@
 #endif
 
 #ifdef ENABLE_COMMON_SSH
-#include "guac_sftp.h"
-#include "guac_ssh.h"
+#include "common-ssh/sftp.h"
+#include "common-ssh/ssh.h"
 #include "sftp.h"
 #endif
 

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d371f2d9/src/protocols/vnc/vnc.h
----------------------------------------------------------------------
diff --git a/src/protocols/vnc/vnc.h b/src/protocols/vnc/vnc.h
index 7ae8b72..0edbcd4 100644
--- a/src/protocols/vnc/vnc.h
+++ b/src/protocols/vnc/vnc.h
@@ -37,9 +37,9 @@
 #endif
 
 #ifdef ENABLE_COMMON_SSH
-#include "guac_sftp.h"
-#include "guac_ssh.h"
-#include "guac_ssh_user.h"
+#include "common-ssh/sftp.h"
+#include "common-ssh/ssh.h"
+#include "common-ssh/user.h"
 #endif
 
 #include <pthread.h>

Reply via email to