386 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			386 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>
 | 
						|
#ifndef _XOPEN_SOURCE
 | 
						|
#define _XOPEN_SOURCE 700
 | 
						|
#endif
 | 
						|
#include <unistd.h>
 | 
						|
#include <crypt.h>
 | 
						|
#include <users.h>
 | 
						|
#include <adminusers.h>
 | 
						|
#include <skygw_utils.h>
 | 
						|
#include <log_manager.h>
 | 
						|
#include <gwdirs.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();
 | 
						|
    sprintf(fname, "%s/passwd", get_datadir());
 | 
						|
	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();
 | 
						|
    sprintf(fname, "%s/passwd", get_datadir());
 | 
						|
        
 | 
						|
	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.
 | 
						|
         */
 | 
						|
        sprintf(fname, "%s/passwd", get_datadir());
 | 
						|
        sprintf(fname_tmp, "%s/passwd_tmp", get_datadir());
 | 
						|
        /**
 | 
						|
         * 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");
 | 
						|
}
 |