
If .secrets file is not used it means that encrypted password is not used. Moved log entry away from error log and placed it to message log. It still prints it multiple times though.
359 lines
9.5 KiB
C
359 lines
9.5 KiB
C
/*
|
|
* This file is distributed as part of the MariaDB Corporation MaxScale. It 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,
|
|
* version 2.
|
|
*
|
|
* 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., 51
|
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Copyright MariaDB Corporation Ab 2013-2014
|
|
*/
|
|
|
|
#include <secrets.h>
|
|
#include <time.h>
|
|
#include <skygw_utils.h>
|
|
#include <log_manager.h>
|
|
#include <ctype.h>
|
|
#include <mysql_client_server_protocol.h>
|
|
|
|
extern int lm_enabled_logfiles_bitmask;
|
|
/**
|
|
* Generate a random printable character
|
|
*
|
|
* @return A random printable character
|
|
*/
|
|
static unsigned char
|
|
secrets_randomchar()
|
|
{
|
|
return (char)((rand() % ('~' - ' ')) + ' ');
|
|
}
|
|
|
|
static int
|
|
secrets_random_str(unsigned char *output, int len)
|
|
{
|
|
int i;
|
|
srand((unsigned long )time(0L) ^ (unsigned long )output);
|
|
|
|
for ( i = 0; i < len; ++i )
|
|
{
|
|
output[i] = secrets_randomchar();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* secrets_readKeys
|
|
*
|
|
* This routine reads data from a binary file and extracts the AES encryption key
|
|
* and the AES Init Vector
|
|
*
|
|
* @return The keys structure or NULL on error
|
|
*/
|
|
static MAXKEYS *
|
|
secrets_readKeys()
|
|
{
|
|
char secret_file[255];
|
|
char *home;
|
|
MAXKEYS *keys;
|
|
struct stat secret_stats;
|
|
int fd;
|
|
int len;
|
|
|
|
home = getenv("MAXSCALE_HOME");
|
|
|
|
if (home == NULL) {
|
|
home = "/usr/local/skysql/MaxScale";
|
|
}
|
|
snprintf(secret_file, 255, "%s/etc/.secrets", home);
|
|
|
|
/* Try to access secrets file */
|
|
if (access(secret_file, R_OK) == -1)
|
|
{
|
|
int eno = errno;
|
|
errno = 0;
|
|
if (eno == ENOENT)
|
|
{
|
|
LOGIF(LM, (skygw_log_write(
|
|
LOGFILE_MESSAGE,
|
|
"Encrypted password file %s can't be accessed "
|
|
"(%s). Password encryption is not used.",
|
|
secret_file,
|
|
strerror(eno))));
|
|
}
|
|
else
|
|
{
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : access for secrets file "
|
|
"[%s] failed. Error %d, %s.",
|
|
secret_file,
|
|
eno,
|
|
strerror(eno))));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* open secret file */
|
|
if ((fd = open(secret_file, O_RDONLY)) < 0)
|
|
{
|
|
int eno = errno;
|
|
errno = 0;
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Failed opening secret "
|
|
"file [%s]. Error %d, %s.",
|
|
secret_file,
|
|
eno,
|
|
strerror(eno))));
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* accessing file details */
|
|
if (fstat(fd, &secret_stats) < 0) {
|
|
int eno = errno;
|
|
errno = 0;
|
|
close(fd);
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : fstat for secret file %s "
|
|
"failed. Error %d, %s.",
|
|
secret_file,
|
|
eno,
|
|
strerror(eno))));
|
|
return NULL;
|
|
}
|
|
|
|
if (secret_stats.st_size != sizeof(MAXKEYS))
|
|
{
|
|
int eno = errno;
|
|
errno = 0;
|
|
close(fd);
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Secrets file %s has "
|
|
"incorrect size. Error %d, %s.",
|
|
secret_file,
|
|
eno,
|
|
strerror(eno))));
|
|
return NULL;
|
|
}
|
|
if (secret_stats.st_mode != (S_IRUSR|S_IFREG))
|
|
{
|
|
close(fd);
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Ignoring secrets file "
|
|
"%s, invalid permissions.",
|
|
secret_file)));
|
|
return NULL;
|
|
}
|
|
|
|
if ((keys = (MAXKEYS *)malloc(sizeof(MAXKEYS))) == NULL)
|
|
{
|
|
close(fd);
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Memory allocation failed "
|
|
"for key structure.")));
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Read all data from file.
|
|
* MAXKEYS (secrets.h) is struct for key, _not_ length-related macro.
|
|
*/
|
|
len = read(fd, keys, sizeof(MAXKEYS));
|
|
|
|
if (len != sizeof(MAXKEYS))
|
|
{
|
|
int eno = errno;
|
|
errno = 0;
|
|
close(fd);
|
|
free(keys);
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Read from secrets file "
|
|
"%s failed. Read %d, expected %d bytes. Error %d, %s.",
|
|
secret_file,
|
|
len,
|
|
sizeof(MAXKEYS),
|
|
eno,
|
|
strerror(eno))));
|
|
return NULL;
|
|
}
|
|
|
|
/* Close the file */
|
|
if (close(fd) < 0) {
|
|
int eno = errno;
|
|
errno = 0;
|
|
free(keys);
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Failed closing the "
|
|
"secrets file %s. Error %d, %s.",
|
|
secret_file,
|
|
eno,
|
|
strerror(eno))));
|
|
return NULL;
|
|
}
|
|
ss_dassert(keys != NULL);
|
|
return keys;
|
|
}
|
|
|
|
/**
|
|
* secrets_writeKeys
|
|
*
|
|
* This routine writes into a binary file the AES encryption key
|
|
* and the AES Init Vector
|
|
*
|
|
* @param secret_file The file with secret keys
|
|
* @return 0 on success and 1 on failure
|
|
*/
|
|
int secrets_writeKeys(char *secret_file)
|
|
{
|
|
int fd;
|
|
MAXKEYS key;
|
|
|
|
/* Open for writing | Create | Truncate the file for writing */
|
|
if ((fd = open(secret_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR)) < 0)
|
|
{
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : failed opening secret "
|
|
"file [%s]. Error %d, %s.",
|
|
secret_file,
|
|
errno,
|
|
strerror(errno))));
|
|
return 1;
|
|
}
|
|
|
|
srand(time(NULL));
|
|
secrets_random_str(key.enckey, MAXSCALE_KEYLEN);
|
|
secrets_random_str(key.initvector, MAXSCALE_IV_LEN);
|
|
|
|
/* Write data */
|
|
if (write(fd, &key, sizeof(key)) < 0)
|
|
{
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : failed writing into "
|
|
"secret file [%s]. Error %d, %s.",
|
|
secret_file,
|
|
errno,
|
|
strerror(errno))));
|
|
return 1;
|
|
}
|
|
|
|
/* close file */
|
|
if (close(fd) < 0)
|
|
{
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : failed closing the "
|
|
"secret file [%s]. Error %d, %s.",
|
|
secret_file,
|
|
errno,
|
|
strerror(errno))));
|
|
}
|
|
chmod(secret_file, S_IRUSR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Decrypt a password that is stored inthe MaxScale configuration file.
|
|
* If the password is not encrypted, ie is not a HEX string, then the
|
|
* original is returned, this allows for backward compatibility with
|
|
* unencrypted password.
|
|
*
|
|
* Note the return is always a malloc'd string that the caller must free
|
|
*
|
|
* @param crypt The encrypted password
|
|
* @return The decrypted password
|
|
*/
|
|
char *
|
|
decryptPassword(char *crypt)
|
|
{
|
|
MAXKEYS *keys;
|
|
AES_KEY aeskey;
|
|
unsigned char *plain;
|
|
char *ptr;
|
|
unsigned char encrypted[80];
|
|
int enlen;
|
|
|
|
keys = secrets_readKeys();
|
|
if (!keys)
|
|
return strdup(crypt);
|
|
/*
|
|
** If the input is not a HEX string return the input
|
|
** it probably was not encrypted
|
|
*/
|
|
for (ptr = crypt; *ptr; ptr++)
|
|
{
|
|
if (!isxdigit(*ptr))
|
|
{
|
|
free(keys);
|
|
return strdup(crypt);
|
|
}
|
|
}
|
|
|
|
enlen = strlen(crypt) / 2;
|
|
gw_hex2bin(encrypted, crypt, strlen(crypt));
|
|
|
|
if ((plain = (unsigned char *)malloc(80)) == NULL)
|
|
{
|
|
free(keys);
|
|
return NULL;
|
|
}
|
|
|
|
AES_set_decrypt_key(keys->enckey, 8 * MAXSCALE_KEYLEN, &aeskey);
|
|
|
|
AES_cbc_encrypt(encrypted, plain, enlen, &aeskey, keys->initvector, AES_DECRYPT);
|
|
free(keys);
|
|
|
|
return (char *)plain;
|
|
}
|
|
|
|
/**
|
|
* Encrypt a password that can be stored in the MaxScale configuration file.
|
|
*
|
|
* Note the return is always a malloc'd string that the caller must free
|
|
*
|
|
* @param password The password to encrypt
|
|
* @return The encrypted password
|
|
*/
|
|
char *
|
|
encryptPassword(char *password)
|
|
{
|
|
MAXKEYS *keys;
|
|
AES_KEY aeskey;
|
|
int padded_len;
|
|
char *hex_output;
|
|
unsigned char padded_passwd[80];
|
|
unsigned char encrypted[80];
|
|
|
|
if ((keys = secrets_readKeys()) == NULL)
|
|
return NULL;
|
|
|
|
memset(padded_passwd, 0, 80);
|
|
strcpy((char *)padded_passwd, password);
|
|
padded_len = ((strlen(password) / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
|
|
|
|
AES_set_encrypt_key(keys->enckey, 8 * MAXSCALE_KEYLEN, &aeskey);
|
|
|
|
AES_cbc_encrypt(padded_passwd, encrypted, padded_len, &aeskey, keys->initvector, AES_ENCRYPT);
|
|
hex_output = (char *)malloc(padded_len * 2);
|
|
gw_bin2hex(hex_output, encrypted, padded_len);
|
|
free(keys);
|
|
|
|
return hex_output;
|
|
}
|