diff --git a/client/X11/xfreerdp.c b/client/X11/xfreerdp.c index 0b1ea9912..ae534cb12 100644 --- a/client/X11/xfreerdp.c +++ b/client/X11/xfreerdp.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -834,63 +835,15 @@ int main(int argc, char* argv[]) if (instance->settings->password == NULL) { - int status; - char* password; - int pipe_ends[2]; - struct termios term_flags; - const int password_size = 512; + const size_t password_size = 512; - password = xmalloc(password_size * sizeof(char)); + instance->settings->password = xmalloc(password_size * sizeof(char)); - printf("Password: "); - - /* Turn off ECHO on stdin, but still echo newlines */ - if (tcgetattr(fileno(stdin), &term_flags) != 0) + if(freerdp_passphrase_read("Password: ", instance->settings->password, password_size) == NULL) { - perror(strerror(errno)); + perror("xfreerdp"); exit(errno); } - - if (pipe(pipe_ends) != 0) - { - perror(strerror(errno)); - exit(errno); - } - - switch (fork()) - { - case -1: - perror(strerror(errno)); - exit(errno); - - case 0: - close(pipe_ends[0]); - term_flags.c_lflag &= ~ECHO; - term_flags.c_lflag |= ECHONL; - tcsetattr(fileno(stdin), TCSAFLUSH, &term_flags); - fgets(password, password_size - 1, stdin); - write(pipe_ends[1], password, strlen(password)); - close(pipe_ends[1]); - exit(EXIT_SUCCESS); - - default: - wait(&status); - if (tcsetattr(fileno(stdin), TCSADRAIN, &term_flags) != 0) - { - tcsetattr(fileno(stdin), TCSANOW, &term_flags); - perror(strerror(errno)); - exit(errno); - } - break; - } - - close(pipe_ends[1]); - read(pipe_ends[0], password, password_size); - close(pipe_ends[0]); - - *(password + strlen(password) - 1) = '\0'; - xfree(instance->settings->password); - instance->settings->password = password; } data = (struct thread_data*) xzalloc(sizeof(struct thread_data)); diff --git a/cunit/test_utils.c b/cunit/test_utils.c index 4b45058e4..395ff4d6d 100644 --- a/cunit/test_utils.c +++ b/cunit/test_utils.c @@ -2,7 +2,7 @@ * FreeRDP: A Remote Desktop Protocol Client * Utils Unit Tests * - * Copyright 2011 Vic Lee + * Copyright 2011 Vic Lee, 2011 Shea Levy * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,24 @@ * limitations under the License. */ +#define _XOPEN_SOURCE 700 #include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include #include +#include #include "test_utils.h" @@ -48,6 +57,7 @@ int add_utils_suite(void) add_test_function(load_plugin); add_test_function(wait_obj); add_test_function(args); + add_test_function(passphrase_read); return 0; } @@ -159,3 +169,463 @@ void test_args(void) } CU_ASSERT(i == 2); } + +void passphrase_read_prompts_to_tty() +{ + static const int read_nbyte = 11; + int masterfd; + char* slavedevice; + char read_buf[read_nbyte]; + fd_set fd_set_write; + + masterfd = posix_openpt(O_RDWR|O_NOCTTY); + + if (masterfd == -1 + || grantpt (masterfd) == -1 + || unlockpt (masterfd) == -1 + || (slavedevice = ptsname (masterfd)) == NULL) + CU_FAIL_FATAL("Could not create pty"); + + switch (fork()) + { + case -1: + CU_FAIL_FATAL("Could not fork"); + case 0: + { + static const int password_size = 512; + char buffer[password_size]; + int slavefd; + if (setsid() == (pid_t) -1) + CU_FAIL_FATAL("Could not create new session"); + + if ((slavefd = open(slavedevice, O_RDWR)) == 0) + CU_FAIL_FATAL("Could not open slave end of pty"); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + close(masterfd); + freerdp_passphrase_read("Password: ", buffer, password_size); + close(slavefd); + exit(EXIT_SUCCESS); + } + } + + read_buf[read_nbyte - 1] = '\0'; + + FD_ZERO(&fd_set_write); + FD_SET(masterfd, &fd_set_write); + if (select(masterfd + 1, NULL, &fd_set_write, NULL, NULL) == -1) + CU_FAIL_FATAL("Master end of pty not writeable"); + if (read(masterfd, read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to slave end of pty"); + CU_ASSERT_STRING_EQUAL(read_buf, "Password: "); + + write(masterfd, "\n", (size_t) 2); + close(masterfd); + return; +} + +void passphrase_read_reads_from_tty() +{ + static const int read_nbyte = 11; + int masterfd; + int pipe_ends[2]; + char* slavedevice; + char read_buf[read_nbyte]; + fd_set fd_set_write; + + masterfd = posix_openpt(O_RDWR|O_NOCTTY); + + if (masterfd == -1 + || grantpt (masterfd) == -1 + || unlockpt (masterfd) == -1 + || (slavedevice = ptsname (masterfd)) == NULL) + CU_FAIL_FATAL("Could not create pty"); + + if (pipe(pipe_ends) != 0) + CU_FAIL_FATAL("Could not create pipe"); + + switch (fork()) + { + case -1: + CU_FAIL_FATAL("Could not fork"); + case 0: + { + static const int password_size = 512; + char buffer[password_size]; + int slavefd; + if (setsid() == (pid_t) -1) + CU_FAIL_FATAL("Could not create new session"); + + if ((slavefd = open(slavedevice, O_RDWR)) == 0) + CU_FAIL_FATAL("Could not open slave end of pty"); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + close(masterfd); + close(pipe_ends[0]); + freerdp_passphrase_read("Password: ", buffer, password_size); + write(pipe_ends[1], buffer, password_size); + close(slavefd); + close(pipe_ends[1]); + exit(EXIT_SUCCESS); + } + } + + close(pipe_ends[1]); + read_buf[read_nbyte - 1] = '\0'; + + FD_ZERO(&fd_set_write); + FD_SET(masterfd, &fd_set_write); + if (select(masterfd + 1, NULL, &fd_set_write, NULL, NULL) == -1) + CU_FAIL_FATAL("Master end of pty not writeable"); + if (read(masterfd, read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to slave end of pty"); + + write(masterfd, "passw0rd\n", sizeof "passw0rd\n"); + if (read(pipe_ends[0], read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to pipe"); + CU_ASSERT_STRING_EQUAL(read_buf, "passw0rd"); + close(masterfd); + close(pipe_ends[0]); + return; +} + +void passphrase_read_turns_off_echo_during_read() +{ + static const int read_nbyte = 11; + int masterfd, slavefd; + char* slavedevice; + char read_buf[read_nbyte]; + fd_set fd_set_write; + struct termios term_flags; + + masterfd = posix_openpt(O_RDWR|O_NOCTTY); + + if (masterfd == -1 + || grantpt (masterfd) == -1 + || unlockpt (masterfd) == -1 + || (slavedevice = ptsname (masterfd)) == NULL) + CU_FAIL_FATAL("Could not create pty"); + + slavefd = open(slavedevice, O_RDWR|O_NOCTTY); + if (slavefd == -1) + CU_FAIL_FATAL("Could not open slave end of pty"); + + if (tcgetattr(slavefd, &term_flags) != 0) + CU_FAIL_FATAL("Could not get slave pty attributes"); + if (!(term_flags.c_lflag & ECHO)) + { + term_flags.c_lflag |= ECHO; + if (tcsetattr(slavefd, TCSANOW, &term_flags) != 0) + CU_FAIL_FATAL("Could not turn ECHO on on slave pty"); + } + + switch (fork()) + { + case -1: + CU_FAIL_FATAL("Could not fork"); + case 0: + { + static const int password_size = 512; + int child_slavefd; + char buffer[password_size]; + if (setsid() == (pid_t) -1) + CU_FAIL_FATAL("Could not create new session"); + + if ((child_slavefd = open(slavedevice, O_RDWR)) == 0) + CU_FAIL_FATAL("Could not open slave end of pty"); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + close(masterfd); + close(slavefd); + freerdp_passphrase_read("Password: ", buffer, password_size); + close(child_slavefd); + exit(EXIT_SUCCESS); + } + } + + read_buf[read_nbyte - 1] = '\0'; + + FD_ZERO(&fd_set_write); + FD_SET(masterfd, &fd_set_write); + if (select(masterfd + 1, NULL, &fd_set_write, NULL, NULL) == -1) + CU_FAIL_FATAL("Master end of pty not writeable"); + if (read(masterfd, read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to slave end of pty"); + + if (tcgetattr(slavefd, &term_flags) != 0) + CU_FAIL_FATAL("Could not get slave pty attributes"); + CU_ASSERT(!(term_flags.c_lflag & ECHO)) + write(masterfd, "\n", (size_t) 2); + close(masterfd); + close(slavefd); + return; +} + +void passphrase_read_resets_terminal_after_read() +{ + static const int read_nbyte = 11; + int masterfd, slavefd, status; + char* slavedevice; + char read_buf[read_nbyte]; + fd_set fd_set_write; + struct termios term_flags; + pid_t child; + + masterfd = posix_openpt(O_RDWR|O_NOCTTY); + + if (masterfd == -1 + || grantpt (masterfd) == -1 + || unlockpt (masterfd) == -1 + || (slavedevice = ptsname (masterfd)) == NULL) + CU_FAIL_FATAL("Could not create pty"); + + slavefd = open(slavedevice, O_RDWR|O_NOCTTY); + if (slavefd == -1) + CU_FAIL_FATAL("Could not open slave end of pty"); + + if (tcgetattr(slavefd, &term_flags) != 0) + CU_FAIL_FATAL("Could not get slave pty attributes"); + if (!(term_flags.c_lflag & ECHO)) + { + term_flags.c_lflag |= ECHO; + if (tcsetattr(slavefd, TCSANOW, &term_flags) != 0) + CU_FAIL_FATAL("Could not turn ECHO on on slave pty"); + } + + switch (child = fork()) + { + case -1: + CU_FAIL_FATAL("Could not fork"); + case 0: + { + static const int password_size = 512; + int child_slavefd; + char buffer[password_size]; + if (setsid() == (pid_t) -1) + CU_FAIL_FATAL("Could not create new session"); + + if ((child_slavefd = open(slavedevice, O_RDWR)) == 0) + CU_FAIL_FATAL("Could not open slave end of pty"); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + close(masterfd); + close(slavefd); + freerdp_passphrase_read("Password: ", buffer, password_size); + close(child_slavefd); + exit(EXIT_SUCCESS); + } + } + + read_buf[read_nbyte - 1] = '\0'; + + FD_ZERO(&fd_set_write); + FD_SET(masterfd, &fd_set_write); + if (select(masterfd + 1, NULL, &fd_set_write, NULL, NULL) == -1) + CU_FAIL_FATAL("Master end of pty not writeable"); + if (read(masterfd, read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to slave end of pty"); + + write(masterfd, "\n", (size_t) 2); + waitpid(child, &status, WUNTRACED); + if (tcgetattr(slavefd, &term_flags) != 0) + CU_FAIL_FATAL("Could not get slave pty attributes"); + CU_ASSERT(term_flags.c_lflag & ECHO) + close(masterfd); + close(slavefd); + return; +} + +void passphrase_read_turns_on_newline_echo_during_read() +{ + static const int read_nbyte = 11; + int masterfd, slavefd; + char* slavedevice; + char read_buf[read_nbyte]; + fd_set fd_set_write; + struct termios term_flags; + + masterfd = posix_openpt(O_RDWR|O_NOCTTY); + + if (masterfd == -1 + || grantpt (masterfd) == -1 + || unlockpt (masterfd) == -1 + || (slavedevice = ptsname (masterfd)) == NULL) + CU_FAIL_FATAL("Could not create pty"); + + slavefd = open(slavedevice, O_RDWR|O_NOCTTY); + if (slavefd == -1) + CU_FAIL_FATAL("Could not open slave end of pty"); + + if (tcgetattr(slavefd, &term_flags) != 0) + CU_FAIL_FATAL("Could not get slave pty attributes"); + if (term_flags.c_lflag & ECHONL) + { + term_flags.c_lflag &= ~ECHONL; + if (tcsetattr(slavefd, TCSANOW, &term_flags) != 0) + CU_FAIL_FATAL("Could not turn ECHO on on slave pty"); + } + + switch (fork()) + { + case -1: + CU_FAIL_FATAL("Could not fork"); + case 0: + { + static const int password_size = 512; + int child_slavefd; + char buffer[password_size]; + if (setsid() == (pid_t) -1) + CU_FAIL_FATAL("Could not create new session"); + + if ((child_slavefd = open(slavedevice, O_RDWR)) == 0) + CU_FAIL_FATAL("Could not open slave end of pty"); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + close(masterfd); + close(slavefd); + freerdp_passphrase_read("Password: ", buffer, password_size); + close(child_slavefd); + exit(EXIT_SUCCESS); + } + } + + read_buf[read_nbyte - 1] = '\0'; + + FD_ZERO(&fd_set_write); + FD_SET(masterfd, &fd_set_write); + if (select(masterfd + 1, NULL, &fd_set_write, NULL, NULL) == -1) + CU_FAIL_FATAL("Master end of pty not writeable"); + if (read(masterfd, read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to slave end of pty"); + + if (tcgetattr(slavefd, &term_flags) != 0) + CU_FAIL_FATAL("Could not get slave pty attributes"); + CU_ASSERT(term_flags.c_lflag & ECHONL) + write(masterfd, "\n", (size_t) 2); + close(masterfd); + close(slavefd); + return; +} + +void passphrase_read_prompts_to_stderr_when_no_tty() +{ + static const int read_nbyte = 11; + int stdin_pipe[2], stderr_pipe[2]; + char read_buf[read_nbyte]; + struct sigaction ignore, orig; + + ignore.sa_handler = SIG_IGN; + sigemptyset(&ignore.sa_mask); + + if (pipe(stdin_pipe) != 0 || pipe(stderr_pipe) != 0) + CU_FAIL_FATAL("Could not create pipe"); + + switch (fork()) + { + case -1: + CU_FAIL_FATAL("Could not fork"); + case 0: + { + static const int password_size = 512; + char buffer[password_size]; + close(stderr_pipe[0]); + close(stdin_pipe[1]); + if (setsid() == (pid_t) -1) + CU_FAIL_FATAL("Could not create new session"); + + dup2(stdin_pipe[0], STDIN_FILENO); + dup2(stderr_pipe[1], STDERR_FILENO); + freerdp_passphrase_read("Password: ", buffer, password_size); + exit(EXIT_SUCCESS); + } + } + close(stderr_pipe[1]); + close(stdin_pipe[0]); + + read_buf[read_nbyte - 1] = '\0'; + + if (read(stderr_pipe[0], read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to pipe"); + CU_ASSERT_STRING_EQUAL(read_buf, "Password: "); + + sigaction(SIGPIPE, &ignore, &orig); + write(stdin_pipe[1], "\n", (size_t) 2); + sigaction(SIGPIPE, &orig, NULL); + close(stderr_pipe[0]); + close(stdin_pipe[1]); + return; +} + +void passphrase_read_reads_from_stdin_when_no_tty() +{ + static const int read_nbyte = 11; + int stdin_pipe[2], stderr_pipe[2], result_pipe[2]; + char read_buf[read_nbyte]; + struct sigaction ignore, orig; + + ignore.sa_handler = SIG_IGN; + sigemptyset(&ignore.sa_mask); + + if (pipe(stdin_pipe) != 0 + || pipe(stderr_pipe) != 0 + || pipe(result_pipe) !=0) + CU_FAIL_FATAL("Could not create pipe"); + + switch (fork()) + { + case -1: + CU_FAIL_FATAL("Could not fork"); + case 0: + { + static const int password_size = 512; + char buffer[password_size]; + close(stderr_pipe[0]); + close(result_pipe[0]); + close(stdin_pipe[1]); + if (setsid() == (pid_t) -1) + CU_FAIL_FATAL("Could not create new session"); + + dup2(stdin_pipe[0], STDIN_FILENO); + dup2(stderr_pipe[1], STDERR_FILENO); + freerdp_passphrase_read("Password: ", buffer, password_size); + write(result_pipe[1], buffer, strlen(buffer) + (size_t) 1); + exit(EXIT_SUCCESS); + } + } + close(stderr_pipe[1]); + close(result_pipe[1]); + close(stdin_pipe[0]); + + read_buf[read_nbyte - 1] = '\0'; + + if (read(stderr_pipe[0], read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to pipe"); + + sigaction(SIGPIPE, &ignore, &orig); + write(stdin_pipe[1], "passw0rd\n", sizeof "passw0rd\n"); + sigaction(SIGPIPE, &orig, NULL); + + if (read(result_pipe[0], read_buf, read_nbyte) == (ssize_t) -1) + CU_FAIL_FATAL("Nothing written to pipe"); + CU_ASSERT_STRING_EQUAL(read_buf, "passw0rd"); + + close(stderr_pipe[0]); + close(stdin_pipe[1]); + return; +} + +void test_passphrase_read(void) +{ + passphrase_read_prompts_to_tty(); + passphrase_read_reads_from_tty(); + passphrase_read_turns_off_echo_during_read(); + passphrase_read_resets_terminal_after_read(); + passphrase_read_turns_on_newline_echo_during_read(); + passphrase_read_prompts_to_stderr_when_no_tty(); + passphrase_read_reads_from_stdin_when_no_tty(); +} diff --git a/cunit/test_utils.h b/cunit/test_utils.h index dcef6ac16..39cae3920 100644 --- a/cunit/test_utils.h +++ b/cunit/test_utils.h @@ -28,3 +28,4 @@ void test_semaphore(void); void test_load_plugin(void); void test_wait_obj(void); void test_args(void); +void test_passphrase_read(void); diff --git a/include/freerdp/utils/passphrase.h b/include/freerdp/utils/passphrase.h new file mode 100644 index 000000000..44c6d0b1e --- /dev/null +++ b/include/freerdp/utils/passphrase.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Passphrase Handling Utils + * + * Copyright 2011 Shea Levy + * + * Licensed 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 __UTILS_PASSPHRASE_H +#define __UTILS_PASSPHRASE_H + +#include +#include + +FREERDP_API char* freerdp_passphrase_read(const char* prompt, char* buf, size_t bufsiz); + +#endif /* __UTILS_PASSPHRASE_H */ diff --git a/libfreerdp-utils/CMakeLists.txt b/libfreerdp-utils/CMakeLists.txt index b93b14cf4..b64263047 100644 --- a/libfreerdp-utils/CMakeLists.txt +++ b/libfreerdp-utils/CMakeLists.txt @@ -31,6 +31,7 @@ set(FREERDP_UTILS_SRCS load_plugin.c memory.c mutex.c + passphrase.c pcap.c profiler.c rail.c diff --git a/libfreerdp-utils/passphrase.c b/libfreerdp-utils/passphrase.c new file mode 100644 index 000000000..220b695bd --- /dev/null +++ b/libfreerdp-utils/passphrase.c @@ -0,0 +1,124 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Passphrase Handling Utils + * + * Copyright 2011 Shea Levy + * + * Licensed 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 +#include +#ifdef _WIN32 +char* freerdp_passphrase_read(const char* prompt, char* buf, size_t bufsiz) +{ + errno=ENOSYS; + return NULL; +} +#else +#include +#include +#include +#include +#include +#include + +char* freerdp_passphrase_read(const char* prompt, char* buf, size_t bufsiz) +{ + char read_char; + char* buf_iter; + char term_name[L_ctermid]; + int term_file, write_file, read_file, reset_terminal = 0; + ssize_t nbytes; + size_t read_bytes = 0; + struct termios orig_flags, no_echo_flags; + + if (bufsiz == 0) + { + errno = EINVAL; + return NULL; + } + + ctermid(term_name); + if(strcmp(term_name, "") == 0 + || (term_file = open(term_name, O_RDWR)) == -1) + { + write_file = STDERR_FILENO; + read_file = STDIN_FILENO; + } + else + { + write_file = term_file; + read_file = term_file; + } + + if (tcgetattr(read_file, &orig_flags) != -1) + { + reset_terminal = 1; + no_echo_flags = orig_flags; + no_echo_flags.c_lflag &= ~ECHO; + no_echo_flags.c_lflag |= ECHONL; + if (tcsetattr(read_file, TCSAFLUSH, &no_echo_flags) == -1) + reset_terminal = 0; + } + + if (write(write_file, prompt, strlen(prompt)) == (ssize_t) -1) + goto error; + + buf_iter = buf; + while ((nbytes = read(read_file, &read_char, sizeof read_char)) == (sizeof read_char)) + { + if (read_char == '\n') + break; + if (read_bytes < (bufsiz - (size_t) 1)) + { + read_bytes++; + *buf_iter = read_char; + buf_iter++; + } + } + *buf_iter = '\0'; + buf_iter = NULL; + read_char = '\0'; + if (nbytes == (ssize_t) -1) + goto error; + + if (reset_terminal) + { + if (tcsetattr(read_file, TCSADRAIN, &orig_flags) == -1) + goto error; + reset_terminal = 0; + } + + if (read_file != STDIN_FILENO) + { + if (close(read_file) == -1) + goto error; + } + + return buf; + + error: + { + int saved_errno = errno; + buf_iter = NULL; + read_char = '\0'; + if (reset_terminal) + tcsetattr(read_file, TCSANOW, &orig_flags); + if (read_file != STDIN_FILENO) + close(read_file); + errno = saved_errno; + return NULL; + } +} +#endif