source: src-qt4/PCDM/src/pcdm-xprocess.cpp @ 6449f61

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

Add in the xhost authentication routine when logging in a user. This will allow the user access to the display. Also fix the authorization file cleanup between sessions of PCDM.

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