3 * kPPP: A pppd Front End for the KDE project
7 * Copyright (C) 1997,98 Bernd Johannes Wuebben,
9 * Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free
24 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 /* A note to developers:
29 * Apart from the first dozen lines in main() the following code represents
30 * the setuid root part of kppp. So please be careful !
31 * o restrain from using X, Qt or KDE library calls
32 * o check for possible buffer overflows
33 * o handle requests from the parent process with care. They might be forged.
34 * o be paranoid and think twice about everything you change.
37 #include <config-kppp.h>
39 #if defined(__osf__) || defined(__svr4__)
40 #define _POSIX_PII_SOCKET
41 extern "C" int sethostname(char *name
, int name_len
);
43 extern "C" int _Psendmsg(int, void*, int);
44 extern "C" int _Precvmsg(int, void*, int);
48 #include "kpppconfig.h"
50 #include <sys/types.h>
53 #include <sys/socket.h>
54 #include <sys/ioctl.h>
57 #include <sys/param.h>
60 #include <netinet/in.h>
63 # include <sys/linker.h> // for kldload
66 #ifndef HAVE_NET_IF_PPP_H
67 # ifdef HAVE_LINUX_IF_PPP_H
68 # include <linux/if_ppp.h>
71 # include <net/ppp_defs.h>
73 # include <net/if_ppp.h>
90 # include <arpa/nameser.h>
95 #define _PATH_RESCONF "/etc/resolv.conf"
100 ssize_t
recvmsg(int, struct msghdr
*, int);
101 ssize_t
sendmsg(int, const struct msghdr
*, int);
105 #define MY_ASSERT(x) if (!(x)) { \
106 fprintf(stderr, "ASSERT: \"%s\" in %s (%d)\n",#x,__FILE__,__LINE__); \
111 #define Debug(s) ((void)0);
112 #define Debug2(s, i) ((void)0);
114 #define Debug(s) fprintf(stderr, (s "\n"));
115 #define Debug2(s, i) fprintf(stderr, (s), (i));
118 static void sighandler_child(int);
119 static pid_t pppdPid
= -1;
120 static int pppdExitStatus
= -1;
121 static int checkForInterface();
123 // processing will stop at first file that could be opened successfully
124 const char * const kppp_syslog
[] = { "/var/log/syslog.ppp",
129 Opener::Opener(int s
) : socket(s
), ttyfd(-1) {
131 signal(SIGUSR1
, SIG_IGN
);
132 signal(SIGTERM
, SIG_IGN
);
133 signal(SIGINT
, SIG_IGN
);
134 signal(SIGCHLD
, sighandler_child
);
138 void Opener::mainLoop() {
143 const char *device
, * const *logFile
;
144 union AllRequests request
;
145 struct ResponseHeader response
;
149 iov
.iov_base
= IOV_BASE_CAST
&request
;
150 iov
.iov_len
= sizeof(request
);
156 msg
.msg_control
= 0L;
157 msg
.msg_controllen
= 0;
161 len
= recvmsg(socket
, &msg
, 0);
165 Debug("Opener: interrupted system call, continuing");
168 perror("Opener: error reading from socket");
172 switch(request
.header
.type
) {
175 Debug("Opener: received OpenDevice");
176 MY_ASSERT(len
== sizeof(struct OpenModemRequest
));
178 device
= deviceByIndex(request
.modem
.deviceNum
);
180 if ((ttyfd
= open(device
, O_RDWR
|O_NDELAY
|O_NOCTTY
)) == -1) {
181 Debug("error opening modem device !");
182 fd
= open(DEVNULL
, O_RDONLY
);
183 response
.status
= -errno
;
184 sendFD(fd
, &response
);
187 sendFD(ttyfd
, &response
);
191 Debug("Opener: received OpenLock\n");
192 MY_ASSERT(len
== sizeof(struct OpenLockRequest
));
193 flags
= request
.lock
.flags
;
194 MY_ASSERT(flags
== O_RDONLY
|| flags
== O_WRONLY
|O_TRUNC
|O_CREAT
);
195 if(flags
== O_WRONLY
|O_TRUNC
|O_CREAT
)
200 device
= deviceByIndex(request
.lock
.deviceNum
);
201 MY_ASSERT(strlen(LOCK_DIR
)+strlen(device
) < MaxPathLen
);
202 strlcpy(lockfile
, LOCK_DIR
"/LCK..", MaxPathLen
);
203 strlcat(lockfile
, strrchr(device
, '/') + 1, MaxPathLen
);
207 // if(stat(lockfile.data(), &st) == -1) {
208 // if(errno == EBADF)
211 // // make sure that this is a regular file
212 // if(!S_ISREG(st.st_mode))
215 if ((fd
= open(lockfile
, flags
, mode
)) == -1) {
216 Debug("error opening lockfile!");
218 fd
= open(DEVNULL
, O_RDONLY
);
219 response
.status
= -errno
;
222 sendFD(fd
, &response
);
227 Debug("Opener: received RemoveLock");
228 MY_ASSERT(len
== sizeof(struct RemoveLockRequest
));
231 response
.status
= unlink(lockfile
);
233 sendResponse(&response
);
237 Debug("Opener: received OpenResolv");
238 MY_ASSERT(len
== sizeof(struct OpenResolvRequest
));
239 flags
= request
.resolv
.flags
;
241 if ((fd
= open(_PATH_RESCONF
, flags
)) == -1) {
242 Debug("error opening resolv.conf!");
243 fd
= open(DEVNULL
, O_RDONLY
);
244 response
.status
= -errno
;
246 sendFD(fd
, &response
);
251 Debug("Opener: received OpenSysLog");
252 MY_ASSERT(len
== sizeof(struct OpenLogRequest
));
254 logFile
= &kppp_syslog
[0];
256 if ((fd
= open(*logFile
, O_RDONLY
)) >= 0)
261 Debug("No success opening a syslog file !");
262 fd
= open(DEVNULL
, O_RDONLY
);
263 response
.status
= -errno
;
265 sendFD(fd
, &response
);
270 Debug("Opener: received SetSecret");
271 MY_ASSERT(len
== sizeof(struct SetSecretRequest
));
272 response
.status
= !createAuthFile(request
.secret
.method
,
273 request
.secret
.username
,
274 request
.secret
.password
);
275 sendResponse(&response
);
279 Debug("Opener: received RemoveSecret");
280 MY_ASSERT(len
== sizeof(struct RemoveSecretRequest
));
281 response
.status
= !removeAuthFile(request
.remove
.method
);
282 sendResponse(&response
);
286 Debug("Opener: received SetHostname");
287 MY_ASSERT(len
== sizeof(struct SetHostnameRequest
));
289 if(sethostname(request
.host
.name
, strlen(request
.host
.name
)))
290 response
.status
= -errno
;
291 sendResponse(&response
);
295 Debug("Opener: received ExecPPPDaemon");
296 MY_ASSERT(len
== sizeof(struct ExecDaemonRequest
));
297 response
.status
= execpppd(request
.daemon
.arguments
);
298 sendResponse(&response
);
302 Debug("Opener: received KillPPPDaemon");
303 MY_ASSERT(len
== sizeof(struct KillDaemonRequest
));
304 response
.status
= killpppd();
305 sendResponse(&response
);
309 Debug("Opener: received PPPDExitStatus");
310 MY_ASSERT(len
== sizeof(struct PPPDExitStatusRequest
));
311 response
.status
= pppdExitStatus
;
312 sendResponse(&response
);
316 Debug("Opener: received STOP command");
321 Debug("Opener: unknown command type. Exiting ...");
330 // Send an open fd over a UNIX socket pair
332 int Opener::sendFD(int fd
, struct ResponseHeader
*response
) {
334 struct { struct cmsghdr cmsg
; int fd
; } control
;
344 iov
.iov_base
= IOV_BASE_CAST response
;
345 iov
.iov_len
= sizeof(struct ResponseHeader
);
347 // Send a (duplicate of) the file descriptor
348 control
.cmsg
.cmsg_len
= sizeof(struct cmsghdr
) + sizeof(int);
349 control
.cmsg
.cmsg_level
= SOL_SOCKET
;
350 control
.cmsg
.cmsg_type
= MY_SCM_RIGHTS
;
352 msg
.msg_control
= (char *) &control
;
353 msg
.msg_controllen
= control
.cmsg
.cmsg_len
;
356 *((int *)CMSG_DATA(&control
.cmsg
)) = fd
;
358 *((int *) &control
.cmsg
.cmsg_data
) = fd
;
361 if (sendmsg(socket
, &msg
, 0) < 0) {
362 perror("unable to send file descriptors");
369 int Opener::sendResponse(struct ResponseHeader
*response
) {
378 msg
.msg_control
= 0L;
379 msg
.msg_controllen
= 0;
382 iov
.iov_base
= IOV_BASE_CAST response
;
383 iov
.iov_len
= sizeof(struct ResponseHeader
);
385 if (sendmsg(socket
, &msg
, 0) < 0) {
386 perror("unable to send response");
393 const char* Opener::deviceByIndex(int idx
) {
395 const char *device
= 0L;
397 for(int i
= 0; devices
[i
]; i
++)
404 bool Opener::createAuthFile(Auth method
, char *username
, char *password
) {
405 const char *authfile
, *oldName
, *newName
;
407 char regexp
[2*MaxStrLen
+30];
410 if(!(authfile
= authFile(method
)))
413 if(!(newName
= authFile(method
, New
)))
416 // look for username, "username" or 'username'
417 // if you modify this RE you have to adapt regexp's size above
418 snprintf(regexp
, sizeof(regexp
), "^[ \t]*%s[ \t]\\|^[ \t]*[\"\']%s[\"\']",
420 MY_ASSERT(regcomp(&preg
, regexp
, 0) == 0);
422 // copy to new file pap- or chap-secrets
423 int old_umask
= umask(0077);
424 FILE *fout
= fopen(newName
, "w");
427 FILE *fin
= fopen(authfile
, "r");
429 while(fgets(line
, sizeof(line
), fin
)) {
430 if(regexec(&preg
, line
, 0, 0L, 0) == 0)
437 // append user/pass pair
438 fprintf(fout
, "\"%s\"\t*\t\"%s\"\n", username
, password
);
445 // free memory allocated by regcomp
448 if(!(oldName
= authFile(method
, Old
)))
451 // delete old file if any
454 rename(authfile
, oldName
);
455 rename(newName
, authfile
);
461 bool Opener::removeAuthFile(Auth method
) {
462 const char *authfile
, *oldName
;
464 if(!(authfile
= authFile(method
)))
466 if(!(oldName
= authFile(method
, Old
)))
469 if(access(oldName
, F_OK
) == 0) {
471 return (rename(oldName
, authfile
) == 0);
477 const char* Opener::authFile(Auth method
, int version
) {
478 switch(method
|version
) {
480 return PAP_AUTH_FILE
;
483 return PAP_AUTH_FILE
".new";
486 return PAP_AUTH_FILE
".old";
489 return CHAP_AUTH_FILE
;
492 return CHAP_AUTH_FILE
".new";
495 return CHAP_AUTH_FILE
".old";
503 bool Opener::execpppd(const char *arguments
) {
504 char buf
[MAX_CMDLEN
];
513 switch(pppdPid
= fork())
516 fprintf(stderr
,"In parent: fork() failed\n");
521 // let's parse the arguments the user supplied into UNIX suitable form
522 // that is a list of pointers each pointing to exactly one word
523 strlcpy(buf
, arguments
, sizeof(buf
));
524 parseargs(buf
, args
);
525 // become a session leader and let /dev/ttySx
526 // be the controlling terminal.
529 if(ioctl(ttyfd
, TIOCSCTTY
, 0)<0)
530 fprintf(stderr
, "ioctl() failed.\n");
531 #elif defined (TIOCSPGRP)
532 if(ioctl(ttyfd
, TIOCSPGRP
, &pgrpid
)<0)
533 fprintf(stderr
, "ioctl() failed.\n");
535 if(tcsetpgrp(ttyfd
, pgrpid
)<0)
536 fprintf(stderr
, "tcsetpgrp() failed.\n");
541 switch (checkForInterface()) {
543 fprintf(stderr
, "Cannot determine if kernel supports ppp.\n");
546 fprintf(stderr
, "Kernel does not support ppp, oops.\n");
549 fprintf(stderr
, "Kernel supports ppp alright.\n");
553 execve(pppdPath(), args
, 0L);
558 Debug2("In parent: pppd pid %d\n",pppdPid
);
567 bool Opener::killpppd()const {
569 Debug2("In killpppd(): Sending SIGTERM to %d\n", pppdPid
);
570 if(kill(pppdPid
, SIGTERM
) < 0) {
571 Debug2("Error terminating %d. Sending SIGKILL\n", pppdPid
);
572 if(kill(pppdPid
, SIGKILL
) < 0) {
573 Debug2("Error killing %d\n", pppdPid
);
582 void Opener::parseargs(char* buf
, char** args
) {
586 while(nargs
< MaxArgs
-1 && *buf
!= '\0') {
590 // Strip whitespace. Use nulls, so that the previous argument is
591 // terminated automatically.
593 while ((*buf
== ' ' ) || (*buf
== '\t' ) || (*buf
== '\n' ) )
596 // detect begin of quoted argument
597 if (*buf
== '"' || *buf
== '\'') {
609 while ((*buf
!= '\0') && (*buf
!= '\n') &&
610 (*buf
!= '\t') && (*buf
!= ' '))
613 while ((*buf
!= '\0') && (*buf
!= quotes
))
623 const char* pppdPath() {
624 // wasting a few bytes
625 static char buffer
[sizeof(PPPDSEARCHPATH
)+sizeof(PPPDNAME
)];
626 static char *pppdPath
= 0L;
630 const char *c
= PPPDSEARCHPATH
;
635 while(*c
!= '\0' && *c
!= ':')
640 if(access(buffer
, F_OK
) == 0)
641 return (pppdPath
= buffer
);
648 int checkForInterface()
650 // I don't know if Linux needs more initialization to get the ioctl to
651 // work, pppd seems to hint it does. But BSD doesn't, and the following
652 // code should compile.
653 #if (defined(HAVE_NET_IF_PPP_H) || defined(HAVE_LINUX_IF_PPP_H)) && !defined(__svr4__)
656 // extern char *no_ppp_msg;
658 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
659 return 1; /* can't tell */
661 strlcpy(ifr
.ifr_name
, "ppp0", sizeof (ifr
.ifr_name
));
662 ok
= ioctl(s
, SIOCGIFFLAGS
, (caddr_t
) &ifr
) >= 0;
666 // This is ifdef'd FreeBSD, because FreeBSD is the only BSD that supports
667 // KLDs, the old LKM interface couldn't handle loading devices
668 // dynamically, and thus can't load ppp support on the fly
670 // If we failed to load ppp support and don't have it already.
671 if (kldload("if_ppp") == -1) {
681 // We attempt to use the SunOS/SysVr4 method and stat /dev/ppp
684 memset(&buf
, 0, sizeof(buf
));
685 return stat("/dev/ppp", &buf
);
690 void sighandler_child(int) {
694 signal(SIGCHLD
, sighandler_child
);
696 pid
= waitpid(pppdPid
, &status
, WNOHANG
);
698 fprintf(stderr
, "received SIGCHLD from unknown origin.\n");
700 Debug("It was pppd that died");
702 if((WIFEXITED(status
))) {
703 pppdExitStatus
= (WEXITSTATUS(status
));
704 Debug2("pppd exited with return value %d\n", pppdExitStatus
);
707 Debug("pppd exited abnormally.");
709 Debug2("Sending %i a SIGUSR1\n", getppid());
710 kill(getppid(), SIGUSR1
);
713 fprintf(stderr
, "received unexpected SIGCHLD.\n");