source: src-qt4/PCDM/src/pcdm-xprocess.cpp @ 5e38281

enter/10releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1releng/10.1.1releng/10.1.2releng/10.2stable/10
Last change on this file since 5e38281 was 5e38281, checked in by Ken Moore <ken@…>, 19 months ago

Turn on a bit more log information for PCDM. This will help people find the logs as necessary.

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/* PCDM Login Manager:
2*  Written by Ken Moore (ken@pcbsd.org) 2012/2013
3*  Modified by Kris Moore (kris@pcbsd.org) 2013
4*  Copyright(c) 2013 by the PC-BSD Project
5*  Available under the 3-clause BSD license
6*/
7
8#include <sys/types.h>
9#include <sys/wait.h>
10#include <unistd.h>
11#include <pwd.h>
12#include <login_cap.h>
13#include <QMessageBox>
14
15/*
16Sub-classed QProcess for starting an XSession Process
17*/
18
19#include "pcdm-xprocess.h"
20
21XProcess::XProcess() : QProcess(0) {
22  //initialize the variables
23  xuser.clear();
24  xcmd.clear();
25  xhome.clear();
26  xpwd.clear();
27  xshell.clear();
28  pam_started = FALSE;
29  pam_session_open = FALSE;
30  //Setup the finished signal/slot
31  //connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) );
32}
33
34XProcess::~XProcess(){
35  if( isRunning() ){
36    this->terminate();
37  }
38  this->close();
39}
40
41void XProcess::loginToXSession(QString username, QString password, QString desktop, QString lang){
42  //Setup the variables
43  xuser = username;
44  xpwd = password;
45  xhome = Backend::getUserHomeDir(xuser);
46  xcmd = Backend::getDesktopBinary(desktop);
47  xshell = Backend::getUserShell(xuser);
48  xde = desktop;
49  xlang = lang;
50  //Now start the login process
51  startXSession();
52}
53
54bool XProcess::isRunning(){
55  if(this->state() != QProcess::NotRunning){ return TRUE; }
56  else{ return FALSE; }
57}
58
59void XProcess::waitForSessionClosed(){
60  // CAUTION!!
61  // This function will pause the calling program to wait for the session to end!
62  if( isRunning() ){ this->waitForFinished(-1); }
63
64}
65
66/*
67 ========== SESSION STARTUP ==========
68*/
69
70bool XProcess::startXSession(){
71  //disconnect(SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) );
72  //Check that the necessary info to start the session is available
73  if( xuser.isEmpty() || xcmd.isEmpty() || xhome.isEmpty() || xde.isEmpty() ){
74    emit InvalidLogin();  //Make sure the GUI knows that it was a failure
75    return FALSE;
76  }
77  //Backend::log("Starting up Desktop environment ("+xcmd+") as user ("+xuser+")");
78 
79  //Check for PAM username/password validity
80  if( !pam_checkPW() ){ emit InvalidLogin(); pam_shutdown(); return FALSE; }
81
82
83  //Save the current user/desktop as the last login
84  Backend::saveLoginInfo(Backend::getDisplayNameFromUsername(xuser),xde);
85
86  // Get the users uid/gid information
87  struct passwd *pw;
88  int uid;
89  char *ok;
90
91  if (!(pw = getpwnam(xuser.toLatin1()))) {
92      uid = strtol(xuser.toLatin1(), &ok, 10);
93      if (!(pw = getpwuid(uid))) {
94          emit InvalidLogin();  //Make sure the GUI knows that it was a failure
95          return FALSE;
96      }
97  }
98
99  // Get the environment before we drop priv
100  this->setProcessEnvironment( QProcessEnvironment::systemEnvironment() ); //current environment
101  //Emit the last couple logs before dropping privileges
102  Backend::log("Starting session:");
103  Backend::log(" - Session Log: ~/.pcdm-startup.log");
104  //Now allow this user access to the Xserver
105  QString xhostcmd = "xhost si:localuser:"+xuser;
106  system(xhostcmd.toUtf8());
107  //QWidget *wid = new QWidget();
108  if (setgid(pw->pw_gid) < 0) {
109      qDebug() << "setgid() failed!";
110      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
111      return FALSE;
112  }
113
114  // Setup our other groups
115  if (initgroups(xuser.toLatin1(), pw->pw_gid) < 0) {
116      qDebug() << "initgroups() failed!";
117      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
118      setgid(0);
119      return FALSE;
120  }
121
122  // Lets drop to user privs
123  if (setuid(pw->pw_uid) < 0) {
124      qDebug() << "setuid() failed!";
125      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
126      return FALSE;
127  }
128  //Startup the PAM session
129  if( !pam_startSession() ){ pam_shutdown(); return FALSE; }
130  pam_session_open = TRUE; //flag that pam has an open session
131  QString cmd;
132  // Configure the DE startup command
133
134  //  - Add the DE startup command to the end
135  cmd.append("dbus-launch --exit-with-session "+xcmd);
136  //cmd.append(xcmd);
137
138  //Backend::log("Startup command: "+cmd);
139  // Setup the process environment
140  setupSessionEnvironment();
141  //Log the DE startup outputs as well
142  this->setProcessChannelMode(QProcess::MergedChannels);
143  this->setStandardOutputFile(xhome+"/.pcdm-startup.log",QIODevice::Truncate);
144  //this->setStandardErrorFile(xhome+"/.pcdm-startup.err",QIODevice::Truncate);
145  // Startup the process(s)
146   //  - Setup to run the user's <home-dir>/.xprofile startup script
147  if(QFile::exists(xhome+"/.xprofile")){
148    //Make sure the file is executable
149    QFile::setPermissions(xhome+"/.xprofile", QFile::permissions(xhome+"/.xprofile") | QFile::ExeOwner | QFile::ExeGroup | QFile::ExeOther );
150    //Need to run a couple commands in sequence: so put them in a script file
151    QStringList contents;
152    contents << ". "+xhome+"/.xprofile";
153    contents << cmd; //end with the actual command for the DE
154    contents << "exit $?"; //Make sure we return the DE return value
155    if( Backend::writeFile(xhome+"/.pcdmsessionstart", contents) ){
156      //script created fine, change the command to just run it
157      cmd = "sh "+xhome+"/.pcdmsessionstart";
158    }else{
159      //Could not create script file, fallback on running them seperately
160      QString xpro = "sh "+xhome+"/.xprofile";
161      this->start(xpro);
162      this->waitForFinished(3000);
163    }
164  }
165  connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) );
166  this->start(cmd);
167  return TRUE;
168}
169
170void XProcess::slotCleanup(){
171  Backend::log("Session Finished\n - Return Code: "+ QString::number(this->exitCode()) );
172  pam_shutdown(); //make sure that PAM shuts down properly
173  //Now remove this user's access to the Xserver
174  QString xhostcmd = "xhost -si:localuser:"+xuser;
175  system(xhostcmd.toUtf8());
176}
177
178void XProcess::setupSessionEnvironment(){
179  // Setup any specialized environment variables
180  QProcessEnvironment environ = this->processEnvironment();
181  // Check the current locale code
182  QString langCode = xlang;
183  if( langCode.toLower() == "c" ){ langCode = "en_US"; } // default to the US english (PCDM code default), LANG=C causes problems
184  if(!environ.value("MM_CHARSET").isEmpty() ){ langCode.append( "."+environ.value("MM_CHARSET") ); }
185  else{ langCode.append(".UTF-8"); }
186  // USER, HOME, and SHELL are set by the "su" login
187  environ.insert("LOGNAME",xuser); //Login name
188  environ.insert("USERNAME",xuser); // Username
189  environ.insert("USER",xuser); // Username
190  environ.insert("PATH",environ.value("PATH")+":"+xhome+"/bin"); // Append the user's home dir to the path
191  environ.insert("LANG",langCode); //Set the proper localized language
192  environ.insert("LC_ALL",langCode); //additional localization variable for some DE's
193  environ.insert("MAIL","/var/mail/"+xuser); //Set the mail variable
194  environ.insert("GROUP",xuser); //Set the proper group id
195  environ.insert("SHLVL","0"); //Set the proper shell level
196  environ.insert("HOME",xhome); //Set the users home directory
197  environ.insert("SHELL",xshell); //Set the user's default shell
198  environ.insert("PCDM_SESSION", Backend::getNLDesktopName(xde).toUpper() ); //List the desktop environment that is being started
199  this->setProcessEnvironment(environ);
200  this->setWorkingDirectory(xhome); //set the current directory to the user's home directory
201}
202
203//Stand-alone function to check a username/password combination for validity
204void XProcess::checkPW(QString user, QString pwd){
205  xuser = Backend::getUsernameFromDisplayname(user); 
206  xpwd = pwd;
207  bool ok = pam_checkPW();
208  pam_shutdown();
209  xuser.clear();
210  xpwd.clear();
211  if(ok){ emit ValidLogin(); }
212  else{ emit InvalidLogin(); }
213}
214
215/*
216 ========== PAM FUNCTIONS ==========
217*/
218static struct pam_conv pamc = { openpam_nullconv, NULL };
219
220bool XProcess::pam_checkPW(){
221 //Requires internal "xuser" and "xpwd" variables to be set
222       
223//Convert the inputs to C character arrays for use in PAM
224  QByteArray tmp = xuser.toUtf8();
225  char* cUser = tmp.data();
226  QByteArray tmp2 = xpwd.toUtf8();
227  char* cPassword = tmp2.data();
228  //initialize variables
229  bool result = FALSE;
230  int ret;
231  //Initialize PAM
232  ret = pam_start("login", cUser, &pamc, &pamh);
233  if( ret == PAM_SUCCESS ){
234    pam_started = TRUE; //flag that pam is started
235    //Place the user-supplied password into the structure
236    ret = pam_set_item(pamh, PAM_AUTHTOK, cPassword);
237    //Set the TTY
238    //ret = pam_set_item(pamh, PAM_TTY, "pcdm-terminal");
239    //Authenticate with PAM
240    ret = pam_authenticate(pamh,0);
241    if( ret == PAM_SUCCESS ){
242      //Check for valid, unexpired account and verify access restrictions
243      ret = pam_acct_mgmt(pamh,0);
244      if( ret == PAM_SUCCESS ){ result = TRUE; }
245   
246    }else{
247      pam_logFailure(ret);
248    }
249  }
250  //return verification result
251  return result;       
252}
253
254bool XProcess::pam_startSession(){
255  //This should only be run if pam_checkPW was successful
256  int ret = pam_open_session(pamh,0);
257  bool ok = FALSE;
258  if(ret == PAM_SUCCESS){ ok = TRUE; }
259  else{ pam_logFailure(ret); }
260 
261  return ok;
262}
263
264bool XProcess::pam_stopSession(){
265  //This should only be run if pam_startSession was successful
266  int ret = pam_close_session(pamh,0);
267  bool ok = FALSE;
268  if(ret == PAM_SUCCESS){ ok = TRUE; }
269  else{ pam_logFailure(ret); }
270 
271  return ok;
272}
273
274void XProcess::pam_logFailure(int ret){
275  //Interpret a PAM error message and log it
276  Backend::log("PAM Error: " + QString::number(ret));
277  switch( ret ){
278  case PAM_ABORT:
279    Backend::log(" - PAM abort error");
280    break;
281  case PAM_AUTHINFO_UNAVAIL:
282    Backend::log(" - Authentication info unavailable");
283    break;
284  case PAM_AUTH_ERR:
285    Backend::log(" - Authentication error");
286    break;
287  case PAM_BUF_ERR:
288    Backend::log(" - Buffer error");
289    break;
290  case PAM_CONV_ERR:
291    Backend::log(" - Conversion error");
292    break;
293  case PAM_CRED_INSUFFICIENT:
294    Backend::log(" - Credentials insufficient");
295    break;
296  case PAM_MAXTRIES:
297    Backend::log(" - Maximum number of tries exceeded");
298    break;
299  case PAM_PERM_DENIED:
300    Backend::log(" - Permission denied");
301    break;
302  case PAM_SERVICE_ERR:
303    Backend::log(" - Service error");
304    break;
305  case PAM_SYMBOL_ERR:
306    Backend::log(" - Symbol error");
307    break;
308  case PAM_SYSTEM_ERR:
309    Backend::log(" - System error");
310    break;
311  case PAM_USER_UNKNOWN:
312    Backend::log(" - Unknown user");
313    break;
314  default:
315    Backend::log(" - Unrecognized authentication error");
316  }
317       
318}
319
320void XProcess::pam_shutdown(){
321  if(pam_session_open){
322    pam_stopSession();
323    pam_session_open = FALSE;
324  }
325  if(pam_started){
326    pam_end(pamh,0);
327    pam_started = FALSE;
328  }
329}
Note: See TracBrowser for help on using the repository browser.