mirror of
https://github.com/outbackdingo/sysadm.git
synced 2026-01-27 10:20:26 +00:00
547 lines
16 KiB
C++
547 lines
16 KiB
C++
//===========================================
|
|
// PC-BSD source code
|
|
// Copyright (c) 2015, PC-BSD Software/iXsystems
|
|
// Available under the 3-clause BSD license
|
|
// See the LICENSE file for full details
|
|
//===========================================
|
|
#include "sysadm-usermanager.h"
|
|
#include "sysadm-general.h"
|
|
using namespace sysadm;
|
|
|
|
UserManager::UserManager(QString chroot)
|
|
{
|
|
this->chroot = chroot;
|
|
loadUsers();
|
|
loadGroups();
|
|
loadShells();
|
|
}
|
|
|
|
void UserManager::NewUser(QString fullName, QString userName, QString password, QString home, QString shell, int uid, int gid, bool encrypt)
|
|
{
|
|
User user;
|
|
user.UserName = userName;
|
|
user.FullName = fullName;
|
|
user.HomeFolder = (home.isEmpty())?"/usr/home/"+userName : home;
|
|
user.Shell = shell;
|
|
|
|
//Add User
|
|
qDebug() << "Adding user " << userName;
|
|
// Create the zfs dataset associated with the home directory
|
|
if ( chroot.isEmpty() )
|
|
{
|
|
QStringList args;
|
|
args.append(user.HomeFolder);
|
|
General::RunCommand("/usr/local/share/pcbsd/scripts/mkzfsdir.sh",args);
|
|
}
|
|
|
|
QStringList args;
|
|
if ( ! chroot.isEmpty() ) //if chroot is not empty the command starts with chroot instead of pw
|
|
args << chroot << "pw"; //and thus we have to add it as an argument
|
|
args << "useradd"; //create a user
|
|
args << userName; //with this userName
|
|
args << "-c"; //sets the comment field
|
|
args << "\""+ fullName+"\""; //with the full name of the user
|
|
args << "-m"; //create the user's home directory
|
|
if(!home.isEmpty())
|
|
{
|
|
args << "-d"; //set the home directory to
|
|
args << home; //this
|
|
}
|
|
args << "-s"; //set the user's shell
|
|
args << shell; //to this
|
|
if(gid != -1)
|
|
{
|
|
args << "-g"; //set the group id to
|
|
args << QString::number(gid); //this
|
|
}
|
|
if(uid != -1)
|
|
{
|
|
args << "-u"; //set the user id to
|
|
args << QString::number(uid); //this
|
|
}
|
|
args << "-G"; //additionally add the user to
|
|
args << "operator"; //the operator's group
|
|
|
|
if ( ! chroot.isEmpty() ) //if we're operating with a chroot call
|
|
General::RunCommand("chroot", args);
|
|
else //otherwise
|
|
General::RunCommand("pw", args);
|
|
|
|
ChangeUserPassword(user,password);
|
|
|
|
//enable flash for the user
|
|
if ( chroot.isEmpty() ) { //if we're not in a chroot
|
|
qDebug() << "Enabling Flash Plugin for " << userName;
|
|
args.clear();
|
|
args << userName; //run command as this user
|
|
args << "-c"; //with the command
|
|
args << "\"flashpluginctl on\""; //turn on flashpluginctl
|
|
General::RunCommand("su",args);
|
|
}
|
|
|
|
//if we're going to PersonaCrypt the home directory
|
|
if(encrypt)
|
|
initPCDevice(user,home,password);
|
|
|
|
//reloads the groups and users so that the internal model is consistent
|
|
loadUsers();
|
|
loadGroups();
|
|
}
|
|
|
|
void UserManager::DeleteUser(User user)
|
|
{
|
|
//Delete User
|
|
qDebug() << "Deleting user " << user.UserName;
|
|
|
|
//remove the dataset associated with the home folder
|
|
QStringList args;
|
|
args << user.HomeFolder;
|
|
General::RunCommand("/usr/local/share/pcbsd/scripts/rmzfsdir.sh",args);
|
|
|
|
//delete the user and their home directory
|
|
args.clear();
|
|
if ( ! chroot.isEmpty() ) //if we're in a chroot we need to use chroot before pw
|
|
args << chroot << "pw";
|
|
args << "userdel"; //delete a user
|
|
args << user.UserName; //this user
|
|
args << "-r"; //remove the contents of the user's home directory
|
|
if ( ! chroot.isEmpty() )
|
|
General::RunCommand("chroot", args);
|
|
else
|
|
General::RunCommand("pw", args);
|
|
|
|
loadUsers();
|
|
loadGroups();
|
|
}
|
|
|
|
const QVector<User> UserManager::GetUsers()
|
|
{
|
|
return users;
|
|
}
|
|
|
|
const User UserManager::GetUser(int id)
|
|
{
|
|
for(User user: users)
|
|
{
|
|
if(user.ID == id)
|
|
return user;
|
|
}
|
|
return User();
|
|
}
|
|
|
|
const User UserManager::GetUser(QString userName)
|
|
{
|
|
for(User user: users)
|
|
{
|
|
if(user.UserName == userName)
|
|
return user;
|
|
}
|
|
return User();
|
|
}
|
|
|
|
void UserManager::ChangeUserPassword(User user, QString newPassword)
|
|
{
|
|
//Don't Change the password of a user with an encrypted Home directory
|
|
if( !QFile::exists("/var/db/personacrypt/"+user.UserName+".key") ){ return; }
|
|
|
|
//Create a temporary file to store the password in
|
|
QTemporaryFile nfile("/tmp/.XXXXXXXX");
|
|
if ( nfile.open() )
|
|
{
|
|
QTextStream stream( &nfile );
|
|
stream << newPassword;
|
|
nfile.close();
|
|
}
|
|
|
|
//set the user password
|
|
QStringList args;
|
|
args.append(nfile.fileName()); //the temp file holding the password
|
|
args.append("|"); //which we're going to pipe to the stdin of
|
|
if ( ! chroot.isEmpty() ) //if we're in a chroot
|
|
{
|
|
args << "chroot"; //a chroot
|
|
args << chroot; //located here
|
|
}
|
|
args << "pw"; //change users
|
|
args << "usermod"; //where we're going to modify a user
|
|
args << user.UserName;//this user
|
|
args << "-h"; //set the user's password
|
|
args << "0"; //using stdin
|
|
General::RunCommand("cat",args);
|
|
|
|
//remove the temp file holding the password
|
|
nfile.remove();
|
|
|
|
}
|
|
|
|
void UserManager::ChangeUserShell(User user, QString shell)
|
|
{
|
|
if(shells.contains(shell))
|
|
{
|
|
qDebug("Shell found");
|
|
QStringList args;
|
|
args << "usermod"; // modify the user
|
|
args << "-n"; //specify a user name
|
|
args << user.UserName; //for this user
|
|
args << "-s"; //set the shell to
|
|
args << shell; //this shell
|
|
General::RunCommand("pw",args);
|
|
}
|
|
else
|
|
qDebug("Shell not found");
|
|
|
|
loadUsers();
|
|
}
|
|
|
|
void UserManager::ChangeUserFullName(User user, QString newName)
|
|
{
|
|
QStringList args;
|
|
args << "usermod"; //modify the user
|
|
args << user.UserName; //for this user
|
|
args << "-c"; //change the gecos field to
|
|
args << newName; //this name
|
|
General::RunCommand("pw",args);
|
|
loadUsers();
|
|
}
|
|
|
|
void UserManager::AddUserToGroup(User user, Group group)
|
|
{
|
|
QStringList args;
|
|
args << "groupmod"; //modify a group
|
|
args << "-n"; //modify for a group
|
|
args << group.Name;//this group
|
|
args << "-m";//by adding a member
|
|
args << user.UserName; //this user
|
|
General::RunCommand("pw",args);
|
|
|
|
loadGroups();
|
|
}
|
|
|
|
void UserManager::RemoveUserFromGroup(User user, Group group)
|
|
{
|
|
QStringList args;
|
|
args << "groupmod"; //modify a group
|
|
args << "-n"; //modify for a group
|
|
args << group.Name; //this group
|
|
args << "-d"; //by removing a user
|
|
args << user.UserName ; //this user
|
|
General::RunCommand("pw", args);
|
|
|
|
loadGroups();
|
|
}
|
|
|
|
void UserManager::NewGroup(QString name, QStringList members)
|
|
{
|
|
QStringList args;
|
|
qDebug() << "Adding group " << name;
|
|
if ( ! chroot.isEmpty() ) //if we're in a chroot need to add chroot before pw
|
|
args << chroot << "pw";
|
|
args << "groupadd"; //create a new group
|
|
args << name; // with this name
|
|
args << "-M"; //with this list of users
|
|
args << members.join(","); //these guys
|
|
if ( ! chroot.isEmpty() ) //if we're in a chroot
|
|
General::RunCommand("chroot", args);
|
|
else
|
|
General::RunCommand("pw", args);
|
|
|
|
loadGroups();
|
|
}
|
|
|
|
void UserManager::DeleteGroup(Group group)
|
|
{
|
|
QStringList args;
|
|
qDebug() << "Deleting group " << group.Name;
|
|
if ( ! chroot.isEmpty() ) //if we're in a chroot need to add chroot before pw
|
|
args << chroot << "pw";
|
|
args << "groupdel"; //delete a group
|
|
args << group.Name; //of this name
|
|
if ( ! chroot.isEmpty() ) //if we're in a chroot
|
|
General::RunCommand("chroot", args);
|
|
else
|
|
General::RunCommand("pw", args);
|
|
|
|
loadGroups();
|
|
}
|
|
|
|
const QVector<Group> UserManager::GetGroups()
|
|
{
|
|
return groups;
|
|
}
|
|
|
|
const Group UserManager::getGroup(int id)
|
|
{
|
|
for(Group group : groups)
|
|
{
|
|
if(group.ID == id)
|
|
return group;
|
|
}
|
|
return Group();
|
|
}
|
|
|
|
const Group UserManager::getGroup(QString name)
|
|
{
|
|
for(Group group : groups)
|
|
{
|
|
if(group.Name == name)
|
|
return group;
|
|
}
|
|
return Group();
|
|
}
|
|
|
|
const QStringList UserManager::GetShells()
|
|
{
|
|
return shells;
|
|
}
|
|
|
|
void UserManager::loadUsers()
|
|
{
|
|
users.clear();
|
|
QStringList userStrings;
|
|
QStringList args;
|
|
if(!chroot.isEmpty())
|
|
{
|
|
args << chroot;
|
|
args << "pw";
|
|
}
|
|
args << "usershow";
|
|
args << "-a";
|
|
if(chroot.isEmpty())
|
|
userStrings = General::RunCommand("pw",args).split("\n");
|
|
else
|
|
userStrings = General::RunCommand("chroot",args).split("\n");
|
|
|
|
//remove the empty string at the end
|
|
userStrings.removeLast();
|
|
|
|
for(QString line : userStrings)
|
|
{
|
|
User user;
|
|
user.UserName = line.section(":",0,0);
|
|
user.ID = line.section(":",2,2).toInt();
|
|
user.GroupID = line.section(":",3,3).toInt();
|
|
user.HomeFolder = line.section(":",8,8);
|
|
user.Shell = line.section(":",9,9);
|
|
user.FullName = line.section(":",7,7);
|
|
|
|
users.append(user);
|
|
}
|
|
}
|
|
|
|
void UserManager::loadGroups()
|
|
{
|
|
groups.clear();
|
|
QStringList groupStrings;
|
|
QStringList args;
|
|
if(!chroot.isEmpty())
|
|
{
|
|
args << chroot;
|
|
args << "pw";
|
|
}
|
|
args << "groupshow";
|
|
args << "-a";
|
|
if(chroot.isEmpty())
|
|
groupStrings = General::RunCommand("pw",args).split("\n");
|
|
else
|
|
groupStrings = General::RunCommand("chroot",args).split("\n");
|
|
|
|
//remove the empty string at the end
|
|
groupStrings.removeLast();
|
|
|
|
for(QString line : groupStrings)
|
|
{
|
|
Group group;
|
|
group.Name = line.section(":",0,0);
|
|
group.ID = line.section(":",2,2).toInt();
|
|
QString memberString = line.section(":",3,3);
|
|
group.Members = memberString.split(",");
|
|
|
|
groups.append(group);
|
|
}
|
|
}
|
|
|
|
void UserManager::loadShells()
|
|
{
|
|
shells.clear();
|
|
QFile shellFile(chroot + "/etc/shells");
|
|
if ( shellFile.open(QIODevice::ReadOnly) ) {
|
|
QTextStream stream(&shellFile);
|
|
stream.setCodec("UTF-8");
|
|
|
|
QString line;
|
|
|
|
while ( !stream.atEnd() ) {
|
|
line = stream.readLine();
|
|
|
|
if ( !line.startsWith("#") && !line.isEmpty() ) { //Make sure it isn't a comment or blank
|
|
shells.append(line);
|
|
}
|
|
}
|
|
} else {
|
|
//Unable to open file error
|
|
qWarning("Error! Unable to open /etc/shells");
|
|
}
|
|
|
|
// Add /sbin/nologin as well
|
|
shells.append("/sbin/nologin");
|
|
}
|
|
|
|
|
|
void UserManager::importPCKey(User user, QString filename){
|
|
//Double check that the key does not exist (button should have been hidden earlier if invalid)
|
|
if( QFile::exists("/var/db/personacrypt/"+user.UserName+".key") ){ return; }
|
|
|
|
//if the location is empty cancel
|
|
if(filename.isEmpty()){ return; }
|
|
|
|
//Now run the import command
|
|
QStringList args;
|
|
args << "import";
|
|
args << "\""+filename + "\"";
|
|
if( 0 == General::RunCommand("personacrypt",args) ){
|
|
//Success
|
|
qDebug("The key file was imported successfully.");
|
|
}else{
|
|
//Failure
|
|
qWarning("The key file could not be imported. Please ensure you are using a valid file.");
|
|
}
|
|
}
|
|
|
|
void UserManager::exportPCKey(User user, QString filename){
|
|
//Double check that the key exists (button should have been hidden earlier if invalid)
|
|
if( !QFile::exists("/var/db/personacrypt/"+user.UserName+".key") ){ return; }
|
|
|
|
if(filename.isEmpty()){ return; } //cancelled
|
|
if( !filename.endsWith(".key") ){ filename.append(".key"); }
|
|
//Now get/save the key file
|
|
QStringList args;
|
|
args << "export";
|
|
args << "\"" + user.UserName + "\"";
|
|
QString key = General::RunCommand("personacrypt",args);
|
|
|
|
QFile file(filename);
|
|
if( !file.open(QIODevice::WriteOnly | QIODevice::Truncate) ){
|
|
//Could not open output file
|
|
qWarning() <<"Output file could not be opened:\n\n" << filename;
|
|
return;
|
|
}
|
|
QTextStream out(&file);
|
|
out << key;
|
|
file.close();
|
|
qDebug() << "The PersonaCrypt key has been saved successfully: \n\n" << filename;
|
|
}
|
|
|
|
void UserManager::disablePCKey(User user){
|
|
//Double check that the key exists (button should have been hidden earlier if invalid)
|
|
if( !QFile::exists("/var/db/personacrypt/"+user.UserName+".key") ){ return; }
|
|
|
|
if( QFile::remove("/var/db/personacrypt/"+user.UserName+".key") ){
|
|
//Success
|
|
qDebug("The PersonaCrypt user key has been disabled." );
|
|
}else{
|
|
//Failure (should almost never happen, since this utility runs as root and just needs to delete a file)
|
|
qDebug("The PersonaCrypt user key could not be removed. Do you have the proper permissions?" );
|
|
}
|
|
}
|
|
|
|
void UserManager::disableAndCopyPCKey(User user, QString password){
|
|
QStringList args;
|
|
args << "list";
|
|
QStringList cusers = General::RunCommand("personacrypt",args).split("\n");
|
|
bool available = false;
|
|
for(int i=0; i<cusers.length(); i++){
|
|
if(cusers[i].section(" on ",0,0) == user.UserName){ available = true; break; } //disk is connected to the system
|
|
}
|
|
if(!available){
|
|
//Warn the user that they need to plug in their USB stick first
|
|
qWarning("PersonaCrypt Device Not Found, Please ensure that your PersonaCrypt device is connected to the system and try again.");
|
|
return;
|
|
}
|
|
|
|
if(password.isEmpty()){ return; } //cancelled
|
|
//Save the password to a temporary file
|
|
QTemporaryFile tmpfile("/tmp/.XXXXXXXXXXXXXXXXXXXX");
|
|
if( !tmpfile.open() ){ return; } //could not create a temporary file (extremely rare)
|
|
QTextStream out(&tmpfile);
|
|
out << password;
|
|
tmpfile.close();
|
|
|
|
//Now run the PersonaCrypt command
|
|
args.clear();
|
|
args << "remove";
|
|
args << "\"" + user.UserName + "\"";
|
|
args << "\"" + tmpfile.fileName() + "\"";
|
|
if(0 == General::RunCommand("personacrypt",args) ){
|
|
//Success
|
|
qDebug("Success; The data for this user has been merged onto the system and the system key has been disabled");
|
|
}else{
|
|
//Failure
|
|
qWarning("Failure; The PersonaCrypt user data could not be merged onto the system. Invalid Password?" );
|
|
}
|
|
}
|
|
|
|
void UserManager::initPCDevice(User user, QString home, QString password)
|
|
{
|
|
//Double check that the key does not exist (button should have been hidden earlier if invalid)
|
|
if( QFile::exists("/var/db/personacrypt/" + user.UserName + ".key") ){ return; }
|
|
|
|
//Prompt for the user to select a device
|
|
QStringList args;
|
|
args << "list";
|
|
args << "-r";
|
|
QStringList devlist = General::RunCommand("personacrypt",args).split("\n");
|
|
for(int i=0; i<devlist.length(); i++){
|
|
//qDebug() << "Devlist:" << devlist[i];
|
|
if(devlist[i].isEmpty() || devlist[i].startsWith("gpart:"))
|
|
{
|
|
devlist.removeAt(i);
|
|
i--;
|
|
}
|
|
}
|
|
if(devlist.isEmpty() || devlist.join("").simplified().isEmpty()){
|
|
qWarning("No Devices Found; Please connect a removable device and try again");
|
|
return;
|
|
}
|
|
|
|
args.clear();
|
|
args << "-h";
|
|
args << user.HomeFolder;
|
|
bool ok = false;
|
|
QString space = General::RunCommand("df -h "+home).split("\n").filter(home).join("");
|
|
space.replace("\t"," ");
|
|
space = space.section(" ",2,2,QString::SectionSkipEmpty);
|
|
|
|
if(!ok || home.isEmpty()){ return; }
|
|
|
|
home = home.section(":",0,0); //only need the raw device
|
|
//Save the password to a temporary file (for input to personacrypt)
|
|
QTemporaryFile tmpfile("/tmp/.XXXXXXXXXXXXXXX");
|
|
if(!tmpfile.open()){
|
|
//Error: could not open a temporary file
|
|
qWarning("Error; Could not create a temporary file for personacrypt");
|
|
return;
|
|
}
|
|
QTextStream out(&tmpfile);
|
|
out << password;
|
|
tmpfile.close();
|
|
//Now start the process of setting up the device
|
|
bool success = false;
|
|
args.clear();
|
|
args << "init";
|
|
args << "\""+user.UserName + "\"";
|
|
args << "\"" + tmpfile.fileName() + "\"";
|
|
args << home;
|
|
QStringList output = General::RunCommand(success,"personacrypt",args).split("\n");
|
|
if(success){
|
|
//Success
|
|
qDebug("Success; The PersonaCrypt device was successfully initialized");
|
|
}else{
|
|
//Failure - make sure the key was not created before the failure
|
|
if(QFile::exists("/var/db/personacrypt/"+user.UserName+".key")){
|
|
QFile::remove("/var/db/personacrypt/"+user.UserName+".key");
|
|
}
|
|
//Now show the error message with the log
|
|
qWarning("Failure; The PersonaCrypt device could not be initialized");
|
|
}
|
|
}
|