diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index 77ecb4f3..13f1b812 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -479,6 +479,8 @@ LIBSSH_API int ssh_channel_request_exec(ssh_channel channel, const char *cmd); LIBSSH_API int ssh_channel_request_pty(ssh_channel channel); LIBSSH_API int ssh_channel_request_pty_size(ssh_channel channel, const char *term, int cols, int rows); +LIBSSH_API int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *term, + int cols, int rows, const unsigned char* modes, size_t modes_len); LIBSSH_API int ssh_channel_request_shell(ssh_channel channel); LIBSSH_API int ssh_channel_request_send_signal(ssh_channel channel, const char *signum); LIBSSH_API int ssh_channel_request_send_break(ssh_channel channel, uint32_t length); diff --git a/include/libssh/libsshpp.hpp b/include/libssh/libsshpp.hpp index 602c7aec..e0f21e85 100644 --- a/include/libssh/libsshpp.hpp +++ b/include/libssh/libsshpp.hpp @@ -587,9 +587,12 @@ public: ssh_throw(err); return_throwable; } - void_throwable requestPty(const char *term=NULL, int cols=0, int rows=0){ + void_throwable requestPty(const char *term=NULL, int cols=0, int rows=0, + const unsigned char* modes=NULL, size_t modes_len=0){ int err; - if(term != NULL && cols != 0 && rows != 0) + if(term != NULL && cols != 0 && rows != 0 && modes != NULL) + err=ssh_channel_request_pty_size_modes(channel,term,cols,rows,modes,modes_len); + else if(term != NULL && cols != 0 && rows != 0) err=ssh_channel_request_pty_size(channel,term,cols,rows); else err=ssh_channel_request_pty(channel); diff --git a/src/channels.c b/src/channels.c index 4e3de321..ee3c8eca 100644 --- a/src/channels.c +++ b/src/channels.c @@ -1927,13 +1927,17 @@ error: * * @param[in] row The number of rows. * + * @param[in] modes Encoded SSH terminal modes for the PTY + * + * @param[in] modes_len Number of bytes in 'modes' + * * @return SSH_OK on success, * SSH_ERROR if an error occurred, * SSH_AGAIN if in nonblocking mode and call has * to be done again. */ -int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, - int col, int row) +int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *terminal, + int col, int row, const unsigned char* modes, size_t modes_len) { ssh_session session; ssh_buffer buffer = NULL; @@ -1963,14 +1967,14 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, } rc = ssh_buffer_pack(buffer, - "sdddddb", + "sdddddP", terminal, col, row, 0, /* pix */ 0, /* pix */ - 1, /* add a 0byte string */ - 0); + (uint32_t)modes_len, + modes_len, modes); if (rc != SSH_OK) { ssh_set_error_oom(session); @@ -1984,6 +1988,14 @@ error: return rc; } +int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal, + int col, int row) +{ + /* default modes/options: none */ + const unsigned char modes[1] = {0}; + return ssh_channel_request_pty_size_modes(channel, terminal, col, row, modes, sizeof(modes)); +} + /** * @brief Request a PTY. * diff --git a/src/libssh.map b/src/libssh.map index e0d310dd..558f921d 100644 --- a/src/libssh.map +++ b/src/libssh.map @@ -471,5 +471,6 @@ LIBSSH_AFTER_4_9_0 sftp_aio_wait_write; ssh_pki_export_privkey_base64_format; ssh_pki_export_privkey_file_format; + ssh_channel_request_pty_size_modes; } LIBSSH_4_9_0; diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt old mode 100644 new mode 100755 index 0e7aa288..adfc0e74 --- a/tests/client/CMakeLists.txt +++ b/tests/client/CMakeLists.txt @@ -17,6 +17,7 @@ set(LIBSSH_CLIENT_TESTS torture_proxycommand torture_session torture_request_env + torture_request_pty_modes torture_client_global_requests) find_program(SCP_EXECUTABLE NAMES scp) diff --git a/tests/client/torture_request_pty_modes.c b/tests/client/torture_request_pty_modes.c new file mode 100755 index 00000000..8004c52c --- /dev/null +++ b/tests/client/torture_request_pty_modes.c @@ -0,0 +1,145 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" + +#define LIBSSH_STATIC + +#include "torture.h" +#include + +#include +#include +#include + +static int sshd_setup(void **state) +{ + torture_setup_sshd_server(state, false); + + return 0; +} + +static int sshd_teardown(void **state) { + torture_teardown_sshd_server(state); + + return 0; +} + +static int session_setup(void **state) +{ + struct torture_state *s = *state; + struct passwd *pwd; + int rc; + + pwd = getpwnam("bob"); + assert_non_null(pwd); + + rc = setuid(pwd->pw_uid); + assert_return_code(rc, errno); + + s->ssh.session = torture_ssh_session(s, + TORTURE_SSH_SERVER, + NULL, + TORTURE_SSH_USER_ALICE, + NULL); + assert_non_null(s->ssh.session); + + return 0; +} + +static int session_teardown(void **state) +{ + struct torture_state *s = *state; + + ssh_disconnect(s->ssh.session); + ssh_free(s->ssh.session); + + return 0; +} + +static void torture_request_pty_modes_translate_ocrnl(void **state) +{ + const unsigned char modes[] = { + /* enable OCRNL */ + 73, 0, 0, 0, 1, + /* disable all other CR/NL handling */ + 34, 0, 0, 0, 0, + 35, 0, 0, 0, 0, + 36, 0, 0, 0, 0, + 72, 0, 0, 0, 0, + 74, 0, 0, 0, 0, + 75, 0, 0, 0, 0, + 0, /* TTY_OP_END */ + }; + struct torture_state *s = *state; + ssh_session session = s->ssh.session; + ssh_channel c; + char buffer[4096] = {0}; + int nbytes; + int rc; + int string_found = 0; + + c = ssh_channel_new(session); + assert_non_null(c); + + rc = ssh_channel_open_session(c); + assert_ssh_return_code(session, rc); + + rc = ssh_channel_request_pty_size_modes(c, "xterm", 80, 25, modes, sizeof(modes)); + assert_ssh_return_code(session, rc); + + rc = ssh_channel_request_exec(c, "echo -e '>TEST\\r\\n<'"); + assert_ssh_return_code(session, rc); + + nbytes = ssh_channel_read(c, buffer, sizeof(buffer) - 1, 0); + while (nbytes > 0) { + buffer[nbytes]='\0'; + /* expect 2 newline characters */ + if (strstr(buffer, ">TEST\n\n<") != NULL) { + string_found = 1; + break; + } + + nbytes = ssh_channel_read(c, buffer, sizeof(buffer), 0); + } + assert_int_equal(string_found, 1); + + ssh_channel_close(c); +} + +int torture_run_tests(void) { + int rc; + + struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(torture_request_pty_modes_translate_ocrnl, + session_setup, + session_teardown), + }; + + ssh_init(); + + torture_filter_tests(tests); + rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown); + + ssh_finalize(); + return rc; +} +