<commit_before>/* PCDM Login Manager:
*  Written by <NAME> (<EMAIL>) 2012/2013
*  Modified by <NAME> (<EMAIL>) 2013
*  Copyright(c) 2013 by the PC-BSD Project
*  Available under the 3-clause BSD license
*/

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pwd.h>
#include <login_cap.h>
#include <QMessageBox>
#include <QTemporaryFile>
#include <QThread>
#include <QCoreApplication>

/*
Sub-classed QProcess for starting an XSession Process
*/

#include "pcdm-xprocess.h"

#define DEBUG 0
extern UserList *USERS;

XProcess::XProcess() : QProcess(0) {
  //initialize the variables
  xuser.clear();
  xcmd.clear();
  xhome.clear();
  xpwd.clear();
  xshell.clear();
  pam_started = false;
  pam_session_open = false;
  //Setup the finished signal/slot
  //connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) );
}

XProcess::~XProcess(){
  if( isRunning() ){
    this->terminate();
  }
  this->close();
}

void XProcess::loginToXSession(QString username, QString password, QString desktop, QString lang, QString devPassword, bool anon){
  //Setup the variables
  xuser = username;
  xpwd = password;
  xhome = USERS->homedir(xuser);
  xcmd = Backend::getDesktopBinary(desktop);
  xshell = USERS->shell(xuser);
  xde = desktop;
  xlang = lang;
  xdevpass = devPassword;
  xanonlogin = anon;
  //Now start the login process
  if( !startXSession() ){
    //Could not continue after session changed significantly - close down the session to restart
    QCoreApplication::exit(-1); //special code to make sure we are just restarting the PCDM session (not the full X session)
  }
}

bool XProcess::isRunning(){
  if(this->state() != QProcess::NotRunning){ return true; }
  else{ return false; }
}

void XProcess::waitForSessionClosed(){
  // CAUTION!! 
  // This function will pause the calling program to wait for the session to end!
  if( isRunning() ){ this->waitForFinished(-1); }

}

/*
 ========== SESSION STARTUP ==========
*/

bool XProcess::startXSession(){
  //Returns true if the session can continue, or false if it needs to be closed down

  //Check that the necessary info to start the session is available
  if( xuser.isEmpty() || xcmd.isEmpty() || xhome.isEmpty() || xde.isEmpty() ){
    emit InvalidLogin();  //Make sure the GUI knows that it was a failure
    return true;
  }
  //Backend::log("Starting up Desktop environment ("+xcmd+") as user ("+xuser+")");
  
  //Check for PAM username/password validity
  if( !pam_checkPW() ){ emit InvalidLogin(); pam_shutdown(); return true; }

  //If this has a special device password, mount the personacrypt device
  if( !xanonlogin && !xdevpass.isEmpty() ){ //&& Backend::getAvailablePersonaCryptUsers().contains(xuser) ){
    Backend::log(" - PersonaCrypt Login Detected");
    if( !Backend::MountPersonaCryptUser(xuser, xdevpass) ){ 
      //Could not mount the personacrypt device (invalid password?)
      xdevpass.clear(); //clear the invalid password
      emit InvalidLogin(); pam_shutdown(); return true; 
    }else{
      //overwrite the password in memory, but leave it flagged (not empty)
      if(DEBUG){ qDebug() << "Mounted PersonaCrypt Device"; }
      xdevpass.clear();
      xdevpass = "<PASSWORD>"; 
    }
  }

  //Save the current user/desktop as the last login
  Backend::saveLoginInfo(xuser, xhome, xde);

  // Get the users uid/gid information
  struct passwd *pw;
  int uid;
  char *ok;

  if (!(pw = getpwnam(xuser.toLatin1()))) {
      uid = strtol(xuser.toLatin1(), &ok, 10);
      if (!(pw = getpwuid(uid))) {
          return false;
      }
  }

  //Emit the last couple logs before dropping privileges
  Backend::log("Starting session:");
  Backend::log(" - Session Log: ~/.pcdm-startup.log");
  QProcess::execute("killall compton");
  //For sanity's sake, ensure that the ZFS mountpoint are all available first
  if(QFile::exists("/sbin/zfs")){
    QProcess::execute("zfs mount -a"); //just to ensure the user's home dir is actually mounted
  }
  //Check/create the user's home-dir before dropping privs
  if(!QFile::exists(xhome)){
    qDebug() << "No Home dir found - populating...";
    QString hmcmd = "pw usermod "+xuser+" -m";
    QProcess::execute(hmcmd);
  }
  //If this is an anonymous login, create the blank home-dir on top
  if(xanonlogin){
    if(DEBUG){ qDebug() << " - Stealth Session selected"; }
    QProcess::execute("personacrypt tempinit "+xuser+" 10G"); //always use 10GB blank dir
  }
  
  // Get the environment before we drop priv
  this->setProcessEnvironment( QProcessEnvironment::systemEnvironment() ); //current environment

  //Now allow this user access to the Xserver
  QString xhostcmd = "xhost si:localuser:"+xuser;
  QProcess::execute(xhostcmd);
  //And finally set the login user before dropping priv
  setlogin( xuser.toUtf8() );

  //Startup the PAM session
  if( !pam_startSession() ){ pam_shutdown(); return false; }
  pam_session_open = true; //flag that pam has an open session

  QString cmd;

  // Setup the process environment
  setupSessionEnvironment();

  // Create our startup script
  tFile = new QTemporaryFile();
  if ( ! tFile->open() )
     return false;

  QTextStream tOut(tFile);

  // Configure the DE startup command
  if(QFile::exists("/usr/local/bin/dbus-launch")){
    cmd.append("dbus-launch --exit-with-session "+xcmd);
  }

  QString tUid, tGid, logFile;
  tUid.setNum(pw->pw_uid);
  tGid.setNum(pw->pw_gid);
  logFile=xhome + "/.pcdm-startup.log";

  QString authfile = qgetenv("XAUTHORITY");
  // Change ownership on the Xauthority file
  QProcess::execute("chown "+tUid+":"+tGid+" "+authfile);

  //Need to run a couple commands in sequence: so put them in a script file
  tOut << "#!/bin/sh\n\n";

  QString PICOCLIENT = qgetenv("PICO_CLIENT_LOGIN");
  QString PICOHOME = qgetenv("PICO_CLIENT_HOME");
  if ( ! PICOCLIENT.isEmpty() && ! PICOHOME.isEmpty() ) {
    tOut << "rm "+authfile+"\n";
    tOut << "ln -fs "+PICOHOME+"/.Xauthority "+authfile+"\n";
    QProcess::execute("chown "+tUid+" "+PICOHOME+"/.Xauthority"); // Change ownership on the pico login
  }
<commit_msg>Properly set the XAUTHORITY file path when logging in via PICO<commit_after>/* PCDM Login Manager:
*  Written by <NAME> (<EMAIL>) 2012/2013
*  Modified by <NAME> (<EMAIL>) 2013
*  Copyright(c) 2013 by the PC-BSD Project
*  Available under the 3-clause BSD license
*/

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pwd.h>
#include <login_cap.h>
#include <QMessageBox>
#include <QTemporaryFile>
#include <QThread>
#include <QCoreApplication>

/*
Sub-classed QProcess for starting an XSession Process
*/

#include "pcdm-xprocess.h"

#define DEBUG 0
extern UserList *USERS;

XProcess::XProcess() : QProcess(0) {
  //initialize the variables
  xuser.clear();
  xcmd.clear();
  xhome.clear();
  xpwd.clear();
  xshell.clear();
  pam_started = false;
  pam_session_open = false;
  //Setup the finished signal/slot
  //connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) );
}

XProcess::~XProcess(){
  if( isRunning() ){
    this->terminate();
  }
  this->close();
}

void XProcess::loginToXSession(QString username, QString password, QString desktop, QString lang, QString devPassword, bool anon){
  //Setup the variables
  xuser = username;
  xpwd = password;
  xhome = USERS->homedir(xuser);
  xcmd = Backend::getDesktopBinary(desktop);
  xshell = USERS->shell(xuser);
  xde = desktop;
  xlang = lang;
  xdevpass = <PASSWORD>;
  xanonlogin = anon;
  //Now start the login process
  if( !startXSession() ){
    //Could not continue after session changed significantly - close down the session to restart
    QCoreApplication::exit(-1); //special code to make sure we are just restarting the PCDM session (not the full X session)
  }
}

bool XProcess::isRunning(){
  if(this->state() != QProcess::NotRunning){ return true; }
  else{ return false; }
}

void XProcess::waitForSessionClosed(){
  // CAUTION!! 
  // This function will pause the calling program to wait for the session to end!
  if( isRunning() ){ this->waitForFinished(-1); }

}

/*
 ========== SESSION STARTUP ==========
*/

bool XProcess::startXSession(){
  //Returns true if the session can continue, or false if it needs to be closed down

  //Check that the necessary info to start the session is available
  if( xuser.isEmpty() || xcmd.isEmpty() || xhome.isEmpty() || xde.isEmpty() ){
    emit InvalidLogin();  //Make sure the GUI knows that it was a failure
    return true;
  }
  //Backend::log("Starting up Desktop environment ("+xcmd+") as user ("+xuser+")");
  
  //Check for PAM username/password validity
  if( !pam_checkPW() ){ emit InvalidLogin(); pam_shutdown(); return true; }

  //If this has a special device password, mount the personacrypt device
  if( !xanonlogin && !xdevpass.isEmpty() ){ //&& Backend::getAvailablePersonaCryptUsers().contains(xuser) ){
    Backend::log(" - PersonaCrypt Login Detected");
    if( !Backend::MountPersonaCryptUser(xuser, xdevpass) ){ 
      //Could not mount the personacrypt device (invalid password?)
      xdevpass.clear(); //clear the invalid password
      emit InvalidLogin(); pam_shutdown(); return true; 
    }else{
      //overwrite the password in memory, but leave it flagged (not empty)
      if(DEBUG){ qDebug() << "Mounted PersonaCrypt Device"; }
      xdevpass.clear();
      xdevpass = "<PASSWORD>"; 
    }
  }

  //Save the current user/desktop as the last login
  Backend::saveLoginInfo(xuser, xhome, xde);

  // Get the users uid/gid information
  struct passwd *pw;
  int uid;
  char *ok;

  if (!(pw = getpwnam(xuser.toLatin1()))) {
      uid = strtol(xuser.toLatin1(), &ok, 10);
      if (!(pw = getpwuid(uid))) {
          return false;
      }
  }

  //Emit the last couple logs before dropping privileges
  Backend::log("Starting session:");
  Backend::log(" - Session Log: ~/.pcdm-startup.log");
  QProcess::execute("killall compton");
  //For sanity's sake, ensure that the ZFS mountpoint are all available first
  if(QFile::exists("/sbin/zfs")){
    QProcess::execute("zfs mount -a"); //just to ensure the user's home dir is actually mounted
  }
  //Check/create the user's home-dir before dropping privs
  if(!QFile::exists(xhome)){
    qDebug() << "No Home dir found - populating...";
    QString hmcmd = "pw usermod "+xuser+" -m";
    QProcess::execute(hmcmd);
  }
  //If this is an anonymous login, create the blank home-dir on top
  if(xanonlogin){
    if(DEBUG){ qDebug() << " - Stealth Session selected"; }
    QProcess::execute("personacrypt tempinit "+xuser+" 10G"); //always use 10GB blank dir
  }
  
  // Get the environment before we drop priv
  this->setProcessEnvironment( QProcessEnvironment::systemEnvironment() ); //current environment

  //Now allow this user access to the Xserver
  QString xhostcmd = "xhost si:localuser:"+xuser;
  QProcess::execute(xhostcmd);
  //And finally set the login user before dropping priv
  setlogin( xuser.toUtf8() );

  //Startup the PAM session
  if( !pam_startSession() ){ pam_shutdown(); return false; }
  pam_session_open = true; //flag that pam has an open session

  QString cmd;

  // Setup the process environment
  setupSessionEnvironment();

  // Create our startup script
  tFile = new QTemporaryFile();
  if ( ! tFile->open() )
     return false;

  QTextStream tOut(tFile);

  // Configure the DE startup command
  if(QFile::exists("/usr/local/bin/dbus-launch")){
    cmd.append("dbus-launch --exit-with-session "+xcmd);
  }

  QString tUid, tGid, logFile;
  tUid.setNum(pw->pw_uid);
  tGid.setNum(pw->pw_gid);
  logFile=xhome + "/.pcdm-startup.log";

  // Change ownership on the Xauthority file
  QString authfile = qgetenv("XAUTHORITY");
  if ( authfile.isEmpty() )
    authfile = xhome + "/.Xauthority";
  QProcess::execute("chown "+tUid+":"+tGid+" "+authfile);

  //Need to run a couple commands in sequence: so put them in a script file
  tOut << "#!/bin/sh\n\n";

  QString PICOCLIENT = qgetenv("PICO_CLIENT_LOGIN");
  QString PICOHOME = qgetenv("PICO_CLIENT_HOME");
  if ( ! PICOCLIENT.isEmpty() && ! PICOHOME.isEmpty() ) {
    tOut << "rm "+authfile+"\n";
    tOut << "ln -fs "+PICOHOME+"/.Xauthority "+authfile+"\n";
    QProcess::execute("chown "+tUid+" "+PICOHOME+"/.Xauthority"); // Change ownership on the pico login
  }

{'fraction_non_alphanumeric': 0.1444037444037444, 'fraction_numerical': 0.004884004884004884, 'mean_word_length': 3.8086105675146773, 'pattern_counts': {'":': 2, '<': 56, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3379219', 'n_tokens_mistral': 4121, 'n_tokens_neox': 3981, 'n_words': 1193}