source: src-qt4/PCDM/src/pcdm-xprocess.cpp @ 999faef

9.2-releasereleng/10.0releng/10.0.1releng/10.0.2releng/10.0.3releng/10.1
Last change on this file since 999faef was 999faef, checked in by Ken Moore <ken@…>, 14 months ago

Remove the old PCDM-xloop script (no longer needed), and also clean up the testing functions for using pure C classes to start up the user session (the Qt session startup is working fine)

  • Property mode set to 100644
File size: 12.6 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  pam_started = FALSE;
28  pam_session_open = FALSE;
29  //Setup the finished signal/slot
30  connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup(int, QProcess::ExitStatus)) );
31}
32
33XProcess::~XProcess(){
34  if( isRunning() ){
35    this->terminate();
36  }
37  this->close();
38}
39
40void XProcess::loginToXSession(QString username, QString password, QString desktop){
41  //Setup the variables
42  xuser = username;
43  xpwd = password;
44  xhome = Backend::getUserHomeDir(xuser);
45  xcmd = Backend::getDesktopBinary(desktop);
46  xde = desktop;
47  //Now start the login process
48  startXSession();
49}
50
51bool XProcess::isRunning(){
52  if(this->state() != QProcess::NotRunning){ return TRUE; }
53  else{ return FALSE; }
54}
55
56void XProcess::waitForSessionClosed(){
57  // CAUTION!!
58  // This function will pause the calling program to wait for the session to end!
59  if( isRunning() ){ this->waitForFinished(-1); }
60
61}
62
63/*
64 ========== SESSION STARTUP ==========
65*/
66
67bool XProcess::startXSession(){
68  //Check that the necessary info to start the session is available
69  if( xuser.isEmpty() || xcmd.isEmpty() || xhome.isEmpty() || xde.isEmpty() ){
70    emit InvalidLogin();  //Make sure the GUI knows that it was a failure
71    return FALSE;
72  }
73  //Backend::log("Starting up Desktop environment ("+xcmd+") as user ("+xuser+")");
74 
75  //Check for PAM username/password validity
76  if( !pam_checkPW() ){ emit InvalidLogin(); pam_shutdown(); return FALSE; }
77
78
79  //Save the current user/desktop as the last login
80  Backend::saveLoginInfo(Backend::getDisplayNameFromUsername(xuser),xde);
81
82  // Get the users uid/gid information
83  struct passwd *pw;
84  int uid;
85  char *ok;
86
87  if (!(pw = getpwnam(xuser.toLatin1()))) {
88      uid = strtol(xuser.toLatin1(), &ok, 10);
89      if (!(pw = getpwuid(uid))) {
90          emit InvalidLogin();  //Make sure the GUI knows that it was a failure
91          return FALSE;
92      }
93  }
94
95  // Get the environment before we drop priv
96  QProcessEnvironment environ = QProcessEnvironment::systemEnvironment(); //current environment
97  //Now allow this user access to the Xserver
98  QString xhostcmd = "xhost si:localuser:"+xuser;
99  system(xhostcmd.toUtf8());
100 
101  //QWidget *wid = new QWidget();
102  if (setgid(pw->pw_gid) < 0) {
103      qDebug() << "setgid() failed!";
104      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
105      return FALSE;
106  }
107
108  // Setup our other groups
109  if (initgroups(xuser.toLatin1(), pw->pw_gid) < 0) {
110      qDebug() << "initgroups() failed!";
111      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
112      setgid(0);
113      return FALSE;
114  }
115
116  // Lets drop to user privs
117  if (setuid(pw->pw_uid) < 0) {
118      qDebug() << "setuid() failed!";
119      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
120      return FALSE;
121  }
122
123  //Startup the PAM session
124  if( !pam_startSession() ){ pam_shutdown(); return FALSE; }
125  pam_session_open = TRUE; //flag that pam has an open session
126 
127  QString cmd;
128  // Configure the DE startup command
129
130  //  - Add the DE startup command to the end
131  cmd.append("dbus-launch --exit-with-session "+xcmd);
132  //cmd.append(xcmd);
133  //cmd.append("; kill -l KILL"); //to clean up the session afterwards
134  // Get the current locale code
135  QLocale mylocale;
136  QString langCode = mylocale.name();
137 
138  //Backend::log("Startup command: "+cmd);
139  // Setup the process environment
140
141  // Setup any specialized environment variables
142  // USER, HOME, and SHELL are set by the "su" login
143  environ.insert("LOGNAME",xuser); //Login name
144  environ.insert("USERNAME",xuser); // Username
145  environ.insert("PATH",environ.value("PATH")+":"+xhome+"/bin"); // Append the user's home dir to the path
146  if( langCode.toLower() == "c" ){} // do nothing extra to it
147  else if(!environ.value("MM_CHARSET").isEmpty() ){ langCode.append( "."+environ.value("MM_CHARSET") ); }
148  else{ langCode.append(".UTF-8"); }
149  environ.insert("LANG",langCode); //Set the proper localized language
150  environ.insert("MAIL","/var/mail/"+xuser); //Set the mail variable
151  environ.insert("GROUP",xuser); //Set the proper group id
152  environ.insert("SHLVL","0"); //Set the proper shell level
153  environ.insert("HOME",xhome); //Set the users home directory
154  this->setProcessEnvironment(environ);
155  this->setWorkingDirectory(xhome); //set the current directory to the user's home directory
156  //Log the DE startup outputs as well
157  this->setStandardOutputFile(xhome+"/.pcdm-startup.log",QIODevice::Truncate);
158  this->setStandardErrorFile(xhome+"/.pcdm-startup.err",QIODevice::Truncate);
159  // Startup the process(s)
160   //  - Setup to run the user's <home-dir>/.xprofile startup script
161  if(QFile::exists(xhome+"/.xprofile")){
162    disconnect(SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup(int, QProcess::ExitStatus)) );
163    this->start("sh "+xhome+"/.xprofile &");//make sure to start it in parallel
164    if(!this->waitForFinished(30000) ){
165      //If it still has not finished this after 30 seconds, kill it
166      this->terminate();
167    }
168    connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup(int, QProcess::ExitStatus)) );
169  }
170  this->start(cmd);
171  return TRUE;
172}
173
174void XProcess::slotCleanup(int exitCode, QProcess::ExitStatus status){
175  pam_shutdown(); //make sure that PAM shuts down properly
176  //Now remove this user's access to the Xserver
177  QString xhostcmd = "xhost -si:localuser:"+xuser;
178  system(xhostcmd.toUtf8());
179}
180
181/*
182//Start the desktop in the current process with C functions
183void XProcess::startDesktop(){
184  //Check for PAM username/password validity
185  if( !pam_checkPW() ){ qDebug() << "Invalid username/password"; pam_shutdown(); return; }
186  //Startup the PAM session
187  if( !pam_startSession() ){ qDebug() << "Unable to open PAM session"; pam_shutdown(); return; }
188  pam_session_open = TRUE; //flag that pam has an open session
189 
190  //Save the current user/desktop as the last login
191  Backend::saveLoginInfo(Backend::getDisplayNameFromUsername(xuser),xde);
192
193  // We will now fork off, so the child can drop root perms and do its thing
194  int pid = fork();
195  if(pid < 0){
196    Backend::log("Error: Could not fork for user permissions");
197    return;
198  }else if( pid !=0 ){
199    //Parent (calling) process
200    int status;
201    sleep(2);
202    waitpid(pid,&status,0); //wait for the child (session) to finish
203
204    // Child is all done, lets close down the pam session and cleanup
205    pam_shutdown();
206    exit(0);
207  }
208 
209  // Get the users uid/gid information
210  struct passwd *pw;
211  int uid;
212  char *ok;
213
214  if (!(pw = getpwnam(xuser.toLatin1()))) {
215      uid = strtol(xuser.toLatin1(), &ok, 10);
216      if (!(pw = getpwuid(uid))) {
217          emit InvalidLogin();  //Make sure the GUI knows that it was a failure
218          return;
219      }
220  }
221
222  if (setgid(pw->pw_gid) < 0) {
223      qDebug() << "setgid() failed!";
224      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
225      return;
226  }
227
228  // Setup our other groups
229  if (initgroups(xuser.toLatin1(), pw->pw_gid) < 0) {
230      qDebug() << "initgroups() failed!";
231      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
232      setgid(0);
233      return;
234  }
235
236  // Lets drop to user privs
237  if (setuid(pw->pw_uid) < 0) {
238      qDebug() << "setuid() failed!";
239      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
240      return;
241  }
242
243  QString cmd;
244  // Configure the DE startup command
245  //  - Setup to run the user's <home-dir>/.xprofile startup script
246  if(QFile::exists(xhome+"/.xprofile")){
247    cmd.append(". "+xhome+"/.xprofile; ");  //make sure to start it in parallel
248  }
249  //  - Add the DE startup command to the end
250  cmd.append("dbus-launch --exit-with-session "+xcmd);
251
252  // Get the current locale code
253  QLocale mylocale;
254  QString langCode = mylocale.name();
255 
256  //Alternate way of starting a process using c library functions
257 
258  //setup the environment variables
259  setenv("LOGNAME",xuser.toUtf8(),1);
260  setenv("USERNAME",xuser.toUtf8(),1);
261  QString pth = QString(getenv("PATH"))+":"+xhome+"/bin";
262  setenv("PATH",pth.toUtf8(),1);
263  if(langCode.toLower()=="c"){}
264  else if(QString(getenv("MM_CHARSET")).isEmpty() ){ langCode.append("."+QString(getenv("MM_CHARSET"))); }
265  else{ langCode.append(".UTF-8"); }
266  setenv("LANG",langCode.toUtf8(),1);
267  setenv("MAIL",QString("/var/mail/"+xuser).toUtf8(),1);
268  setenv("GROUP",xuser.toUtf8(),1);
269  setenv("HOME",xhome.toUtf8(),1);
270  setenv("SHELL",pw->pw_shell,1);
271  setenv("SHLVL","0",1);
272  chdir(xhome.toUtf8()); //move to home dir
273   
274  //Now start the process
275  system(cmd.toLatin1());
276}
277*/
278 
279/*void XProcess::setupDesktop(QString user, QString pwd, QString desktop){
280  //Setup internal variables
281  xuser = Backend::getUsernameFromDisplayname(user);
282  xpwd = pwd;
283  xhome = Backend::getUserHomeDir(xuser);
284  xcmd = Backend::getDesktopBinary(desktop);
285  xde = desktop;
286  //Also check password  validity
287  bool ok = pam_checkPW();
288  pam_shutdown();
289  if(ok){ emit ValidLogin(); }
290  else{ emit InvalidLogin(); }
291}
292  */
293 
294
295//Stand-alone function to check a username/password combination for validity
296void XProcess::checkPW(QString user, QString pwd){
297  xuser = Backend::getUsernameFromDisplayname(user); 
298  xpwd = pwd;
299  bool ok = pam_checkPW();
300  pam_shutdown();
301  xuser.clear();
302  xpwd.clear();
303  if(ok){ emit ValidLogin(); }
304  else{ emit InvalidLogin(); }
305}
306
307/*
308 ========== PAM FUNCTIONS ==========
309*/
310static struct pam_conv pamc = { openpam_nullconv, NULL };
311
312bool XProcess::pam_checkPW(){
313 //Requires internal "xuser" and "xpwd" variables to be set
314       
315//Convert the inputs to C character arrays for use in PAM
316  QByteArray tmp = xuser.toUtf8();
317  char* cUser = tmp.data();
318  QByteArray tmp2 = xpwd.toUtf8();
319  char* cPassword = tmp2.data();
320  //initialize variables
321  bool result = FALSE;
322  int ret;
323  //Initialize PAM
324  ret = pam_start("login", cUser, &pamc, &pamh);
325  if( ret == PAM_SUCCESS ){
326    pam_started = TRUE; //flag that pam is started
327    //Place the user-supplied password into the structure
328    ret = pam_set_item(pamh, PAM_AUTHTOK, cPassword);
329    //Set the TTY
330    //ret = pam_set_item(pamh, PAM_TTY, "pcdm-terminal");
331    //Authenticate with PAM
332    ret = pam_authenticate(pamh,0);
333    if( ret == PAM_SUCCESS ){
334      //Check for valid, unexpired account and verify access restrictions
335      ret = pam_acct_mgmt(pamh,0);
336      if( ret == PAM_SUCCESS ){ result = TRUE; }
337   
338    }else{
339      pam_logFailure(ret);
340    }
341  }
342  //return verification result
343  return result;       
344}
345
346bool XProcess::pam_startSession(){
347  //This should only be run if pam_checkPW was successful
348  int ret = pam_open_session(pamh,0);
349  bool ok = FALSE;
350  if(ret == PAM_SUCCESS){ ok = TRUE; }
351  else{ pam_logFailure(ret); }
352 
353  return ok;
354}
355
356bool XProcess::pam_stopSession(){
357  //This should only be run if pam_startSession was successful
358  int ret = pam_close_session(pamh,0);
359  bool ok = FALSE;
360  if(ret == PAM_SUCCESS){ ok = TRUE; }
361  else{ pam_logFailure(ret); }
362 
363  return ok;
364}
365
366void XProcess::pam_logFailure(int ret){
367  //Interpret a PAM error message and log it
368  Backend::log("PAM Error: " + QString::number(ret));
369  switch( ret ){
370  case PAM_ABORT:
371    Backend::log(" - PAM abort error");
372    break;
373  case PAM_AUTHINFO_UNAVAIL:
374    Backend::log(" - Authentication info unavailable");
375    break;
376  case PAM_AUTH_ERR:
377    Backend::log(" - Authentication error");
378    break;
379  case PAM_BUF_ERR:
380    Backend::log(" - Buffer error");
381    break;
382  case PAM_CONV_ERR:
383    Backend::log(" - Conversion error");
384    break;
385  case PAM_CRED_INSUFFICIENT:
386    Backend::log(" - Credentials insufficient");
387    break;
388  case PAM_MAXTRIES:
389    Backend::log(" - Maximum number of tries exceeded");
390    break;
391  case PAM_PERM_DENIED:
392    Backend::log(" - Permission denied");
393    break;
394  case PAM_SERVICE_ERR:
395    Backend::log(" - Service error");
396    break;
397  case PAM_SYMBOL_ERR:
398    Backend::log(" - Symbol error");
399    break;
400  case PAM_SYSTEM_ERR:
401    Backend::log(" - System error");
402    break;
403  case PAM_USER_UNKNOWN:
404    Backend::log(" - Unknown user");
405    break;
406  default:
407    Backend::log(" - Unrecognized authentication error");
408  }
409       
410}
411
412void XProcess::pam_shutdown(){
413  if(pam_session_open){
414    pam_stopSession();
415    pam_session_open = FALSE;
416  }
417  if(pam_started){
418    pam_end(pamh,0);
419    pam_started = FALSE;
420  }
421}
Note: See TracBrowser for help on using the repository browser.