MaxScale/server/core/adminusers.c
2015-03-23 20:12:56 +02:00

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");
}