diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bcb704366cb5e333a626c18c308c7e0448a8e69f (patch) | |
tree | f0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kppp/opener.cpp | |
download | tdenetwork-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.cpp | 722 |
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"); +} |