source: src-qt4/PCDM/src/pcdm-xprocess.cpp @ 9cc1b3c

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

Turn on the "-auth <authfile>" option for xinit in PCDMd

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