400 lines
11 KiB
C
400 lines
11 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#define _XOPEN_SOURCE
|
|
#include <unistd.h>
|
|
#include <crypt.h>
|
|
#include <users.h>
|
|
#include <adminusers.h>
|
|
#include <skygw_utils.h>
|
|
#include <log_manager.h>
|
|
|
|
/** Defined in log_manager.cc */
|
|
extern int lm_enabled_logfiles_bitmask;
|
|
extern size_t log_ses_count[];
|
|
extern __thread log_info_t tls_log_info;
|
|
|
|
/**
|
|
* @file adminusers.c - Administration user account management
|
|
*
|
|
* @verbatim
|
|
* Revision History
|
|
*
|
|
* Date Who Description
|
|
* 18/07/13 Mark Riddoch Initial implementation
|
|
* 23/07/13 Mark Riddoch Addition of error mechanism to add user
|
|
*
|
|
* @endverbatim
|
|
*/
|
|
static USERS *loadUsers();
|
|
static void initialise();
|
|
|
|
static USERS *users = NULL;
|
|
static int admin_init = 0;
|
|
|
|
static char *ADMIN_ERR_NOMEM = "Out of memory";
|
|
static char *ADMIN_ERR_FILEOPEN = "Unable to create password file";
|
|
static char *ADMIN_ERR_DUPLICATE = "Duplicate username specified";
|
|
static char *ADMIN_ERR_USERNOTFOUND = "User not found";
|
|
static char *ADMIN_ERR_AUTHENTICATION = "Authentication failed";
|
|
static char *ADMIN_ERR_FILEAPPEND = "Unable to append to password file";
|
|
static char *ADMIN_ERR_PWDFILEOPEN = "Failed to open password file";
|
|
static char *ADMIN_ERR_TMPFILEOPEN = "Failed to open temporary password file";
|
|
static char *ADMIN_ERR_PWDFILEACCESS = "Failed to access password file";
|
|
static char *ADMIN_ERR_DELLASTUSER = "Deleting the last user is forbidden";
|
|
static char *ADMIN_SUCCESS = NULL;
|
|
|
|
static const int LINELEN=80;
|
|
|
|
/**
|
|
* Admin Users initialisation
|
|
*/
|
|
static void
|
|
initialise()
|
|
{
|
|
if (admin_init)
|
|
return;
|
|
admin_init = 1;
|
|
users = loadUsers();
|
|
}
|
|
|
|
/**
|
|
* Verify a username and password
|
|
*
|
|
* @param username Username to verify
|
|
* @param password Password to verify
|
|
* @return Non-zero if the username/password combination is valid
|
|
*/
|
|
int
|
|
admin_verify(char *username, char *password)
|
|
{
|
|
char *pw;
|
|
|
|
initialise();
|
|
if (users == NULL)
|
|
{
|
|
if (strcmp(username, "admin") == 0 && strcmp(password, "mariadb") == 0)
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
if ((pw = users_fetch(users, username)) == NULL)
|
|
return 0;
|
|
if (strcmp(pw, crypt(password, ADMIN_SALT)) == 0)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* Load the admin users
|
|
*
|
|
* @return Table of users
|
|
*/
|
|
static USERS *
|
|
loadUsers()
|
|
{
|
|
USERS *rval;
|
|
FILE *fp;
|
|
char fname[1024], *home;
|
|
char uname[80], passwd[80];
|
|
|
|
initialise();
|
|
if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024){
|
|
sprintf(fname, "%s/etc/passwd", home);
|
|
}
|
|
else{
|
|
sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd");
|
|
}
|
|
if ((fp = fopen(fname, "r")) == NULL)
|
|
return NULL;
|
|
if ((rval = users_alloc()) == NULL)
|
|
{
|
|
fclose(fp);
|
|
return NULL;
|
|
}
|
|
while (fscanf(fp, "%[^:]:%s\n", uname, passwd) == 2)
|
|
{
|
|
users_add(rval, uname, passwd);
|
|
}
|
|
fclose(fp);
|
|
|
|
return rval;
|
|
}
|
|
|
|
/**
|
|
* Add user
|
|
*
|
|
* @param uname Name of the new user
|
|
* @param passwd Password for the new user
|
|
* @return NULL on success or an error string on failure
|
|
*/
|
|
char *
|
|
admin_add_user(char *uname, char *passwd)
|
|
{
|
|
FILE *fp;
|
|
char fname[1024], *home, *cpasswd;
|
|
|
|
initialise();
|
|
if ((home = getenv("MAXSCALE_HOME")) != NULL && strlen(home) < 1024){
|
|
sprintf(fname, "%s/etc/passwd", home);
|
|
}
|
|
else{
|
|
sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd");
|
|
}
|
|
|
|
if (users == NULL)
|
|
{
|
|
LOGIF(LM,
|
|
(skygw_log_write(LOGFILE_MESSAGE,
|
|
"Create initial password file.")));
|
|
|
|
if ((users = users_alloc()) == NULL)
|
|
return ADMIN_ERR_NOMEM;
|
|
if ((fp = fopen(fname, "w")) == NULL)
|
|
{
|
|
LOGIF(LE,
|
|
(skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Unable to create password file %s.",
|
|
fname)));
|
|
return ADMIN_ERR_PWDFILEOPEN;
|
|
}
|
|
fclose(fp);
|
|
}
|
|
if (users_fetch(users, uname) != NULL)
|
|
{
|
|
return ADMIN_ERR_DUPLICATE;
|
|
}
|
|
cpasswd = crypt(passwd, ADMIN_SALT);
|
|
users_add(users, uname, cpasswd);
|
|
if ((fp = fopen(fname, "a")) == NULL)
|
|
{
|
|
LOGIF(LE,
|
|
(skygw_log_write_flush(LOGFILE_ERROR,
|
|
"Error : Unable to append to password file %s.",
|
|
fname)));
|
|
return ADMIN_ERR_FILEAPPEND;
|
|
}
|
|
fprintf(fp, "%s:%s\n", uname, cpasswd);
|
|
fclose(fp);
|
|
return ADMIN_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove maxscale user from in-memory structure and from password file
|
|
*
|
|
* @param uname Name of the new user
|
|
* @param passwd Password for the new user
|
|
* @return NULL on success or an error string on failure
|
|
*/
|
|
char* admin_remove_user(
|
|
char* uname,
|
|
char* passwd)
|
|
{
|
|
FILE* fp;
|
|
FILE* fp_tmp;
|
|
char fname[1024];
|
|
char fname_tmp[1024];
|
|
char* home;
|
|
char fusr[LINELEN];
|
|
char fpwd[LINELEN];
|
|
char line[LINELEN];
|
|
fpos_t rpos;
|
|
int n_deleted;
|
|
|
|
if (!admin_search_user(uname)) {
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Couldn't find user %s. Removing user failed",
|
|
uname)));
|
|
return ADMIN_ERR_USERNOTFOUND;
|
|
}
|
|
|
|
if (admin_verify(uname, passwd) == 0) {
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Authentication failed, wrong user/password "
|
|
"combination. Removing user failed.")));
|
|
return ADMIN_ERR_AUTHENTICATION;
|
|
}
|
|
|
|
|
|
/** Remove user from in-memory structure */
|
|
n_deleted = users_delete(users, uname);
|
|
|
|
if (n_deleted == 0) {
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Deleting the only user is forbidden. Add new "
|
|
"user before deleting the one.")));
|
|
return ADMIN_ERR_DELLASTUSER;
|
|
}
|
|
/**
|
|
* Open passwd file and remove user from the file.
|
|
*/
|
|
if ((home = getenv("MAXSCALE_HOME")) != NULL &&
|
|
strnlen(home,PATH_MAX) < PATH_MAX &&
|
|
strnlen(home,PATH_MAX) > 0) {
|
|
sprintf(fname, "%s/etc/passwd", home);
|
|
sprintf(fname_tmp, "%s/etc/passwd_tmp", home);
|
|
} else {
|
|
sprintf(fname, "/usr/local/mariadb-maxscale/etc/passwd");
|
|
sprintf(fname_tmp, "/usr/local/mariadb-maxscale/etc/passwd_tmp");
|
|
}
|
|
/**
|
|
* Rewrite passwd file from memory.
|
|
*/
|
|
if ((fp = fopen(fname, "r")) == NULL)
|
|
{
|
|
int err = errno;
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Unable to open password file %s : errno %d.\n"
|
|
"Removing user from file failed; it must be done "
|
|
"manually.",
|
|
fname,
|
|
err)));
|
|
return ADMIN_ERR_PWDFILEOPEN;
|
|
}
|
|
/**
|
|
* Open temporary passwd file.
|
|
*/
|
|
if ((fp_tmp = fopen(fname_tmp, "w")) == NULL)
|
|
{
|
|
int err = errno;
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Unable to open tmp file %s : errno %d.\n"
|
|
"Removing user from passwd file failed; it must be done "
|
|
"manually.",
|
|
fname_tmp,
|
|
err)));
|
|
fclose(fp);
|
|
return ADMIN_ERR_TMPFILEOPEN;
|
|
}
|
|
|
|
/**
|
|
* Scan passwd and copy all but matching lines to temp file.
|
|
*/
|
|
if (fgetpos(fp, &rpos) != 0) {
|
|
int err = errno;
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Unable to process passwd file %s : errno %d.\n"
|
|
"Removing user from file failed, and must be done "
|
|
"manually.",
|
|
fname,
|
|
err)));
|
|
fclose(fp);
|
|
fclose(fp_tmp);
|
|
unlink(fname_tmp);
|
|
return ADMIN_ERR_PWDFILEACCESS;
|
|
}
|
|
|
|
while (fscanf(fp, "%[^:]:%s\n", fusr, fpwd) == 2)
|
|
{
|
|
/**
|
|
* Compare username what was found from passwd file.
|
|
* Unmatching lines are copied to tmp file.
|
|
*/
|
|
if (strncmp(uname, fusr, strlen(uname)+1) != 0) {
|
|
if(fsetpos(fp, &rpos) != 0){ /** one step back */
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Unable to set stream position. ")));
|
|
|
|
}
|
|
fgets(line, LINELEN, fp);
|
|
fputs(line, fp_tmp);
|
|
}
|
|
|
|
if (fgetpos(fp, &rpos) != 0) {
|
|
int err = errno;
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Unable to process passwd file %s : "
|
|
"errno %d.\n"
|
|
"Removing user from file failed, and must be "
|
|
"done manually.",
|
|
fname,
|
|
err)));
|
|
fclose(fp);
|
|
fclose(fp_tmp);
|
|
unlink(fname_tmp);
|
|
return ADMIN_ERR_PWDFILEACCESS;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
/**
|
|
* Replace original passwd file with new.
|
|
*/
|
|
if (rename(fname_tmp, fname)) {
|
|
int err = errno;
|
|
LOGIF(LE, (skygw_log_write_flush(
|
|
LOGFILE_ERROR,
|
|
"Error : Unable to rename new passwd file %s : errno "
|
|
"%d.\n"
|
|
"Rename it to %s manually.",
|
|
fname_tmp,
|
|
err,
|
|
fname)));
|
|
unlink(fname_tmp);
|
|
fclose(fp_tmp);
|
|
return ADMIN_ERR_PWDFILEACCESS;
|
|
}
|
|
fclose(fp_tmp);
|
|
return ADMIN_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Check for existance of the user
|
|
*
|
|
* @param user The user name to test
|
|
* @return Non-zero if the user exists
|
|
*/
|
|
int
|
|
admin_search_user(char *user)
|
|
{
|
|
initialise();
|
|
if (users == NULL)
|
|
return 0;
|
|
return users_fetch(users, user) != NULL;
|
|
}
|
|
|
|
/**
|
|
* Print the statistics and user names of the administration users
|
|
*
|
|
* @param dcb A DCB to send the output to
|
|
*/
|
|
void
|
|
dcb_PrintAdminUsers(DCB *dcb)
|
|
{
|
|
if (users)
|
|
dcb_usersPrint(dcb, users);
|
|
else
|
|
dcb_printf(dcb, "No administration users have been defined.\n");
|
|
}
|