summaryrefslogtreecommitdiffstats
path: root/kppp/opener.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbcb704366cb5e333a626c18c308c7e0448a8e69f (patch)
treef0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kppp/opener.cpp
downloadtdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz
tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kppp/opener.cpp')
-rw-r--r--kppp/opener.cpp722
1 files changed, 722 insertions, 0 deletions
diff --git a/kppp/opener.cpp b/kppp/opener.cpp
new file mode 100644
index 00000000..be72f631
--- /dev/null
+++ b/kppp/opener.cpp
@@ -0,0 +1,722 @@
+
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997,98 Bernd Johannes Wuebben,
+ * Mario Weilguni
+ * Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* A note to developers:
+ *
+ * Apart from the first dozen lines in main() the following code represents
+ * the setuid root part of kppp. So please be careful !
+ * o restrain from using X, Qt or KDE library calls
+ * o check for possible buffer overflows
+ * o handle requests from the parent process with care. They might be forged.
+ * o be paranoid and think twice about everything you change.
+ */
+
+#include <config.h>
+
+#if defined(__osf__) || defined(__svr4__)
+#define _POSIX_PII_SOCKET
+extern "C" int sethostname(char *name, int name_len);
+#if !defined(__osf__)
+extern "C" int _Psendmsg(int, void*, int);
+extern "C" int _Precvmsg(int, void*, int);
+#endif
+#endif
+
+#include "kpppconfig.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+
+
+#include <netinet/in.h>
+
+#ifdef __FreeBSD__
+# include <sys/linker.h> // for kldload
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef HAVE_NET_IF_PPP_H
+# if defined(__DragonFly__)
+# include <net/ppp_layer/ppp_defs.h>
+# include <net/if.h>
+# include <net/ppp/if_ppp.h>
+# elif defined HAVE_LINUX_IF_PPP_H
+# include <linux/if_ppp.h>
+# endif
+#else
+# include <net/ppp_defs.h>
+# include <net/if.h>
+# include <net/if_ppp.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include "opener.h"
+#include "devices.h"
+
+#ifdef HAVE_RESOLV_H
+# include <arpa/nameser.h>
+# include <resolv.h>
+#endif
+
+#ifndef _PATH_RESCONF
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
+
+#ifdef _XPG4_2
+extern "C" {
+ ssize_t recvmsg(int, struct msghdr *, int);
+ ssize_t sendmsg(int, const struct msghdr *, int);
+}
+#endif
+
+#define MY_ASSERT(x) if (!(x)) { \
+ fprintf(stderr, "ASSERT: \"%s\" in %s (%d)\n",#x,__FILE__,__LINE__); \
+ exit(1); }
+
+#define MY_DEBUG
+#ifndef MY_DEBUG
+#define Debug(s) ((void)0);
+#define Debug2(s, i) ((void)0);
+#else
+#define Debug(s) fprintf(stderr, (s "\n"));
+#define Debug2(s, i) fprintf(stderr, (s), (i));
+#endif
+
+static void sighandler_child(int);
+static pid_t pppdPid = -1;
+static int pppdExitStatus = -1;
+static int checkForInterface();
+
+// processing will stop at first file that could be opened successfully
+const char * const kppp_syslog[] = { "/var/log/syslog.ppp",
+ "/var/log/syslog",
+ "/var/log/messages",
+ 0 };
+
+Opener::Opener(int s) : socket(s), ttyfd(-1) {
+ lockfile[0] = '\0';
+ signal(SIGUSR1, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGCHLD, sighandler_child);
+ mainLoop();
+}
+
+void Opener::mainLoop() {
+
+ int len;
+ int fd = -1;
+ int flags, mode;
+ const char *device, * const *logFile;
+ union AllRequests request;
+ struct ResponseHeader response;
+ struct msghdr msg;
+ struct iovec iov;
+
+ iov.iov_base = IOV_BASE_CAST &request;
+ iov.iov_len = sizeof(request);
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = 0L;
+ msg.msg_controllen = 0;
+
+ // loop forever
+ while(1) {
+ len = recvmsg(socket, &msg, 0);
+ if(len < 0) {
+ switch(errno) {
+ case EINTR:
+ Debug("Opener: interrupted system call, continuing");
+ break;
+ default:
+ perror("Opener: error reading from socket");
+ _exit(1);
+ }
+ } else {
+ switch(request.header.type) {
+
+ case OpenDevice:
+ Debug("Opener: received OpenDevice");
+ MY_ASSERT(len == sizeof(struct OpenModemRequest));
+ close(ttyfd);
+ device = deviceByIndex(request.modem.deviceNum);
+ response.status = 0;
+ if ((ttyfd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) == -1) {
+ Debug("error opening modem device !");
+ fd = open(DEVNULL, O_RDONLY);
+ response.status = -errno;
+ sendFD(fd, &response);
+ close(fd);
+ } else
+ sendFD(ttyfd, &response);
+ break;
+
+ case OpenLock:
+ Debug("Opener: received OpenLock\n");
+ MY_ASSERT(len == sizeof(struct OpenLockRequest));
+ flags = request.lock.flags;
+ MY_ASSERT(flags == O_RDONLY || flags == O_WRONLY|O_TRUNC|O_CREAT);
+ if(flags == O_WRONLY|O_TRUNC|O_CREAT)
+ mode = 0644;
+ else
+ mode = 0;
+
+ device = deviceByIndex(request.lock.deviceNum);
+ MY_ASSERT(strlen(LOCK_DIR)+strlen(device) < MaxPathLen);
+ strlcpy(lockfile, LOCK_DIR"/LCK..", MaxPathLen);
+ strlcat(lockfile, strrchr(device, '/') + 1, MaxPathLen );
+ response.status = 0;
+ // TODO:
+ // struct stat st;
+ // if(stat(lockfile.data(), &st) == -1) {
+ // if(errno == EBADF)
+ // return -1;
+ // } else {
+ // // make sure that this is a regular file
+ // if(!S_ISREG(st.st_mode))
+ // return -1;
+ // }
+ if ((fd = open(lockfile, flags, mode)) == -1) {
+ Debug("error opening lockfile!");
+ lockfile[0] = '\0';
+ fd = open(DEVNULL, O_RDONLY);
+ response.status = -errno;
+ } else
+ fchown(fd, 0, 0);
+ sendFD(fd, &response);
+ close(fd);
+ break;
+
+ case RemoveLock:
+ Debug("Opener: received RemoveLock");
+ MY_ASSERT(len == sizeof(struct RemoveLockRequest));
+ close(ttyfd);
+ ttyfd = -1;
+ response.status = unlink(lockfile);
+ lockfile[0] = '\0';
+ sendResponse(&response);
+ break;
+
+ case OpenResolv:
+ Debug("Opener: received OpenResolv");
+ MY_ASSERT(len == sizeof(struct OpenResolvRequest));
+ flags = request.resolv.flags;
+ response.status = 0;
+ if ((fd = open(_PATH_RESCONF, flags)) == -1) {
+ Debug("error opening resolv.conf!");
+ fd = open(DEVNULL, O_RDONLY);
+ response.status = -errno;
+ }
+ sendFD(fd, &response);
+ close(fd);
+ break;
+
+ case OpenSysLog:
+ Debug("Opener: received OpenSysLog");
+ MY_ASSERT(len == sizeof(struct OpenLogRequest));
+ response.status = 0;
+ logFile = &kppp_syslog[0];
+ while (*logFile) {
+ if ((fd = open(*logFile, O_RDONLY)) >= 0)
+ break;
+ logFile++;
+ }
+ if (!*logFile) {
+ Debug("No success opening a syslog file !");
+ fd = open(DEVNULL, O_RDONLY);
+ response.status = -errno;
+ }
+ sendFD(fd, &response);
+ close(fd);
+ break;
+
+ case SetSecret:
+ Debug("Opener: received SetSecret");
+ MY_ASSERT(len == sizeof(struct SetSecretRequest));
+ response.status = !createAuthFile(request.secret.method,
+ request.secret.username,
+ request.secret.password);
+ sendResponse(&response);
+ break;
+
+ case RemoveSecret:
+ Debug("Opener: received RemoveSecret");
+ MY_ASSERT(len == sizeof(struct RemoveSecretRequest));
+ response.status = !removeAuthFile(request.remove.method);
+ sendResponse(&response);
+ break;
+
+ case SetHostname:
+ Debug("Opener: received SetHostname");
+ MY_ASSERT(len == sizeof(struct SetHostnameRequest));
+ response.status = 0;
+ if(sethostname(request.host.name, strlen(request.host.name)))
+ response.status = -errno;
+ sendResponse(&response);
+ break;
+
+ case ExecPPPDaemon:
+ Debug("Opener: received ExecPPPDaemon");
+ MY_ASSERT(len == sizeof(struct ExecDaemonRequest));
+ response.status = execpppd(request.daemon.arguments);
+ sendResponse(&response);
+ break;
+
+ case KillPPPDaemon:
+ Debug("Opener: received KillPPPDaemon");
+ MY_ASSERT(len == sizeof(struct KillDaemonRequest));
+ response.status = killpppd();
+ sendResponse(&response);
+ break;
+
+ case PPPDExitStatus:
+ Debug("Opener: received PPPDExitStatus");
+ MY_ASSERT(len == sizeof(struct PPPDExitStatusRequest));
+ response.status = pppdExitStatus;
+ sendResponse(&response);
+ break;
+
+ case Stop:
+ Debug("Opener: received STOP command");
+ _exit(0);
+ break;
+
+ default:
+ Debug("Opener: unknown command type. Exiting ...");
+ _exit(1);
+ }
+ } // else
+ }
+}
+
+
+//
+// Send an open fd over a UNIX socket pair
+//
+int Opener::sendFD(int fd, struct ResponseHeader *response) {
+
+ struct { struct cmsghdr cmsg; int fd; } control;
+ struct msghdr msg;
+ struct iovec iov;
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ // Send data
+ iov.iov_base = IOV_BASE_CAST response;
+ iov.iov_len = sizeof(struct ResponseHeader);
+
+ // Send a (duplicate of) the file descriptor
+ control.cmsg.cmsg_len = sizeof(struct cmsghdr) + sizeof(int);
+ control.cmsg.cmsg_level = SOL_SOCKET;
+ control.cmsg.cmsg_type = MY_SCM_RIGHTS;
+
+ msg.msg_control = (char *) &control;
+ msg.msg_controllen = control.cmsg.cmsg_len;
+
+#ifdef CMSG_DATA
+ *((int *)CMSG_DATA(&control.cmsg)) = fd;
+#else
+ *((int *) &control.cmsg.cmsg_data) = fd;
+#endif
+
+ if (sendmsg(socket, &msg, 0) < 0) {
+ perror("unable to send file descriptors");
+ return -1;
+ }
+
+ return 0;
+}
+
+int Opener::sendResponse(struct ResponseHeader *response) {
+
+ struct msghdr msg;
+ struct iovec iov;
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = 0L;
+ msg.msg_controllen = 0;
+
+ // Send data
+ iov.iov_base = IOV_BASE_CAST response;
+ iov.iov_len = sizeof(struct ResponseHeader);
+
+ if (sendmsg(socket, &msg, 0) < 0) {
+ perror("unable to send response");
+ return -1;
+ }
+
+ return 0;
+}
+
+const char* Opener::deviceByIndex(int idx) {
+
+ const char *device = 0L;
+
+ for(int i = 0; devices[i]; i++)
+ if(i == idx)
+ device = devices[i];
+ MY_ASSERT(device);
+ return device;
+}
+
+bool Opener::createAuthFile(Auth method, char *username, char *password) {
+ const char *authfile, *oldName, *newName;
+ char line[100];
+ char regexp[2*MaxStrLen+30];
+ regex_t preg;
+
+ if(!(authfile = authFile(method)))
+ return false;
+
+ if(!(newName = authFile(method, New)))
+ return false;
+
+ // look for username, "username" or 'username'
+ // if you modify this RE you have to adapt regexp's size above
+ snprintf(regexp, sizeof(regexp), "^[ \t]*%s[ \t]\\|^[ \t]*[\"\']%s[\"\']",
+ username,username);
+ MY_ASSERT(regcomp(&preg, regexp, 0) == 0);
+
+ // copy to new file pap- or chap-secrets
+ int old_umask = umask(0077);
+ FILE *fout = fopen(newName, "w");
+ if(fout) {
+ // copy old file
+ FILE *fin = fopen(authfile, "r");
+ if(fin) {
+ while(fgets(line, sizeof(line), fin)) {
+ if(regexec(&preg, line, 0, 0L, 0) == 0)
+ continue;
+ fputs(line, fout);
+ }
+ fclose(fin);
+ }
+
+ // append user/pass pair
+ fprintf(fout, "\"%s\"\t*\t\"%s\"\n", username, password);
+ fclose(fout);
+ }
+
+ // restore umask
+ umask(old_umask);
+
+ // free memory allocated by regcomp
+ regfree(&preg);
+
+ if(!(oldName = authFile(method, Old)))
+ return false;
+
+ // delete old file if any
+ unlink(oldName);
+
+ rename(authfile, oldName);
+ rename(newName, authfile);
+
+ return true;
+}
+
+
+bool Opener::removeAuthFile(Auth method) {
+ const char *authfile, *oldName;
+
+ if(!(authfile = authFile(method)))
+ return false;
+ if(!(oldName = authFile(method, Old)))
+ return false;
+
+ if(access(oldName, F_OK) == 0) {
+ unlink(authfile);
+ return (rename(oldName, authfile) == 0);
+ } else
+ return false;
+}
+
+
+const char* Opener::authFile(Auth method, int version) {
+ switch(method|version) {
+ case PAP|Original:
+ return PAP_AUTH_FILE;
+ break;
+ case PAP|New:
+ return PAP_AUTH_FILE".new";
+ break;
+ case PAP|Old:
+ return PAP_AUTH_FILE".old";
+ break;
+ case CHAP|Original:
+ return CHAP_AUTH_FILE;
+ break;
+ case CHAP|New:
+ return CHAP_AUTH_FILE".new";
+ break;
+ case CHAP|Old:
+ return CHAP_AUTH_FILE".old";
+ break;
+ default:
+ return 0L;
+ }
+}
+
+
+bool Opener::execpppd(const char *arguments) {
+ char buf[MAX_CMDLEN];
+ char *args[MaxArgs];
+ pid_t pgrpid;
+
+ if(ttyfd<0)
+ return false;
+
+ pppdExitStatus = -1;
+
+ switch(pppdPid = fork())
+ {
+ case -1:
+ fprintf(stderr,"In parent: fork() failed\n");
+ return false;
+ break;
+
+ case 0:
+ // let's parse the arguments the user supplied into UNIX suitable form
+ // that is a list of pointers each pointing to exactly one word
+ strlcpy(buf, arguments, sizeof(buf));
+ parseargs(buf, args);
+ // become a session leader and let /dev/ttySx
+ // be the controlling terminal.
+ pgrpid = setsid();
+#ifdef TIOCSCTTY
+ if(ioctl(ttyfd, TIOCSCTTY, 0)<0)
+ fprintf(stderr, "ioctl() failed.\n");
+#elif defined (TIOCSPGRP)
+ if(ioctl(ttyfd, TIOCSPGRP, &pgrpid)<0)
+ fprintf(stderr, "ioctl() failed.\n");
+#endif
+ if(tcsetpgrp(ttyfd, pgrpid)<0)
+ fprintf(stderr, "tcsetpgrp() failed.\n");
+
+ dup2(ttyfd, 0);
+ dup2(ttyfd, 1);
+
+ switch (checkForInterface()) {
+ case 1:
+ fprintf(stderr, "Cannot determine if kernel supports ppp.\n");
+ break;
+ case -1:
+ fprintf(stderr, "Kernel does not support ppp, oops.\n");
+ break;
+ case 0:
+ fprintf(stderr, "Kernel supports ppp alright.\n");
+ break;
+ }
+
+ execve(pppdPath(), args, 0L);
+ _exit(0);
+ break;
+
+ default:
+ Debug2("In parent: pppd pid %d\n",pppdPid);
+ close(ttyfd);
+ ttyfd = -1;
+ return true;
+ break;
+ }
+}
+
+
+bool Opener::killpppd()const {
+ if(pppdPid > 0) {
+ Debug2("In killpppd(): Sending SIGTERM to %d\n", pppdPid);
+ if(kill(pppdPid, SIGTERM) < 0) {
+ Debug2("Error terminating %d. Sending SIGKILL\n", pppdPid);
+ if(kill(pppdPid, SIGKILL) < 0) {
+ Debug2("Error killing %d\n", pppdPid);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+void Opener::parseargs(char* buf, char** args) {
+ int nargs = 0;
+ int quotes;
+
+ while(nargs < MaxArgs-1 && *buf != '\0') {
+
+ quotes = 0;
+
+ // Strip whitespace. Use nulls, so that the previous argument is
+ // terminated automatically.
+
+ while ((*buf == ' ' ) || (*buf == '\t' ) || (*buf == '\n' ) )
+ *buf++ = '\0';
+
+ // detect begin of quoted argument
+ if (*buf == '"' || *buf == '\'') {
+ quotes = *buf;
+ *buf++ = '\0';
+ }
+
+ // save the argument
+ if(*buf != '\0') {
+ *args++ = buf;
+ nargs++;
+ }
+
+ if (!quotes)
+ while ((*buf != '\0') && (*buf != '\n') &&
+ (*buf != '\t') && (*buf != ' '))
+ buf++;
+ else {
+ while ((*buf != '\0') && (*buf != quotes))
+ buf++;
+ *buf++ = '\0';
+ }
+ }
+
+ *args = 0L;
+}
+
+
+const char* pppdPath() {
+ // wasting a few bytes
+ static char buffer[sizeof(PPPDSEARCHPATH)+sizeof(PPPDNAME)];
+ static char *pppdPath = 0L;
+ char *p;
+
+ if(pppdPath == 0L) {
+ const char *c = PPPDSEARCHPATH;
+ while(*c != '\0') {
+ while(*c == ':')
+ c++;
+ p = buffer;
+ while(*c != '\0' && *c != ':')
+ *p++ = *c++;
+ *p = '\0';
+ strcat(p, "/");
+ strcat(p, PPPDNAME);
+ if(access(buffer, F_OK) == 0)
+ return (pppdPath = buffer);
+ }
+ }
+
+ return pppdPath;
+}
+
+int checkForInterface()
+{
+// I don't know if Linux needs more initialization to get the ioctl to
+// work, pppd seems to hint it does. But BSD doesn't, and the following
+// code should compile.
+#if (defined(HAVE_NET_IF_PPP_H) || defined(HAVE_LINUX_IF_PPP_H)) && !defined(__svr4__)
+ int s, ok;
+ struct ifreq ifr;
+ // extern char *no_ppp_msg;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return 1; /* can't tell */
+
+ strlcpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+ ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+ close(s);
+
+ if (ok == -1) {
+// This is ifdef'd FreeBSD, because FreeBSD is the only BSD that supports
+// KLDs, the old LKM interface couldn't handle loading devices
+// dynamically, and thus can't load ppp support on the fly
+#ifdef __FreeBSD__
+ // If we failed to load ppp support and don't have it already.
+ if (kldload("if_ppp") == -1) {
+ return -1;
+ }
+ return 0;
+#else
+ return -1;
+#endif
+ }
+ return 0;
+#else
+// We attempt to use the SunOS/SysVr4 method and stat /dev/ppp
+ struct stat buf;
+
+ memset(&buf, 0, sizeof(buf));
+ return stat("/dev/ppp", &buf);
+#endif
+}
+
+
+void sighandler_child(int) {
+ pid_t pid;
+ int status;
+
+ signal(SIGCHLD, sighandler_child);
+ if(pppdPid>0) {
+ pid = waitpid(pppdPid, &status, WNOHANG);
+ if(pid != pppdPid) {
+ fprintf(stderr, "received SIGCHLD from unknown origin.\n");
+ } else {
+ Debug("It was pppd that died");
+ pppdPid = -1;
+ if((WIFEXITED(status))) {
+ pppdExitStatus = (WEXITSTATUS(status));
+ Debug2("pppd exited with return value %d\n", pppdExitStatus);
+ } else {
+ pppdExitStatus = 99;
+ Debug("pppd exited abnormally.");
+ }
+ Debug2("Sending %i a SIGUSR1\n", getppid());
+ kill(getppid(), SIGUSR1);
+ }
+ } else
+ fprintf(stderr, "received unexpected SIGCHLD.\n");
+}