From 27a1327b49210e342741cf8fb11576983cbcc36d Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 22 Nov 2012 15:22:40 +0100 Subject: util: Add a UNIX platform independent samba_getpass(). Reviewed-by: Jelmer Vernooij --- lib/util/getpass.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/util/samba_util.h | 40 +++++++++ lib/util/wscript_build | 2 +- 3 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 lib/util/getpass.c (limited to 'lib/util') diff --git a/lib/util/getpass.c b/lib/util/getpass.c new file mode 100644 index 0000000000..480bd56898 --- /dev/null +++ b/lib/util/getpass.c @@ -0,0 +1,224 @@ +/* + * Unix SMB/CIFS implementation. + * + * getpass.c - platform independent getpass function. + * + * Copyright (c) 2010-2012 Andreas Schneider + * + * 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 . + */ + +#include "includes.h" + +#include "system/filesys.h" +#include "system/terminal.h" + +#if !defined(SMB_MALLOC) +#undef malloc +#define SMB_MALLOC(s) malloc((s)) +#endif + +/** + * @internal + * + * @brief Get the password from the console. + * + * @param[in] prompt The prompt to display. + * + * @param[in] buf The buffer to fill. + * + * @param[in] len The length of the buffer. + * + * @param[in] verify Should the password be verified? + * + * @return 1 on success, 0 on error. + */ +static int samba_gets(const char *prompt, char *buf, size_t len, bool verify) +{ + char *tmp; + char *ptr = NULL; + int ok = 0; + + tmp = SMB_MALLOC(len); + if (tmp == NULL) { + return 0; + } + memset(tmp,'\0',len); + + /* read the password */ + while (!ok) { + if (buf[0] != '\0') { + fprintf(stdout, "%s[%s] ", prompt, buf); + } else { + fprintf(stdout, "%s", prompt); + } + fflush(stdout); + if (fgets(tmp, len, stdin) == NULL) { + free(tmp); + return 0; + } + + if ((ptr = strchr(tmp, '\n'))) { + *ptr = '\0'; + } + fprintf(stdout, "\n"); + + if (*tmp) { + strncpy(buf, tmp, len); + } + + if (verify) { + char *key_string; + + key_string = SMB_MALLOC(len); + if (key_string == NULL) { + break; + } + memset(key_string, '\0', len); + + fprintf(stdout, "\nVerifying, please re-enter. %s", prompt); + fflush(stdout); + if (! fgets(key_string, len, stdin)) { + memset(key_string, '\0', len); + SAFE_FREE(key_string); + clearerr(stdin); + continue; + } + if ((ptr = strchr(key_string, '\n'))) { + *ptr = '\0'; + } + fprintf(stdout, "\n"); + if (strcmp(buf, key_string)) { + printf("\n\07\07Mismatch - try again\n"); + memset(key_string, '\0', len); + SAFE_FREE(key_string); + fflush(stdout); + continue; + } + memset(key_string, '\0', len); + SAFE_FREE(key_string); + } + ok = 1; + } + memset(tmp, '\0', len); + free(tmp); + + return ok; +} + +/** + * @brief Get a password from the console. + * + * You should make sure that the buffer is an empty string! + * + * You can also use this function to ask for a username. Then you can fill the + * buffer with the username and it is shows to the users. If the users just + * presses enter the buffer will be untouched. + * + * @code + * char username[128]; + * + * snprintf(username, sizeof(username), "john"); + * + * samba_getpass("Username:", username, sizeof(username), 1, 0); + * @endcode + * + * The prompt will look like this: + * + * Username: [john] + * + * If you press enter then john is used as the username, or you can type it in + * to change it. + * + * @param[in] prompt The prompt to show to ask for the password. + * + * @param[out] buf The buffer the password should be stored. It NEEDS to be + * empty or filled out. + * + * @param[in] len The length of the buffer. + * + * @param[in] echo Should we echo what you type. + * + * @param[in] verify Should we ask for the password twice. + * + * @return 0 on success, -1 on error. + */ +int samba_getpass(const char *prompt, + char *buf, + size_t len, + bool echo, + bool verify) +{ + struct termios attr; + struct termios old_attr; + int ok = 0; + int fd = -1; + + /* fgets needs at least len - 1 */ + if (prompt == NULL || buf == NULL || len < 2) { + return -1; + } + + ZERO_STRUCT(attr); + ZERO_STRUCT(old_attr); + + /* get local terminal attributes */ + if (tcgetattr(STDIN_FILENO, &attr) < 0) { + perror("tcgetattr"); + return -1; + } + + /* save terminal attributes */ + memcpy(&old_attr, &attr, sizeof(attr)); + if((fd = fcntl(0, F_GETFL, 0)) < 0) { + perror("fcntl"); + return -1; + } + + /* disable echo */ + if (!echo) { + attr.c_lflag &= ~(ECHO); + } + + /* write attributes to terminal */ + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) < 0) { + perror("tcsetattr"); + return -1; + } + + /* disable nonblocking I/O */ + if (fd & O_NDELAY) { + fcntl(0, F_SETFL, fd & ~O_NDELAY); + } + + ok = samba_gets(prompt, buf, len, verify); + + /* reset terminal */ + tcsetattr(STDIN_FILENO, TCSANOW, &old_attr); + + /* close fd */ + if (fd & O_NDELAY) { + fcntl(0, F_SETFL, fd); + } + + if (!ok) { + memset (buf, '\0', len); + return -1; + } + + /* force termination */ + buf[len - 1] = '\0'; + + return 0; +} diff --git a/lib/util/samba_util.h b/lib/util/samba_util.h index 4a6dd3bc80..e553ec1342 100644 --- a/lib/util/samba_util.h +++ b/lib/util/samba_util.h @@ -816,6 +816,46 @@ _PUBLIC_ void close_low_fds(bool stdin_too, bool stdout_too, bool stderr_too); **/ _PUBLIC_ void become_daemon(bool do_fork, bool no_process_group, bool log_stdout); +/** + * @brief Get a password from the console. + * + * You should make sure that the buffer is an empty string! + * + * You can also use this function to ask for a username. Then you can fill the + * buffer with the username and it is shows to the users. If the users just + * presses enter the buffer will be untouched. + * + * @code + * char username[128]; + * + * snprintf(username, sizeof(username), "john"); + * + * smb_getpass("Username:", username, sizeof(username), 1, 0); + * @endcode + * + * The prompt will look like this: + * + * Username: [john] + * + * If you press enter then john is used as the username, or you can type it in + * to change it. + * + * @param[in] prompt The prompt to show to ask for the password. + * + * @param[out] buf The buffer the password should be stored. It NEEDS to be + * empty or filled out. + * + * @param[in] len The length of the buffer. + * + * @param[in] echo Should we echo what you type. + * + * @param[in] verify Should we ask for the password twice. + * + * @return 0 on success, -1 on error. + */ +_PUBLIC_ int samba_getpass(const char *prompt, char *buf, size_t len, + bool echo, bool verify); + /** * Load a ini-style file. */ diff --git a/lib/util/wscript_build b/lib/util/wscript_build index ddaf90ffc2..a671bc9c36 100755 --- a/lib/util/wscript_build +++ b/lib/util/wscript_build @@ -2,7 +2,7 @@ bld.SAMBA_LIBRARY('samba-util', source='''talloc_stack.c smb_threads.c xfile.c data_blob.c - util_file.c time.c rbtree.c rfc1738.c select.c + util_file.c time.c rbtree.c rfc1738.c select.c getpass.c genrand.c fsusage.c blocking.c become_daemon.c signal.c system.c params.c util.c util_id.c util_net.c util_strlist.c util_paths.c idtree.c debug.c fault.c base64.c -- cgit