/* * kPPP: A pppd front end for the KDE project * * Copyright (C) 1997 Bernd Johannes Wuebben * wuebben@math.cornell.edu * Copyright (C) 1998-2002 Harri Porten * * based on EzPPP: * Copyright (C) 1997 Jay Painter * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "main.h" #include #include #include #include #include #include #include #include #ifdef _XPG4_2 #define __xnet_connect connect #endif #ifdef HAVE_SYS_PARAM_H #include #endif #include #include #include #include #include #include #include #include #include "kpppwidget.h" #include "opener.h" #include "pppdata.h" #include "providerdb.h" #include "version.h" #include "requester.h" #include static const char description[] = I18N_NOOP("A dialer and front-end to pppd"); static const TDECmdLineOptions options[] = { { "c ", I18N_NOOP("Connect using 'account_name'"), 0 }, { "m ", I18N_NOOP("Connect using 'modem_name'"), 0 }, { "k", I18N_NOOP("Terminate an existing connection"), 0 }, { "q", I18N_NOOP("Quit after end of connection"), 0 }, { "r ", I18N_NOOP("Check syntax of rule_file"), 0 }, { "T", I18N_NOOP("Enable test-mode"), 0 }, { "dev ", I18N_NOOP("Use the specified device"), 0 }, TDECmdLineLastOption }; KPPPWidget* p_kppp; // for testing purposes bool TESTING=0; // initial effective user id before possible suid status is dropped uid_t euid; // helper process' pid pid_t helperPid = -1; TQString local_ip_address; TQString remote_ip_address; TQString pidfile; #if 0 extern "C" { static int kppp_x_errhandler( Display *dpy, XErrorEvent *err ) { char errstr[256]; // safe /* if(gpppdata.pppdpid() >= 0) { kill(gpppdata.pppdpid(), SIGTERM); } p_kppp->stopAccounting(); removedns(); unlockdevice();*/ XGetErrorText( dpy, err->error_code, errstr, 256 ); kdFatal() << "X Error: " << errstr << endl; kdFatal() << "Major opcode: " << err->request_code << endl; exit(256); return 0; } static int kppp_xio_errhandler( Display * ) { if(gpppdata.get_xserver_exit_disconnect()) { fprintf(stderr, "X11 Error!\n"); if(gpppdata.pppdRunning()) Requester::rq->killPPPDaemon(); p_kppp->stopAccounting(); removedns(); Modem::modem->unlockdevice(); return 0; } else{ kdFatal() << "Fatal IO error: client killed" << endl; exit(256); return 0; } } } /* extern "C" */ #endif int main( int argc, char **argv ) { // make sure that open/fopen and so on NEVER return 1 or 2 (stdout and stderr) // Expl: if stdout/stderr were closed on program start (by parent), open() // would return a FD of 1, 2 (or even 0 if stdin was closed too) if(fcntl(0, F_GETFL) == -1) (void)open("/dev/null", O_RDONLY); if(fcntl(1, F_GETFL) == -1) (void)open("/dev/null", O_WRONLY); if(fcntl(2, F_GETFL) == -1) (void)open("/dev/null", O_WRONLY); // Don't insert anything above this line unless you really know what // you're doing. We're most likely running setuid root here, // until we drop this status a few lines below. int sockets[2]; if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) != 0) { fprintf(stderr, "error creating socketpair !\n"); return 1; } switch(helperPid = fork()) { case 0: // child process // make process leader of new group setsid(); umask(0); close(sockets[0]); signal(SIGHUP, SIG_IGN); (void) new Opener(sockets[1]); // we should never get here _exit(1); case -1: perror("fork() failed"); exit(1); } // parent process close(sockets[1]); // drop setuid status euid = geteuid(); if (setgid(getgid()) < 0 && errno != EPERM) { perror("setgid() failed"); exit(1); } setuid(getuid()); if (geteuid() != getuid()) { perror("setuid() failed"); exit(1); } // // end of setuid-dropping block. // // install exit handler that will kill the helper process atexit(myShutDown); // not needed anymore, just causes problems with broken setup // if(getHomeDir() != 0) // setenv("HOME", getHomeDir(), 1); (void) new Requester(sockets[0]); TDEAboutData aboutData("kppp", I18N_NOOP("KPPP"), KPPPVERSION, description, TDEAboutData::License_GPL, I18N_NOOP("(c) 1999-2002, The KPPP Developers")); aboutData.addAuthor("Harri Porten", I18N_NOOP("Current maintainer"), "porten@kde.org"); aboutData.addAuthor("Bernd Wuebben", I18N_NOOP("Original author"), "wuebben@kde.org"); aboutData.addAuthor("Mario Weilguni",0, ""); TDECmdLineArgs::init( argc, argv, &aboutData ); TDECmdLineArgs::addCmdLineOptions( options ); TDEApplication a; // set portable locale for decimal point setlocale(LC_NUMERIC ,"C"); // open configuration file gpppdata.open(); kdDebug(5002) << "helperPid: " << (int) helperPid << endl; TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); bool terminate_connection = args->isSet("k"); if (args->isSet("r")) return RuleSet::checkRuleFile(args->getOption("r")); TESTING = args->isSet("T"); // make sure that nobody can read the password from the // config file TQString configFile = TDEGlobal::dirs()->saveLocation("config") + TQString(kapp->name()) + "rc"; if(access(TQFile::encodeName(configFile), F_OK) == 0) chmod(TQFile::encodeName(configFile), S_IRUSR | S_IWUSR); // do we really need to generate an empty directory structure here ? TDEGlobal::dirs()->saveLocation("appdata", "Rules"); int pid = create_pidfile(); TQString err_msg = i18n("kppp can't create or read from\n%1.").arg(pidfile); if(pid < 0) { KMessageBox::error(0L, err_msg); return 1; } if (terminate_connection) { setgid(getgid()); setuid(getuid()); if (pid > 0) kill(pid, SIGINT); else remove_pidfile(); return 0; } // Mario: testing if(TESTING) { gpppdata.open(); gpppdata.setAccountByIndex(0); TQString s = argv[2]; urlEncode(s); kdDebug(5002) << s << endl; remove_pidfile(); return 0; } if (pid > 0) { TQString msg = i18n("kppp has detected a %1 file.\n" "Another instance of kppp seems to be " "running under process-ID %2.\n" "Please click Exit, make sure that you are " "not running another kppp, delete the pid " "file, and restart kppp.\n" "Alternatively, if you have determined that " "there is no other kppp running, please " "click Continue to begin.") .arg(pidfile).arg(pid); int button = KMessageBox::warningYesNo(0, msg, i18n("Error"), i18n("Exit"), KStdGuiItem::cont()); if (button == KMessageBox::Yes) /* exit */ return 1; remove_pidfile(); pid = create_pidfile(); if(pid) { KMessageBox::error(0L, err_msg); return 1; } } KPPPWidget kppp; p_kppp = &kppp; (void)new DockWidget(p_kppp->con_win, "dockw", p_kppp->stats); a.setMainWidget(&kppp); a.setTopWidget(&kppp); // we really don't want to die accidentally, since that would leave the // modem connected. If you really really want to kill me you must send // me a SIGKILL. signal(SIGINT, sighandler); signal(SIGCHLD, sighandler); signal(SIGUSR1, sighandler); signal(SIGTERM, SIG_IGN); // XSetErrorHandler( kppp_x_errhandler ); // XSetIOErrorHandler( kppp_xio_errhandler ); int ret = a.exec(); remove_pidfile(); return ret; } pid_t execute_command (const TQString & cmd) { TQCString command = TQFile::encodeName(cmd); if (command.isEmpty() || command.length() > COMMAND_SIZE) return (pid_t) -1; pid_t id; kdDebug(5002) << "Executing command: " << command << endl; TQApplication::flushX(); if((id = fork()) == 0) { // don't bother dieppp() signal(SIGCHLD, SIG_IGN); // close file descriptors const int open_max = sysconf( _SC_OPEN_MAX ); for (int fd = 3; fd < open_max; ++fd) close(fd); // drop privileges if running setuid root setgid(getgid()); setuid(getuid()); system(command); _exit(0); } return id; } // Create a file containing the current pid. Returns 0 on success, // -1 on failure or the pid of an already running kppp process. pid_t create_pidfile() { int fd = -1; char pidstr[40]; // safe pidfile = TDEGlobal::dirs()->saveLocation("appdata") + "kppp.pid"; if(access(TQFile::encodeName(pidfile), F_OK) == 0) { if((access(TQFile::encodeName(pidfile), R_OK) < 0) || (fd = open(TQFile::encodeName(pidfile), O_RDONLY)) < 0) return -1; int sz = read(fd, &pidstr, 32); close (fd); if (sz < 0) return -1; pidstr[sz] = '\0'; kdDebug(5002) << "found kppp.pid containing: " << pidstr << endl; // non-empty file ? if (sz > 0) { int oldpid; int match = sscanf(pidstr, "%d", &oldpid); // found a pid in pidfile ? if (match < 1 || oldpid <= 0) return -1; // check if process exists if (kill((pid_t)oldpid, 0) == 0 || errno != ESRCH) return oldpid; } kdDebug(5002) << "pidfile is stale\n" << endl; remove_pidfile(); } if((fd = open(TQFile::encodeName(pidfile), O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) return -1; fchown(fd, getuid(), getgid()); sprintf(pidstr, "%d\n", getpid()); write(fd, pidstr, strlen(pidstr)); close(fd); return 0; } bool remove_pidfile() { struct stat st; // only remove regular files with user write permissions if(stat(TQFile::encodeName(pidfile), &st) == 0 ) if(S_ISREG(st.st_mode) && (access(TQFile::encodeName(pidfile), W_OK) == 0)) { unlink(TQFile::encodeName(pidfile)); return true; } fprintf(stderr, "error removing pidfile.\n"); return false; } void myShutDown() { pid_t pid; // don't bother about SIGCHLDs anymore signal(SIGCHLD, SIG_IGN); // fprintf(stderr, "myShutDown(%i)\n", status); pid = helperPid; if(pid > 0) { helperPid = -1; // fprintf(stderr, "killing child process %i", pid); kill(pid, SIGKILL); } } void sighandler(int sig) { TQEvent *e = 0L; if(sig == SIGCHLD) { pid_t id = wait(0L); if(id >= 0 && id == helperPid) // helper process died e = new SignalEvent(sig); } else if(sig == SIGINT || sig == SIGUSR1) e = new SignalEvent(sig); // let eventFilter() deal with this when we're back in the loop if (e) TQApplication::postEvent(p_kppp, e); signal(sig, sighandler); // reinstall signal handler }