//Stand-alone function to check a username/password combination for validity void XProcess::checkPW(QString user, QString pwd){ xuser = Backend::getUsernameFromDisplayname(user); xpwd = pwd; bool ok = pam_checkPW(); pam_shutdown(); xuser.clear(); xpwd.clear(); if(ok){ emit ValidLogin(); } else{ emit InvalidLogin(); } }
//Stand-alone function to check a username/password combination for validity void XProcess::checkPW(QString user, QString pwd){ if(USERS->isReady(user)){ xuser = user; } else{ xuser = USERS->findUser(user); } //convert display name into username //xuser = Backend::getUsernameFromDisplayname(user); xpwd = pwd; bool ok = pam_checkPW(); pam_shutdown(); xuser.clear(); xpwd.clear(); if(ok){ emit ValidLogin(); } else{ emit InvalidLogin(); } }
void XProcess::setupDesktop(QString user, QString pwd, QString desktop){ //Setup internal variables xuser = Backend::getUsernameFromDisplayname(user); xpwd = pwd; xhome = Backend::getUserHomeDir(xuser); xcmd = Backend::getDesktopBinary(desktop); xde = desktop; //Also check password validity bool ok = pam_checkPW(); pam_shutdown(); if(ok){ emit ValidLogin(); } else{ emit InvalidLogin(); } }
int runSingleSession(int argc, char *argv[]){ //QTime clock; //clock.start(); Backend::checkLocalDirs(); // Create and fill "/usr/local/share/PCDM" if needed Backend::openLogFile("/var/log/PCDM.log"); //qDebug() << "Backend Checks Finished:" << QString::number(clock.elapsed())+" ms"; //Check for the flag to try and auto-login bool ALtriggered = FALSE; if(QFile::exists(TMPAUTOLOGINFILE)){ ALtriggered=TRUE; QFile::remove(TMPAUTOLOGINFILE); } QString changeLang; // Load the configuration file QString confFile = "/usr/local/etc/pcdm.conf"; if(!QFile::exists(confFile)){ qDebug() << "PCDM: Configuration file missing:"<<confFile<<"\n - Using default configuration"; confFile = ":samples/pcdm.conf"; } Config::loadConfigFile(confFile); //qDebug() << "Config File Loaded:" << QString::number(clock.elapsed())+" ms"; // Startup the main application QApplication a(argc,argv); // Show our splash screen, so the user doesn't freak that that it takes a few seconds to show up QSplashScreen splash; if(!Config::splashscreen().isEmpty()){ splash.setPixmap( QPixmap(Config::splashscreen()) ); //load the splashscreen file } splash.show(); QCoreApplication::processEvents(); //Process the splash screen event immediately //qDebug() << "SplashScreen Started:" << QString::number(clock.elapsed())+" ms"; //Initialize the xprocess XProcess desktop; //*** STARTUP THE PROGRAM *** bool goodAL = FALSE; //Flag for whether the autologin was successful // Start the autologin procedure if applicable if( ALtriggered && Config::useAutoLogin() ){ //Setup the Auto Login QString user = Backend::getALUsername(); QString pwd = Backend::getALPassword(); QString dsk = Backend::getLastDE(user); if( user.isEmpty() || dsk.isEmpty() ){ goodAL=FALSE; }else{ desktop.loginToXSession(user,pwd, dsk); splash.close(); if(desktop.isRunning()){ goodAL=TRUE; //flag this as a good login to skip the GUI } } } //qDebug() << "AutoLogin Finished:" << QString::number(clock.elapsed())+" ms"; if(!goodAL){ // ------START THE PCDM GUI------- // Check what directory our app is in QString appDir = "/usr/local/share/PCDM"; // Load the translator QTranslator translator; QLocale mylocale; QString langCode = mylocale.name(); //Check for a language change detected if ( ! changeLang.isEmpty() ) langCode = changeLang; //Load the proper locale for the translator if ( QFile::exists(appDir + "/i18n/PCDM_" + langCode + ".qm" ) ) { translator.load( QString("PCDM_") + langCode, appDir + "/i18n/" ); a.installTranslator(&translator); Backend::log("Loaded Translation:" + appDir + "/i18n/PCDM_" + langCode + ".qm"); } else { Backend::log("Could not find: " + appDir + "/i18n/PCDM_" + langCode + ".qm"); langCode = ""; } //qDebug() << "Translation Finished:" << QString::number(clock.elapsed())+" ms"; Backend::log("Starting up PCDM interface"); PCDMgui w; //qDebug() << "Main GUI Created:" << QString::number(clock.elapsed())+" ms"; splash.finish(&w); //close the splash when the GUI starts up // Set full-screen dimensions QRect dimensions = QApplication::desktop()->screenGeometry(); int wid = dimensions.width(); // returns desktop width int hig = dimensions.height(); // returns desktop height w.setGeometry(0, 0, wid, hig); //Set the proper size on the Application w.setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnBottomHint); w.setWindowState(Qt::WindowMaximized); //Qt::WindowFullScreen); //Setup the signals/slots to startup the desktop session if(USECLIBS){ QObject::connect( &w,SIGNAL(xLoginAttempt(QString,QString,QString)), &desktop,SLOT(setupDesktop(QString,QString,QString))); } else{ QObject::connect( &w,SIGNAL(xLoginAttempt(QString,QString,QString)), &desktop,SLOT(loginToXSession(QString,QString,QString)) ); } //Setup the signals/slots for return information for the GUI QObject::connect( &desktop, SIGNAL(InvalidLogin()), &w, SLOT(slotLoginFailure()) ); QObject::connect( &desktop, SIGNAL(started()), &w, SLOT(slotLoginSuccess()) ); QObject::connect( &desktop, SIGNAL(ValidLogin()), &w, SLOT(slotLoginSuccess()) ); //qDebug() << "Showing GUI:" << QString::number(clock.elapsed())+" ms"; w.show(); a.exec(); } // end of PCDM GUI running int retcode = 0; //Wait for the desktop session to finish before exiting if(USECLIBS){ desktop.startDesktop(); } else{ desktop.waitForSessionClosed(); } splash.show(); //show the splash screen again splash.showMessage(QObject::tr("System Shutting Down"), Qt::AlignHCenter | Qt::AlignBottom, Qt::white); //check for shutdown process if(QFile::exists("/var/run/nologin")){ //Pause for a few seconds to prevent starting a new session during a shutdown QTime wTime = QTime::currentTime().addSecs(30); while( QTime::currentTime() < wTime ){ //Keep processing events during the wait (for splashscreen) QCoreApplication::processEvents(QEventLoop::AllEvents, 100); } //set the return code for a shutdown retcode = -1; //make sure it does not start a new session } //Clean up Code delete &desktop; delete &a; delete &splash; return retcode; }
bool XProcess::startXSession(){ //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 FALSE; } //Backend::log("Starting up Desktop environment ("+xcmd+") as user ("+xuser+")"); //Check for PAM username/password validity if( !pam_checkPW() ){ emit InvalidLogin(); pam_shutdown(); return FALSE; } //Save the current user/desktop as the last login Backend::saveLoginInfo(Backend::getDisplayNameFromUsername(xuser),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))) { emit InvalidLogin(); //Make sure the GUI knows that it was a failure return FALSE; } } // Get the environment before we drop priv QProcessEnvironment environ = QProcessEnvironment::systemEnvironment(); //current environment QWidget *wid = new QWidget(); if (setgid(pw->pw_gid) < 0) { qDebug() << "setgid() failed!"; emit InvalidLogin(); //Make sure the GUI knows that it was a failure return FALSE; } // Setup our other groups if (initgroups(xuser.toLatin1(), pw->pw_gid) < 0) { qDebug() << "initgroups() failed!"; emit InvalidLogin(); //Make sure the GUI knows that it was a failure setgid(0); return FALSE; } // Lets drop to user privs if (setuid(pw->pw_uid) < 0) { qDebug() << "setuid() failed!"; emit InvalidLogin(); //Make sure the GUI knows that it was a failure return FALSE; } //Startup the PAM session if( !pam_startSession() ){ pam_shutdown(); return FALSE; } pam_session_open = TRUE; //flag that pam has an open session QString cmd; // Configure the DE startup command // - Setup to run the user's <home-dir>/.xprofile startup script if(QFile::exists(xhome+"/.xprofile")){ //cmd.append(". "+xhome+"/.xprofile; "); //make sure to start it in parallel } // - Add the DE startup command to the end //cmd.append("dbus-launch --exit-with-session "+xcmd); cmd.append(xcmd); //cmd.append("; kill -l KILL"); //to clean up the session afterwards // Get the current locale code QLocale mylocale; QString langCode = mylocale.name(); //Backend::log("Startup command: "+cmd); // Setup the process environment // Setup any specialized environment variables // USER, HOME, and SHELL are set by the "su" login environ.insert("LOGNAME",xuser); //Login name environ.insert("USERNAME",xuser); // Username environ.insert("PATH",environ.value("PATH")+":"+xhome+"/bin"); // Append the user's home dir to the path if( langCode.toLower() == "c" ){} // do nothing extra to it else if(!environ.value("MM_CHARSET").isEmpty() ){ langCode.append( "."+environ.value("MM_CHARSET") ); } else{ langCode.append(".UTF-8"); } environ.insert("LANG",langCode); //Set the proper localized language environ.insert("MAIL","/var/mail/"+xuser); //Set the mail variable environ.insert("GROUP",xuser); //Set the proper group id environ.insert("SHLVL","0"); //Set the proper shell level environ.insert("HOME",xhome); //Set the users home directory this->setProcessEnvironment(environ); this->setWorkingDirectory(xhome); //set the current directory to the user's home directory //Log the DE startup outputs as well this->setStandardOutputFile(xhome+"/.pcdm-startup.log",QIODevice::Truncate); this->setStandardErrorFile(xhome+"/.pcdm-startup.err",QIODevice::Truncate); // Startup the process QMessageBox::warning(wid, "My Application", "CMD: " + cmd, QMessageBox::Ok, QMessageBox::Ok); this->start(cmd); return TRUE; }
//Start the desktop in the current process with C functions void XProcess::startDesktop(){ //Check for PAM username/password validity if( !pam_checkPW() ){ qDebug() << "Invalid username/password"; pam_shutdown(); return; } //Startup the PAM session if( !pam_startSession() ){ qDebug() << "Unable to open PAM session"; pam_shutdown(); return; } pam_session_open = TRUE; //flag that pam has an open session //Save the current user/desktop as the last login Backend::saveLoginInfo(Backend::getDisplayNameFromUsername(xuser),xde); // We will now fork off, so the child can drop root perms and do its thing int pid = fork(); if(pid < 0){ Backend::log("Error: Could not fork for user permissions"); return; }else if( pid !=0 ){ //Parent (calling) process int status; sleep(2); waitpid(pid,&status,0); //wait for the child (session) to finish // Child is all done, lets close down the pam session and cleanup pam_shutdown(); exit(0); } // 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))) { emit InvalidLogin(); //Make sure the GUI knows that it was a failure return; } } if (setgid(pw->pw_gid) < 0) { qDebug() << "setgid() failed!"; emit InvalidLogin(); //Make sure the GUI knows that it was a failure return; } // Setup our other groups if (initgroups(xuser.toLatin1(), pw->pw_gid) < 0) { qDebug() << "initgroups() failed!"; emit InvalidLogin(); //Make sure the GUI knows that it was a failure setgid(0); return; } // Lets drop to user privs if (setuid(pw->pw_uid) < 0) { qDebug() << "setuid() failed!"; emit InvalidLogin(); //Make sure the GUI knows that it was a failure return; } QString cmd; // Configure the DE startup command // - Setup to run the user's <home-dir>/.xprofile startup script if(QFile::exists(xhome+"/.xprofile")){ cmd.append(". "+xhome+"/.xprofile; "); //make sure to start it in parallel } // - Add the DE startup command to the end cmd.append("dbus-launch --exit-with-session "+xcmd); // Get the current locale code QLocale mylocale; QString langCode = mylocale.name(); //Alternate way of starting a process using c library functions //setup the environment variables setenv("LOGNAME",xuser.toUtf8(),1); setenv("USERNAME",xuser.toUtf8(),1); QString pth = QString(getenv("PATH"))+":"+xhome+"/bin"; setenv("PATH",pth.toUtf8(),1); if(langCode.toLower()=="c"){} else if(QString(getenv("MM_CHARSET")).isEmpty() ){ langCode.append("."+QString(getenv("MM_CHARSET"))); } else{ langCode.append(".UTF-8"); } setenv("LANG",langCode.toUtf8(),1); setenv("MAIL",QString("/var/mail/"+xuser).toUtf8(),1); setenv("GROUP",xuser.toUtf8(),1); setenv("HOME",xhome.toUtf8(),1); setenv("SHELL",pw->pw_shell,1); setenv("SHLVL","0",1); chdir(xhome.toUtf8()); //move to home dir //Now start the process system(cmd.toLatin1()); }
bool XProcess::startXSession(){ //Returns true if the session can continue, or false if it needs to be closed down qDebug() << "Starting X Session:" << xuser << xcmd << xhome << xde << VT; //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; } //Make sure the user is added to any required groups QStringList cgroups = Backend::runShellCommand("id -nG "+xuser).join("").split(" "); //current groups if(!cgroups.contains("video")){ Backend::runShellCommand("pw group mod video -m "+xuser); } //add user to video group (required for GPU access) //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 = "******"; } } //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:"******"/tmp/pcdm-session."+VT+".XXXXXX"); if ( ! tFile->open() ) return false; QTextStream tOut(tFile); // Configure the DE startup command if(QFile::exists("/usr/local/bin/dbus-launch") && !xcmd.contains("lumina-desktop") ){ cmd.append("dbus-launch --exit-with-session "+xcmd); }else{ cmd.append(xcmd); } QString tUid, tGid, logFile; tUid.setNum(pw->pw_uid); tGid.setNum(pw->pw_gid); logFile=xhome + "/.pcdm-startup.log"; // Locate the auth file QString authfile = qgetenv("XAUTHORITY"); if ( authfile.isEmpty() ) authfile = xhome + "/.Xauthority"; //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() ) { // Change ownership on the Xauthority file for PICO usage tOut << "rm "+authfile+"\n"; tOut << "ln -fs "+PICOHOME+"/.Xauthority "+authfile+"\n"; QString PICOPULSE = qgetenv("PICO_PULSE_COOKIE"); // Check if PULSE is enabled if ( ! PICOPULSE.isEmpty() ) { tOut << "mkdir -p "+xhome+"/.config/pulse 2>/dev/null\n"; tOut << "cp "+PICOPULSE+" "+xhome+"/.config/pulse/cookie\n"; tOut << "chown "+tUid+":"+tGid+" " +xhome+"/.config/pulse/cookie\n"; } QProcess::execute("chown "+tUid+" "+PICOHOME+"/.Xauthority"); // Change ownership on the pico login } else { // Change ownership on the Xauthority file QProcess::execute("chown "+tUid+":"+tGid+" "+authfile); } tOut << Backend::resetKbdCmd() + "\n"; //do this before sourcing .xprofile - in case there is a user-override set there tOut << "if [ -e '"+xhome+"/.xprofile' ] ; then\n"; tOut << " chmod 755 "+xhome+"/.xprofile\n"; tOut << " . "+xhome+"/.xprofile\n"; tOut << "fi\n"; tOut << cmd + "\n"; //+ " >" + xhome+ "/.pcdm-startup.log" + " 2>" + xhome + "/.pcdm-startup.log\n"; tOut << "exit $?"; //Make sure we return the DE return value cmd = "/usr/local/share/PCDM/pcdm-session"; //+xuser+" "+tUid+" "+tGid+" "+tFile->fileName()+" "+logFile; QStringList cmdArgs; cmdArgs << xuser << tUid << tGid << tFile->fileName() << logFile; connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) ); tFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner |QFile::ReadGroup | QFile::ReadUser | QFile::ReadOther); tFile->close(); if(DEBUG){ Backend::log("Starting session with:\n" + cmd ); } this->start(cmd, cmdArgs); return true; }
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) ){ 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 = "******"; } } //Save the current user/desktop as the last login Backend::saveLoginInfo(xuser,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"); //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:"******"/usr/local/bin/dbus-launch")){ cmd.append("dbus-launch --exit-with-session "+xcmd); } //Need to run a couple commands in sequence: so put them in a script file tOut << "#!/bin/sh\n\n"; tOut << "if [ -e '"+xhome+"/.xprofile' ] ; then\n"; tOut << " chmod 755 "+xhome+"/.xprofile\n"; tOut << " . "+xhome+"/.xprofile\n"; tOut << "fi\n"; tOut << cmd + "\n"; //+ " >" + xhome+ "/.pcdm-startup.log" + " 2>" + xhome + "/.pcdm-startup.log\n"; tOut << "exit $?"; //Make sure we return the DE return value QString tUid, tGid, logFile; tUid.setNum(pw->pw_uid); tGid.setNum(pw->pw_gid); logFile=xhome + "/.pcdm-startup.log"; cmd = "/usr/local/share/PCDM/pcdm-session "+xuser+" "+tUid+" "+tGid+" "+tFile->fileName()+" "+logFile; connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) ); tFile->setPermissions(QFile::ReadOwner | QFile::WriteOwner |QFile::ReadGroup | QFile::ReadUser | QFile::ReadOther); tFile->close(); if(DEBUG){ Backend::log("Starting session with:\n" + cmd ); } this->start(cmd); return true; }