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

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

Fix up the new PCDM splashscreen, and also fix a bug in the login process from the recent compilation fix for 10.x

  • Property mode set to 100644
File size: 9.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){
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  //Now start the login process
50  startXSession();
51}
52
53bool XProcess::isRunning(){
54  if(this->state() != QProcess::NotRunning){ return TRUE; }
55  else{ return FALSE; }
56}
57
58void XProcess::waitForSessionClosed(){
59  // CAUTION!!
60  // This function will pause the calling program to wait for the session to end!
61  if( isRunning() ){ this->waitForFinished(-1); }
62
63}
64
65/*
66 ========== SESSION STARTUP ==========
67*/
68
69bool XProcess::startXSession(){
70  //Check that the necessary info to start the session is available
71  if( xuser.isEmpty() || xcmd.isEmpty() || xhome.isEmpty() || xde.isEmpty() ){
72    emit InvalidLogin();  //Make sure the GUI knows that it was a failure
73    return FALSE;
74  }
75  //Backend::log("Starting up Desktop environment ("+xcmd+") as user ("+xuser+")");
76 
77  //Check for PAM username/password validity
78  if( !pam_checkPW() ){ emit InvalidLogin(); pam_shutdown(); return FALSE; }
79
80
81  //Save the current user/desktop as the last login
82  Backend::saveLoginInfo(Backend::getDisplayNameFromUsername(xuser),xde);
83
84  // Get the users uid/gid information
85  struct passwd *pw;
86  int uid;
87  char *ok;
88
89  if (!(pw = getpwnam(xuser.toLatin1()))) {
90      uid = strtol(xuser.toLatin1(), &ok, 10);
91      if (!(pw = getpwuid(uid))) {
92          emit InvalidLogin();  //Make sure the GUI knows that it was a failure
93          return FALSE;
94      }
95  }
96
97  // Get the environment before we drop priv
98  this->setProcessEnvironment( QProcessEnvironment::systemEnvironment() ); //current environment
99  //Now allow this user access to the Xserver
100  QString xhostcmd = "xhost si:localuser:"+xuser;
101  system(xhostcmd.toUtf8());
102  //QWidget *wid = new QWidget();
103  if (setgid(pw->pw_gid) < 0) {
104      qDebug() << "setgid() failed!";
105      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
106      return FALSE;
107  }
108
109  // Setup our other groups
110  if (initgroups(xuser.toLatin1(), pw->pw_gid) < 0) {
111      qDebug() << "initgroups() failed!";
112      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
113      setgid(0);
114      return FALSE;
115  }
116
117  // Lets drop to user privs
118  if (setuid(pw->pw_uid) < 0) {
119      qDebug() << "setuid() failed!";
120      emit InvalidLogin();  //Make sure the GUI knows that it was a failure
121      return FALSE;
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  QString cmd;
127  // Configure the DE startup command
128
129  //  - Add the DE startup command to the end
130  cmd.append("dbus-launch --exit-with-session "+xcmd);
131  //cmd.append(xcmd);
132  //cmd.append("; kill -l KILL"); //to clean up the session afterwards
133  //Backend::log("Startup command: "+cmd);
134  // Setup the process environment
135  setupSessionEnvironment();
136  //Log the DE startup outputs as well
137  this->setStandardOutputFile(xhome+"/.pcdm-startup.log",QIODevice::Truncate);
138  this->setStandardErrorFile(xhome+"/.pcdm-startup.err",QIODevice::Truncate);
139  // Startup the process(s)
140   //  - Setup to run the user's <home-dir>/.xprofile startup script
141  if(QFile::exists(xhome+"/.xprofile")){
142    disconnect(SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) );
143    qDebug() << "Run user ~/.xprofile";
144    this->start("sh "+xhome+"/.xprofile &");//make sure to start it in parallel
145    if(!this->waitForFinished(30000) ){
146      //If it still has not finished this after 30 seconds, kill it
147      this->terminate();
148    }
149    connect( this, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(slotCleanup()) );
150  }
151  this->start(cmd);
152  return TRUE;
153}
154
155void XProcess::slotCleanup(){
156  pam_shutdown(); //make sure that PAM shuts down properly
157  //Now remove this user's access to the Xserver
158  QString xhostcmd = "xhost -si:localuser:"+xuser;
159  system(xhostcmd.toUtf8());
160}
161
162void XProcess::setupSessionEnvironment(){
163  // Setup any specialized environment variables
164  QProcessEnvironment environ = this->processEnvironment();
165  // Get the current locale code
166  QLocale mylocale;
167  QString langCode = mylocale.name();
168  if( langCode.toLower() == "c" ){ langCode = "en_US"; } // default to the US english (PCDM code default), LANG=C causes problems
169  if(!environ.value("MM_CHARSET").isEmpty() ){ langCode.append( "."+environ.value("MM_CHARSET") ); }
170  else{ langCode.append(".UTF-8"); }
171  // USER, HOME, and SHELL are set by the "su" login
172  environ.insert("LOGNAME",xuser); //Login name
173  environ.insert("USERNAME",xuser); // Username
174  environ.insert("USER",xuser); // Username
175  environ.insert("PATH",environ.value("PATH")+":"+xhome+"/bin"); // Append the user's home dir to the path
176  environ.insert("LANG",langCode); //Set the proper localized language
177  environ.insert("MAIL","/var/mail/"+xuser); //Set the mail variable
178  environ.insert("GROUP",xuser); //Set the proper group id
179  environ.insert("SHLVL","0"); //Set the proper shell level
180  environ.insert("HOME",xhome); //Set the users home directory
181  environ.insert("SHELL",xshell); //Set the user's default shell
182  this->setProcessEnvironment(environ);
183  this->setWorkingDirectory(xhome); //set the current directory to the user's home directory
184}
185
186//Stand-alone function to check a username/password combination for validity
187void XProcess::checkPW(QString user, QString pwd){
188  xuser = Backend::getUsernameFromDisplayname(user); 
189  xpwd = pwd;
190  bool ok = pam_checkPW();
191  pam_shutdown();
192  xuser.clear();
193  xpwd.clear();
194  if(ok){ emit ValidLogin(); }
195  else{ emit InvalidLogin(); }
196}
197
198/*
199 ========== PAM FUNCTIONS ==========
200*/
201static struct pam_conv pamc = { openpam_nullconv, NULL };
202
203bool XProcess::pam_checkPW(){
204 //Requires internal "xuser" and "xpwd" variables to be set
205       
206//Convert the inputs to C character arrays for use in PAM
207  QByteArray tmp = xuser.toUtf8();
208  char* cUser = tmp.data();
209  QByteArray tmp2 = xpwd.toUtf8();
210  char* cPassword = tmp2.data();
211  //initialize variables
212  bool result = FALSE;
213  int ret;
214  //Initialize PAM
215  ret = pam_start("login", cUser, &pamc, &pamh);
216  if( ret == PAM_SUCCESS ){
217    pam_started = TRUE; //flag that pam is started
218    //Place the user-supplied password into the structure
219    ret = pam_set_item(pamh, PAM_AUTHTOK, cPassword);
220    //Set the TTY
221    //ret = pam_set_item(pamh, PAM_TTY, "pcdm-terminal");
222    //Authenticate with PAM
223    ret = pam_authenticate(pamh,0);
224    if( ret == PAM_SUCCESS ){
225      //Check for valid, unexpired account and verify access restrictions
226      ret = pam_acct_mgmt(pamh,0);
227      if( ret == PAM_SUCCESS ){ result = TRUE; }
228   
229    }else{
230      pam_logFailure(ret);
231    }
232  }
233  //return verification result
234  return result;       
235}
236
237bool XProcess::pam_startSession(){
238  //This should only be run if pam_checkPW was successful
239  int ret = pam_open_session(pamh,0);
240  bool ok = FALSE;
241  if(ret == PAM_SUCCESS){ ok = TRUE; }
242  else{ pam_logFailure(ret); }
243 
244  return ok;
245}
246
247bool XProcess::pam_stopSession(){
248  //This should only be run if pam_startSession was successful
249  int ret = pam_close_session(pamh,0);
250  bool ok = FALSE;
251  if(ret == PAM_SUCCESS){ ok = TRUE; }
252  else{ pam_logFailure(ret); }
253 
254  return ok;
255}
256
257void XProcess::pam_logFailure(int ret){
258  //Interpret a PAM error message and log it
259  Backend::log("PAM Error: " + QString::number(ret));
260  switch( ret ){
261  case PAM_ABORT:
262    Backend::log(" - PAM abort error");
263    break;
264  case PAM_AUTHINFO_UNAVAIL:
265    Backend::log(" - Authentication info unavailable");
266    break;
267  case PAM_AUTH_ERR:
268    Backend::log(" - Authentication error");
269    break;
270  case PAM_BUF_ERR:
271    Backend::log(" - Buffer error");
272    break;
273  case PAM_CONV_ERR:
274    Backend::log(" - Conversion error");
275    break;
276  case PAM_CRED_INSUFFICIENT:
277    Backend::log(" - Credentials insufficient");
278    break;
279  case PAM_MAXTRIES:
280    Backend::log(" - Maximum number of tries exceeded");
281    break;
282  case PAM_PERM_DENIED:
283    Backend::log(" - Permission denied");
284    break;
285  case PAM_SERVICE_ERR:
286    Backend::log(" - Service error");
287    break;
288  case PAM_SYMBOL_ERR:
289    Backend::log(" - Symbol error");
290    break;
291  case PAM_SYSTEM_ERR:
292    Backend::log(" - System error");
293    break;
294  case PAM_USER_UNKNOWN:
295    Backend::log(" - Unknown user");
296    break;
297  default:
298    Backend::log(" - Unrecognized authentication error");
299  }
300       
301}
302
303void XProcess::pam_shutdown(){
304  if(pam_session_open){
305    pam_stopSession();
306    pam_session_open = FALSE;
307  }
308  if(pam_started){
309    pam_end(pamh,0);
310    pam_started = FALSE;
311  }
312}
Note: See TracBrowser for help on using the repository browser.