desktop: Modem: cleaned up pppd permissions checks, and GUI su wrapper
[barry/progweb.git] / desktop / src / ModemDlg.cc
blobe784b09ab5134d1fa021b321c3f3ed83f6358bfc
1 ///
2 /// \file ModemDlg.cc
3 /// Dialog class to handle modem functionality
4 ///
6 /*
7 Copyright (C) 2012, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "ModemDlg.h"
23 #include "windowids.h"
24 #include "exechelper.h"
25 #include "barrydesktop.h"
26 #include "tempdir.h"
27 #include <iostream>
28 #include <sstream>
29 #include <algorithm>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <grp.h>
33 #include <limits.h>
34 #include <stdlib.h>
35 #include <wx/dir.h>
36 #include <wx/filename.h>
37 #include <barry/barry.h>
39 using namespace std;
41 // begin wxGlade: ::extracode
42 // end wxGlade
45 ModemDlg::ModemDlg(wxWindow* parent,
46 const std::vector<std::string> &peers,
47 const std::string &default_peer)
48 : wxDialog(parent, Dialog_Modem, _T("Modem Kickstart"))
50 bottom_buttons = CreateButtonSizer(wxOK | wxCANCEL);
52 // begin wxGlade: ModemDlg::ModemDlg
53 sizer_5_staticbox = new wxStaticBox(this, -1, wxT("Device"));
54 sizer_1_staticbox = new wxStaticBox(this, -1, wxT("Providers"));
55 const wxString list_box_1_choices[] = {
56 wxT("barry-minimal"),
57 wxT("barry-rogers"),
58 wxT("barry-testing")
60 list_box_1 = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 3, list_box_1_choices, wxLB_SINGLE);
61 label_1 = new wxStaticText(this, wxID_ANY, wxT("Password:"));
62 text_ctrl_1 = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD);
64 set_properties();
65 do_layout();
66 // end wxGlade
68 // add all peers to the listbox
69 list_box_1->Clear();
70 int default_index = -1, index = 0;
71 for( std::vector<std::string>::const_iterator i = peers.begin();
72 i != peers.end(); ++i, ++index )
74 if( default_peer == *i )
75 default_index = index;
76 list_box_1->Append(wxString(i->c_str(), wxConvUTF8));
79 if( default_index >= 0 ) {
80 list_box_1->SetSelection(default_index);
82 else {
83 list_box_1->SetSelection(0);
87 void ModemDlg::set_properties()
89 // begin wxGlade: ModemDlg::set_properties
90 SetTitle(wxT("Modem Starter"));
91 list_box_1->SetMinSize(wxSize(-1, 150));
92 list_box_1->SetSelection(0);
93 text_ctrl_1->SetMinSize(wxSize(150, -1));
94 // end wxGlade
98 void ModemDlg::do_layout()
100 // begin wxGlade: ModemDlg::do_layout
101 wxBoxSizer* sizer_2 = new wxBoxSizer(wxHORIZONTAL);
102 wxBoxSizer* sizer_3 = new wxBoxSizer(wxVERTICAL);
103 wxStaticBoxSizer* sizer_5 = new wxStaticBoxSizer(sizer_5_staticbox, wxHORIZONTAL);
104 wxStaticBoxSizer* sizer_1 = new wxStaticBoxSizer(sizer_1_staticbox, wxVERTICAL);
105 sizer_1->Add(list_box_1, 0, wxEXPAND, 0);
106 sizer_3->Add(sizer_1, 0, wxEXPAND, 0);
107 sizer_5->Add(label_1, 0, wxRIGHT|wxALIGN_CENTER_VERTICAL, 2);
108 sizer_5->Add(text_ctrl_1, 0, 0, 0);
109 sizer_3->Add(sizer_5, 1, wxEXPAND, 0);
110 sizer_2->Add(sizer_3, 0, wxALL|wxEXPAND, 7);
111 // end wxGlade
113 sizer_3->Add(bottom_buttons, 0, wxTOP|wxEXPAND, 5);
115 SetSizer(sizer_2);
116 sizer_2->Fit(this);
117 Layout();
121 // ProgramDetect
123 /// Searches for the given program name, in various directories, and
124 /// stores the following info:
125 /// Exists() - file exists, and if true, then:
126 /// GetPath() - returns full path of file, and:
127 /// IsExecutable() - returns true if file is a runnable program
128 /// IsSuid() - returns true if file is suid
129 /// GetGroup() - returns the group name of the file
131 /// For pppd, for example, this may return on Debian:
132 /// exists? true
133 /// path? /usr/sbin/pppd
134 /// executable? true if user in group, false if not
135 /// suid? true, suid root on Debian
136 /// group? uses the 'dip' group on Debian
138 /// On Fedora:
139 /// exists? true
140 /// path? /usr/sbin/pppd
141 /// exec? true
142 /// suid? false
143 /// group? root
145 class ProgramDetect
147 std::string m_path;
148 std::string m_group;
149 bool m_executable;
150 bool m_suid;
152 public:
153 void Dump(std::ostream &os)
155 os << "Path: " << m_path << "\n";
156 os << "Group: " << m_group << "\n";
157 os << "Exec: " << (m_executable ? "true" : "false") << "\n";
158 os << "Suid: " << (m_suid ? "true" : "false") << "\n";
161 const std::string& GetPath() const { return m_path; }
162 const std::string& GetGroup() const { return m_group; }
163 bool Exists() const { return m_path.size() > 0; }
164 bool IsExecutable() const { return m_executable; }
165 bool IsSuid() const { return m_suid; }
167 bool CheckPath(const std::string &path)
169 if( access(path.c_str(), F_OK) == 0 ) {
170 m_path = path;
171 return true;
173 return false;
176 void CheckAll(const char *prog, const std::string &path)
178 istringstream iss(path);
179 string p;
181 while( getline(iss, p, ':') ) {
182 string f = p + "/" + prog;
183 if( CheckPath(f) )
184 return;
188 ProgramDetect(const char *prog, const std::string &path)
189 : m_executable(false)
190 , m_suid(false)
192 // find the program first
193 CheckAll(prog, path);
195 // does it exist?
196 if( !m_path.size() )
197 return; // nope
199 // executable?
200 if( access(m_path.c_str(), X_OK) == 0 ) {
201 m_executable = true;
204 // suid?
205 struct stat s;
206 if( stat(m_path.c_str(), &s) == 0 ) {
207 if( s.st_mode & S_ISUID ) {
208 m_suid = true;
212 struct group *g = getgrgid(s.st_gid);
213 if( g ) {
214 m_group = g->gr_name;
220 std::string ModemDlg::GetPeerName() const
222 return string(list_box_1->GetStringSelection().utf8_str());
225 std::string ModemDlg::GetPassword() const
227 return string(text_ctrl_1->GetValue().utf8_str());
230 void ModemDlg::DoModem(wxWindow *parent, const Barry::Pin &pin)
233 // Search for dependency programs: xterm, pppd, gksu, etc.
236 // test whether xterm is in the path
237 ProgramDetect xterm("xterm", getenv("PATH"));
238 if( !xterm.Exists() || !xterm.IsExecutable() ) {
239 wxMessageBox(_T("Cannot locate the xterm program. This is used to display modem connection output. Please install it and try again."), _T("Xterm Not Found"), wxOK | wxICON_ERROR, parent);
240 return;
243 // in preparation for pppob search, find sbin, relative to our
244 // current application path
245 wxFileName app_path(wxTheApp->argv[0]);
246 string app_sbin(app_path.GetPath().utf8_str());
247 app_sbin += "/../sbin";
248 if( app_sbin[0] != '/' ) {
249 char cwdbuf[PATH_MAX];
250 getcwd(cwdbuf, PATH_MAX);
252 // insert current directory
253 app_sbin.insert(0, string(cwdbuf) + "/");
255 char *app_real = realpath(app_sbin.c_str(), NULL);
256 app_sbin = app_real;
257 free(app_real);
258 cout << "app_sbin = " << app_sbin << endl;
260 // test whether pppob is available... use app directory as
261 // part of search as well, in case Barry is installed in an
262 // unusual place
263 ProgramDetect pppob("pppob", app_sbin + ":/usr/sbin:" + getenv("PATH"));
264 if( !pppob.Exists() || !pppob.IsExecutable() ) {
265 wxMessageBox(_T("Cannot find pppob. Please make sure Barry's command line utilities are installed."), _T("pppob Not Found"), wxOK | wxICON_ERROR, parent);
266 return;
268 cout << pppob.GetPath() << endl;
270 // test whether we can run pppd, probably need to use full
271 // /usr/sbin/pppd path to reach it... check for return code
272 // of 0 or 2
273 ProgramDetect pppd("pppd", "/usr/sbin:/usr/bin");
274 if( !pppd.Exists() ) {
275 wxMessageBox(_T("Cannot find pppd. Please install it for modem use."), _T("pppd Not Found"), wxOK | wxICON_ERROR, parent);
276 return;
281 // Check if we need root access to run pppd
283 string need_sudo;
285 if( pppd.IsExecutable() && pppd.IsSuid() ) {
286 // all good!
288 else if( !pppd.IsExecutable() && pppd.IsSuid() && pppd.GetGroup() != "root" ) {
289 wxString gname(pppd.GetGroup().c_str(), wxConvUTF8);
290 wxString msg = wxString::Format(_T("Your system's PPP has the suid bit set, with a group of '%s'. You should add your account to the '%s' group, to avoid the need to enter the root password every time you use the modem.\n\nContinue anyway?"), gname.c_str(), gname.c_str());
291 int choice = wxMessageBox(msg, _T("System Group"), wxYES_NO,
292 parent);
293 if( choice != wxYES )
294 return;
296 need_sudo = BARRYDESKTOP_SYSTEM_GUI_SU + string(" ");
298 else if( pppd.IsExecutable() && !pppd.IsSuid() ) {
299 need_sudo = BARRYDESKTOP_SYSTEM_GUI_SU + string(" ");
303 // Check if we can run pppd, in the non-root case
305 if( need_sudo.size() == 0 ) {
306 ExecHelper eh(0);
307 wxString cmd((pppd.GetPath() + " permission_test").c_str(), wxConvUTF8);
308 if( !eh.Run(0, "", cmd) ) {
309 wxMessageBox(_T("Internal fork error. Should never happen."), _T("Cannot Run pppd"), wxOK | wxICON_ERROR, parent);
310 return;
313 eh.WaitForChild();
314 if( !(eh.GetChildExitCode() == 0 || eh.GetChildExitCode() == 2) ) {
315 wxString msg = wxString::Format(_T("Unable to run pppd correctly. Unexpected error code: %d, %s"), eh.GetChildExitCode(), cmd.c_str());
316 wxMessageBox(msg, _T("Error Code"), wxOK | wxICON_ERROR, parent);
317 return;
322 // Load all peer files.
324 // do a search in /etc/ppp/peers for all barry-* files and
325 // store in a vector
327 std::vector<std::string> peers;
328 wxDir dir(wxString("/etc/ppp/peers", wxConvUTF8));
329 if( !dir.IsOpened() ) {
330 wxMessageBox(_T("Unable to access files in /etc/ppp/peers. Do you have the correct permissions?"), _T("Cannot Open Peers"), wxOK | wxICON_ERROR, parent);
331 return;
333 wxString filename;
334 bool cont = dir.GetFirst(&filename, _T("barry-*"), wxDIR_FILES);
335 while( cont ) {
336 peers.push_back( string(filename.utf8_str()) );
337 cont = dir.GetNext(&filename);
340 // anything available?
341 if( !peers.size() ) {
342 wxMessageBox(_T("No providers found. Make sure Barry was properly installed, with peer files in /etc/ppp/peers."), _T("No Providers"), wxOK | wxICON_ERROR, parent);
343 return;
346 // sort the vector
347 sort(peers.begin(), peers.end());
351 // Double check that we can read the peer files
354 // do an access or file open test on the first barry-* file
355 // to make sure that we have access to peers/
356 string testfile = "/etc/ppp/peers/" + peers[0];
357 if( access(testfile.c_str(), R_OK) != 0 ) {
358 wxString msg = wxString::Format(_T("Cannot read provider files under /etc/ppp/peers. Please check your file permissions. (Access failed for %s)"), wxString(testfile.c_str(), wxConvUTF8).c_str());
359 wxMessageBox(msg, _T("Permissions Error"), wxOK | wxICON_ERROR, parent);
360 return;
365 // Fetch default peer choice
367 Barry::GlobalConfigFile &config = wxGetApp().GetGlobalConfig();
368 string key = pin.Str() + "-DefaultPeer";
369 string default_peer = config.GetKey(key);
373 // Show the dialog
375 ModemDlg dlg(parent, peers, default_peer);
376 if( dlg.ShowModal() == wxID_OK ) {
377 string password = dlg.GetPassword();
378 string peer = dlg.GetPeerName();
379 string peerfile = "/etc/ppp/peers/" + peer;
380 if( !peer.size() || access(peerfile.c_str(), R_OK) != 0 ) {
381 wxString msg = wxString::Format(_T("Unable to open peer file: %s"), wxString(peerfile.c_str(), wxConvUTF8).c_str());
382 wxMessageBox(msg, _T("Invalid Peer"), wxOK | wxICON_ERROR, parent);
383 return;
386 // save peer selection as default for this device
387 config.SetKey(key, peer);
389 // create shell script which calls pppd with proper
390 // pty pppob override, and password if needed
391 TempDir tempdir("BarryDesktopModem");
392 string tmp_path = tempdir.GetNewFilename();
394 ofstream otmp(tmp_path.c_str());
395 otmp << "#!/bin/sh" << endl;
396 otmp << "echo Starting pppd for device PIN "
397 << pin.Str() << "... " << endl;
398 // FIXME - need gksu here, for the pty override :-( Need method to
399 // pass options to pppob without root
400 otmp << need_sudo << pppd.GetPath()
401 << " call " << peer
402 << " pty \"" << pppob.GetPath()
403 << " -p " << pin.Str();
404 if( password.size() )
405 otmp << " -P " << password;
406 otmp << '"' << endl;
407 otmp << "echo Press enter to close window..." << endl;
408 otmp << "read" << endl;
409 otmp.close();
411 chmod(tmp_path.c_str(), S_IRUSR | S_IRGRP | S_IROTH |
412 S_IXUSR | S_IXGRP | S_IXOTH);
414 // create command line using xterm as display
415 wxString xterm_cmd((xterm.GetPath() + " " + tmp_path).c_str(),
416 wxConvUTF8);
418 // run! and go back to main screen
419 ExecHelper run(0);
420 run.Run(parent, "modem", xterm_cmd);
422 // let run, then let TempDir cleanup
423 sleep(2);