//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(); } }
void XProcess::slotCleanup(){ Backend::log("Session Finished\n - Return Code: "+ QString::number(this->exitCode()) ); pam_shutdown(); //make sure that PAM shuts down properly this->closeWriteChannel(); this->closeReadChannel(QProcess::StandardOutput); this->closeReadChannel(QProcess::StandardError); //Now remove this user's access to the Xserver QString xhostcmd = "xhost -si:localuser:"******" - Removing Stealth session"; } QProcess::execute("personacrypt temprem"); //remove the temporary home-dir }else if( !xdevpass.isEmpty() ){ if(DEBUG){ Backend::log(" - Unmounting PersonaCrypt User: "******" WARNING: Could not unmount user device (attempt "+QString::number(tries)+"/10)"); tries++; QObject().thread()->usleep(500000); //wait 1/2 second before trying again (max 5 seconds) } if(tries>10){ Backend::log(" ERROR: Could not unmount user device"); } else{ Backend::log(" SUCCESS: User device unmounted"); } } }
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()); }
void XProcess::slotCleanup(int exitCode, QProcess::ExitStatus status){ pam_shutdown(); //make sure that PAM shuts down properly }
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; }