aboutsummaryrefslogtreecommitdiffstats
path: root/src/unistd/getpass.c
diff options
context:
space:
mode:
authorMattias Andrée <maandree@member.fsf.org>2015-12-21 15:32:32 +0100
committerMattias Andrée <maandree@member.fsf.org>2015-12-21 15:32:32 +0100
commitd361eccacfa3044c840778cba66ae75e6d9fcd7b (patch)
treeb6fe7d276d322e6c06dc864a0d316d7903010137 /src/unistd/getpass.c
parentattributes (diff)
downloadslibc-d361eccacfa3044c840778cba66ae75e6d9fcd7b.tar.gz
slibc-d361eccacfa3044c840778cba66ae75e6d9fcd7b.tar.bz2
slibc-d361eccacfa3044c840778cba66ae75e6d9fcd7b.tar.xz
add getpass
Signed-off-by: Mattias Andrée <maandree@member.fsf.org>
Diffstat (limited to '')
-rw-r--r--src/unistd/getpass.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/unistd/getpass.c b/src/unistd/getpass.c
new file mode 100644
index 0000000..5968d38
--- /dev/null
+++ b/src/unistd/getpass.c
@@ -0,0 +1,193 @@
+/**
+ * slibc — Yet another C library
+ * Copyright © 2015 Mattias Andrée (maandree@member.fsf.org)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <termios.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <slibc-alloca.h>
+
+
+#define t(...) do { if (__VA_ARGS__) goto fail; } while (0)
+
+
+
+/**
+ * This function performs the actual password-reading
+ * part of `getpass`. It will read into a statically
+ * allocated buffer until that buffer is full (8196 bytes,
+ * including termination), and switch to a dynamically
+ * allocated buffer when the it [the statically allocated
+ * buffer] is full.
+ *
+ * @prarm fd File descriptor to the, already configured,
+ * controlling terminal.
+ * @return The entered line. You should override it with zeroes as
+ * soon as possible to avoid leaving cleartest passphrases
+ * visible in memory, or potentially stored to unencrypted
+ * swap. The returned string is statically allocated, and
+ * should not be deallocated, if the length of the string,
+ * is (strictly) less than 8196, otherwise it is dynamically
+ * allocated, and should be deallocated.
+ *
+ * @throws Any error specified for malloc(3).
+ * @throws Any error specified for read(3).
+ */
+static char* _getpass(int fd)
+{
+ static char pass_s[8196];
+ char* pass_d = NULL;
+ size_t ptr_s = 0, ptr_d = 8196, size_d = (8196 << 1);
+ ssize_t got = 1;
+ void* new;
+
+ /* Read into statically allocated buffer until it is full. */
+ while (ptr_s < sizeof(pass_s) / sizeof(char))
+ {
+ /* Read. */
+ t (got = read(fd, pass_s + ptr_s, sizeof(pass_s) - ptr_s * sizeof(char)), got < 0);
+ ptr_s += (size_t)got;
+ /* End of line? */
+ if ((got == 0) || (pass_s[ptr_s - 1] == '\n'))
+ {
+ pass_s[ptr_s - !!got] = '\0';
+ return pass_s;
+ }
+ }
+
+ /* Transfer input to dynamically allocated buffer. */
+ t (pass_d = secure_realloc(pass_d, size_d * sizeof(char)), pass_d == NULL);
+ memcpy(pass_d, pass_s, sizeof(pass_s)), explicit_bzero(pass_s, sizeof(pass_s));
+
+ /* Read into dynamically allocated buffer. */
+ for (;;)
+ {
+ /* Do we need to grow the buffer? */
+ if (ptr_d + 1 == size_d)
+ {
+ t (new = secure_realloc(pass_d, (size_d <<= 1) * sizeof(char)), new == NULL);
+ pass_d = new;
+ }
+ /* Read. */
+ t (got = read(fd, pass_d + ptr_d, (size_d - ptr_d - 1) * sizeof(char)), got < 0);
+ ptr_d += (size_t)got;
+ /* End of line? */
+ if ((got == 0) || (pass_d[ptr_d - 1] == '\n'))
+ {
+ pass_d[ptr_d - !!got] = '\0';
+ return pass_d;
+ }
+ }
+
+ fail:
+ saved_errno = errno;
+ explicit_bzero(pass_s, sizeof(pass_s));
+ secure_free(pass_d);
+ errno = saved_errno;
+ return NULL;
+}
+
+
+/**
+ * Get password input from the terminal.
+ *
+ * The exact behaviour of this function depends on the implementations.
+ * However you can assume that, the controlling terminal (/dev/tty) is
+ * opened and used for input and output, and that echoing is disabled.
+ * You cannot assume that arbitrary lengths are supported. However, in
+ * this implementation, line editing is enabled and arbitrary lengths
+ * are supported. If the length of the input (excluding termination) is
+ * less than 8192 a statically allocated string is returned, otherwise
+ * a dynamically allocated string is returned.
+ *
+ * @etymology (Get) (pass)word from terminal!
+ *
+ * @param prompt Text to print at the beginning of the line.
+ * Used to tell the user what is expected of her.
+ * Must not be `NULL`.
+ * @return The entered line. You should override it with zeroes as
+ * soon as possible to avoid leaving cleartest passphrases
+ * visible in memory, or potentially stored to unencrypted
+ * swap. The returned string is statically allocated, do
+ * not deallocate it, unless you know that you are using
+ * slibc and the length of the string is at least 8192.
+ * `NULL` on error. If a statically allocated string is
+ * returned, it will be overwritten at the next call.
+ *
+ * @throws Any error specified for open(3).
+ * @throws Any error specified for malloc(3).
+ * @throws Any error specified for read(3).
+ * @throws Any error specified for tcgetattr(3).
+ * @throws Any error specified for tcsetattr(3).
+ * @throws Any error specified for tcdrain(3).
+ *
+ * @since Always.
+ */
+char* getpass(const char* prompt)
+{
+ struct termios saved_stty, stty;
+ char* pass;
+ int fd = -1, stty_saved = 0, saved_errno;
+
+ /* Open controlling terminal. */
+ t (fd = open("/dev/tty", O_RDWR | O_NOCTTY | O_CLOEXEC), fd < 0);
+
+ /* Configure terminal settings. */
+ t (tcgetattr(fd, &stty));
+ saved_stty = stty;
+ stty_saved = 1;
+ stty.c_lflag = (stty.c_lflag & (tcflag_t)~(ECHO | ISIG )) | (tcflag_t)ICANON;
+ stty.c_iflag = (stty.c_iflag & (tcflag_t)~(INLCR | IGNCR)) | (tcflag_t)ICRNL;
+ t (tcsetattr(fd, TCSAFLUSH, &stty));
+ t (tcdrain(fd));
+
+ /* Prompt the user. */
+ (void) dprintf(fd, "%s", prompt);
+
+ /* Get password. */
+ pass = _getpass(fd);
+ if (pass == NULL)
+ goto fail;
+
+ /* Move cursor to next line. */
+ (void) dprintf(fd, "\n");
+
+ /* Restore terminal settings. */
+ (void) tcsetattr(fd, TCSAFLUSH, &saved_stty);
+ (void) tcdrain(fd);
+
+ /* Close controlling terminal. */
+ close(fd);
+
+ return pass;
+
+ fail:
+ saved_errno = errno;
+ if (fd >= 0)
+ close(fd);
+ if (stty_saved)
+ {
+ tcsetattr(fd, TCSAFLUSH, &saved_stty);
+ tcdrain(fd);
+ }
+ errno = saved_errno;
+ return NULL;
+}
+