349 lines
9.5 KiB
C
349 lines
9.5 KiB
C
/*
|
|
*****************************************************************************
|
|
*
|
|
* File: getpasswd.c
|
|
*
|
|
* Purpose: Routines for obtaining a password from a user.
|
|
*
|
|
* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
|
|
* Copyright (C) 2009-2014 fwknop developers and contributors. For a full
|
|
* list of contributors, see the file 'CREDITS'.
|
|
*
|
|
* License (GNU General Public License):
|
|
*
|
|
* 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
* USA
|
|
*
|
|
*****************************************************************************
|
|
*/
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
|
|
#ifdef WIN32
|
|
#include <conio.h>
|
|
#else
|
|
#include <termios.h>
|
|
#endif
|
|
|
|
#include "fwknop_common.h"
|
|
#include "getpasswd.h"
|
|
#include "utils.h"
|
|
|
|
#define PW_BUFSIZE 128 /*!< Maximum number of chars an encryption key or a password can contain */
|
|
|
|
#define PW_BREAK_CHAR 0x03 /*!< Ascii code for the Ctrl-C char */
|
|
#define PW_BS_CHAR 0x08 /*!< Ascii code for the backspace char */
|
|
#define PW_LF_CHAR 0x0A /*!< Ascii code for the \n char */
|
|
#define PW_CR_CHAR 0x0D /*!< Ascii code for the \r char */
|
|
#define PW_CLEAR_CHAR 0x15 /*!< Ascii code for the Ctrl-U char */
|
|
|
|
#define ARRAY_FIRST_ELT_ADR(t) &((t)[0]) /*!< Macro to get the first element of an array */
|
|
#define ARRAY_LAST_ELT_ADR(t) &((t)[sizeof(t)-1]) /*!< Macro to get the last element of an array */
|
|
|
|
#ifdef HAVE_C_UNIT_TESTS
|
|
#define TEST_SUITE_DESCRIPTION "Get password module"
|
|
#endif
|
|
|
|
/**
|
|
* @brief Read a password from a stream object
|
|
*
|
|
* @param stream Pointer to a FILE object that identifies an input stream.
|
|
*
|
|
* @return The password buffer or NULL if not set
|
|
*/
|
|
static char *
|
|
read_passwd_from_stream(FILE *stream)
|
|
{
|
|
static char password[PW_BUFSIZE] = {0};
|
|
int c;
|
|
char *ptr;
|
|
|
|
ptr = ARRAY_FIRST_ELT_ADR(password);
|
|
|
|
if(stream == NULL)
|
|
return password;
|
|
|
|
#ifdef WIN32
|
|
while((c = _getch()) != PW_CR_CHAR)
|
|
#else
|
|
while( ((c = getc(stream)) != EOF) && (c != PW_LF_CHAR) && (c != PW_BREAK_CHAR) )
|
|
#endif
|
|
{
|
|
/* Handle a backspace without backing up too far. */
|
|
if (c == PW_BS_CHAR)
|
|
{
|
|
if (ptr != ARRAY_FIRST_ELT_ADR(password))
|
|
ptr--;
|
|
}
|
|
|
|
/* Handle a Ctrl-U to clear the password entry and start over */
|
|
else if (c == PW_CLEAR_CHAR)
|
|
ptr = ARRAY_FIRST_ELT_ADR(password);
|
|
|
|
/* Fill in the password buffer until it reaches the last -1 char.
|
|
* The last char is used to NULL terminate the string. */
|
|
else if (ptr < ARRAY_LAST_ELT_ADR(password))
|
|
{
|
|
*ptr++ = c;
|
|
}
|
|
|
|
/* Discard char */
|
|
else;
|
|
}
|
|
|
|
/* A CTRL-C char has been detected, we discard the password */
|
|
if (c == PW_BREAK_CHAR)
|
|
password[0] = '\0';
|
|
|
|
/* Otherwise we NULL terminate the string here. Overflows are handled
|
|
* previously, so we can add the char without worrying */
|
|
else
|
|
*ptr = '\0';
|
|
|
|
return password;
|
|
}
|
|
|
|
/**
|
|
* @brief Function for accepting password input from users
|
|
*
|
|
* The functions reads chars from a buffered stream and store them in a buffer of
|
|
* chars. If a file descriptor is supplied then, the password is read from
|
|
* the associated stream, otherwise a new buffered stream is created and a
|
|
* prompt is displayed to the user.
|
|
*
|
|
* @param prompt String displayed on the terminal to prompt the user for a
|
|
* password or an encryption key
|
|
* @param fd File descriptor to use to read the pasword from. If fd is set
|
|
* to FD_INVALID, then a new stream is opened.
|
|
*
|
|
* @return NULL if a problem occured or the user killed the terminal (Ctrl-C)\n
|
|
* otherwise the password - empty password is accepted.
|
|
*/
|
|
char*
|
|
getpasswd(const char *prompt, int fd)
|
|
{
|
|
char *ptr = NULL;
|
|
FILE *fp = NULL;
|
|
|
|
#ifndef WIN32
|
|
sigset_t sig, old_sig;
|
|
struct termios ts;
|
|
tcflag_t old_c_lflag = 0;
|
|
#else
|
|
/* Force stdin on windows. */
|
|
fd = 0;
|
|
#endif
|
|
|
|
/* If a valid file descriptor is supplied, we try to open a stream from it */
|
|
if (FD_IS_VALID(fd))
|
|
{
|
|
fp = fdopen(fd, "r");
|
|
if (fp == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "getpasswd() - "
|
|
"Unable to create a stream from the file descriptor : %s",
|
|
strerror(errno));
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
#ifndef WIN32
|
|
/* Otherwise we are going to open a new stream */
|
|
else
|
|
{
|
|
if((fp = fopen(ctermid(NULL), "r+")) == NULL)
|
|
return(NULL);
|
|
|
|
setbuf(fp, NULL);
|
|
|
|
/* Setup blocks for SIGINT and SIGTSTP and save the original signal
|
|
* mask.
|
|
*/
|
|
sigemptyset(&sig);
|
|
sigaddset(&sig, SIGINT);
|
|
sigaddset(&sig, SIGTSTP);
|
|
sigprocmask(SIG_BLOCK, &sig, &old_sig);
|
|
|
|
/*
|
|
* Save current tty state for later restoration after we :
|
|
* - disable echo of characters to the tty
|
|
* - disable signal generation
|
|
* - disable cannonical mode (input read line by line mode)
|
|
*/
|
|
tcgetattr(fileno(fp), &ts);
|
|
old_c_lflag = ts.c_lflag;
|
|
ts.c_lflag &= ~(ECHO | ICANON | ISIG);
|
|
tcsetattr(fileno(fp), TCSAFLUSH, &ts);
|
|
|
|
fputs(prompt, fp);
|
|
}
|
|
#else
|
|
_cputs(prompt);
|
|
#endif
|
|
/* Read the password */
|
|
ptr = read_passwd_from_stream(fp);
|
|
|
|
#ifdef WIN32
|
|
/* In Windows, it would be a CR-LF
|
|
*/
|
|
_putch(PW_CR_CHAR);
|
|
_putch(PW_LF_CHAR);
|
|
#else
|
|
if(! FD_IS_VALID(fd))
|
|
{
|
|
/* Reset terminal settings
|
|
*/
|
|
fputs("\n", fp);
|
|
ts.c_lflag = old_c_lflag;
|
|
tcsetattr(fileno(fp), TCSAFLUSH, &ts);
|
|
}
|
|
#endif
|
|
|
|
fclose(fp);
|
|
|
|
return (ptr);
|
|
}
|
|
|
|
/* Function for accepting password input from a file
|
|
*/
|
|
int
|
|
get_key_file(char *key, int *key_len, const char *key_file,
|
|
fko_ctx_t ctx, const fko_cli_options_t *options)
|
|
{
|
|
FILE *pwfile_ptr;
|
|
unsigned int numLines = 0, i = 0, found_dst;
|
|
|
|
char conf_line_buf[MAX_LINE_LEN] = {0};
|
|
char tmp_char_buf[MAX_LINE_LEN] = {0};
|
|
char *lptr;
|
|
|
|
memset(key, 0x00, MAX_KEY_LEN+1);
|
|
|
|
if ((pwfile_ptr = fopen(key_file, "r")) == NULL)
|
|
{
|
|
log_msg(LOG_VERBOSITY_ERROR, "Could not open config file: %s", key_file);
|
|
return 0;
|
|
}
|
|
|
|
while ((fgets(conf_line_buf, MAX_LINE_LEN, pwfile_ptr)) != NULL)
|
|
{
|
|
numLines++;
|
|
conf_line_buf[MAX_LINE_LEN-1] = '\0';
|
|
lptr = conf_line_buf;
|
|
|
|
memset(tmp_char_buf, 0x0, MAX_LINE_LEN);
|
|
|
|
while (*lptr == ' ' || *lptr == '\t' || *lptr == '=')
|
|
lptr++;
|
|
|
|
/* Get past comments and empty lines.
|
|
*/
|
|
if (*lptr == '#' || *lptr == '\n' || *lptr == '\r' || *lptr == '\0' || *lptr == ';')
|
|
continue;
|
|
|
|
/* Look for a line like "<SPA destination IP>: <password>" - this allows
|
|
* multiple keys to be placed within the same file, and the client will
|
|
* reference the matching one for the SPA server we are contacting
|
|
*/
|
|
found_dst = 1;
|
|
for (i=0; i < strlen(options->spa_server_str); i++)
|
|
if (*lptr++ != options->spa_server_str[i])
|
|
found_dst = 0;
|
|
|
|
if (! found_dst)
|
|
continue;
|
|
|
|
if (*lptr == ':')
|
|
lptr++;
|
|
else
|
|
continue;
|
|
|
|
/* Skip whitespace until we get to the password
|
|
*/
|
|
while (*lptr == ' ' || *lptr == '\t' || *lptr == '=')
|
|
lptr++;
|
|
|
|
i = 0;
|
|
while (*lptr != '\0' && *lptr != '\n') {
|
|
key[i] = *lptr;
|
|
lptr++;
|
|
i++;
|
|
}
|
|
key[i] = '\0';
|
|
}
|
|
|
|
fclose(pwfile_ptr);
|
|
|
|
if (key[0] == '\0') {
|
|
log_msg(LOG_VERBOSITY_ERROR, "Could not get key for IP: %s from: %s",
|
|
options->spa_server_str, key_file);
|
|
return 0;
|
|
}
|
|
|
|
*key_len = strlen(key);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_C_UNIT_TESTS
|
|
|
|
static int ut_init_test_suite(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int ut_clean_test_suite(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
DECLARE_UTEST(test1, "Description de ut1 dans getpassword.c")
|
|
{
|
|
CU_ASSERT(1 == 0);
|
|
}
|
|
|
|
DECLARE_UTEST(test2, "Description de ut2 dans getpassword.c")
|
|
{
|
|
CU_ASSERT(0);
|
|
}
|
|
|
|
/* TODO add a generic function to go through a tab which contains all utests */
|
|
int register_ts_getpassword(void)
|
|
{
|
|
CU_pSuite pSuite = NULL;
|
|
|
|
/* add a suite to the registry */
|
|
pSuite = CU_add_suite(TEST_SUITE_DESCRIPTION, ut_init_test_suite, ut_clean_test_suite);
|
|
if (NULL == pSuite) {
|
|
CU_cleanup_registry();
|
|
return CU_get_error();
|
|
}
|
|
|
|
/* add the tests to the suite */
|
|
if ((NULL == CU_add_test(pSuite, UTEST_DESCR(test1), UTEST_FCT(test1))) ||
|
|
(NULL == CU_add_test(pSuite, UTEST_DESCR(test2), UTEST_FCT(test2))))
|
|
{
|
|
CU_cleanup_registry();
|
|
return CU_get_error();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_C_UNIT_TESTS */
|
|
|
|
/***EOF***/
|