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 # if defined(__DragonFly__)
68 # include <net/ppp_layer/ppp_defs.h>
70 # include <net/ppp/if_ppp.h>
71 # elif defined HAVE_LINUX_IF_PPP_H
72 # include <linux/if_ppp.h>
75 # include <net/ppp_defs.h>
77 # include <net/if_ppp.h>
94 # include <arpa/nameser.h>
99 #define _PATH_RESCONF "/etc/resolv.conf"
104 ssize_t
recvmsg(int, struct msghdr
*, int);
105 ssize_t
sendmsg(int, const struct msghdr
*, int);
109 #define MY_ASSERT(x) if (!(x)) { \
110 fprintf(stderr, "ASSERT: \"%s\" in %s (%d)\n",#x,__FILE__,__LINE__); \
115 #define Debug(s) ((void)0);
116 #define Debug2(s, i) ((void)0);
118 #define Debug(s) fprintf(stderr, (s "\n"));
119 #define Debug2(s, i) fprintf(stderr, (s), (i));
122 static void sighandler_child(int);
123 static pid_t pppdPid
= -1;
124 static int pppdExitStatus
= -1;
125 static int checkForInterface();
127 // processing will stop at first file that could be opened successfully
128 const char * const kppp_syslog
[] = { "/var/log/syslog.ppp",
133 Opener::Opener(int s
) : socket(s
), ttyfd(-1) {
135 signal(SIGUSR1
, SIG_IGN
);
136 signal(SIGTERM
, SIG_IGN
);
137 signal(SIGINT
, SIG_IGN
);
138 signal(SIGCHLD
, sighandler_child
);
142 void Opener::mainLoop() {
147 const char *device
, * const *logFile
;
148 union AllRequests request
;
149 struct ResponseHeader response
;
153 iov
.iov_base
= IOV_BASE_CAST
&request
;
154 iov
.iov_len
= sizeof(request
);
160 msg
.msg_control
= 0L;
161 msg
.msg_controllen
= 0;
165 len
= recvmsg(socket
, &msg
, 0);
169 Debug("Opener: interrupted system call, continuing");
172 perror("Opener: error reading from socket");
176 switch(request
.header
.type
) {
179 Debug("Opener: received OpenDevice");
180 MY_ASSERT(len
== sizeof(struct OpenModemRequest
));
182 device
= deviceByIndex(request
.modem
.deviceNum
);
184 if ((ttyfd
= open(device
, O_RDWR
|O_NDELAY
|O_NOCTTY
)) == -1) {
185 Debug("error opening modem device !");
186 fd
= open(DEVNULL
, O_RDONLY
);
187 response
.status
= -errno
;
188 sendFD(fd
, &response
);
191 sendFD(ttyfd
, &response
);
195 Debug("Opener: received OpenLock\n");
196 MY_ASSERT(len
== sizeof(struct OpenLockRequest
));
197 flags
= request
.lock
.flags
;
198 MY_ASSERT(flags
== O_RDONLY
|| flags
== (O_WRONLY
|O_TRUNC
|O_CREAT
));
199 if(flags
== (O_WRONLY
|O_TRUNC
|O_CREAT
))
204 device
= deviceByIndex(request
.lock
.deviceNum
);
205 MY_ASSERT(strlen(LOCK_DIR
)+strlen(device
) < MaxPathLen
);
206 strlcpy(lockfile
, LOCK_DIR
"/LCK..", MaxPathLen
);
207 strlcat(lockfile
, strrchr(device
, '/') + 1, MaxPathLen
);
211 // if(stat(lockfile.data(), &st) == -1) {
212 // if(errno == EBADF)
215 // // make sure that this is a regular file
216 // if(!S_ISREG(st.st_mode))
219 if ((fd
= open(lockfile
, flags
, mode
)) == -1) {
220 Debug("error opening lockfile!");
222 fd
= open(DEVNULL
, O_RDONLY
);
223 response
.status
= -errno
;
226 sendFD(fd
, &response
);
231 Debug("Opener: received RemoveLock");
232 MY_ASSERT(len
== sizeof(struct RemoveLockRequest
));
235 response
.status
= unlink(lockfile
);
237 sendResponse(&response
);
241 Debug("Opener: received OpenResolv");
242 MY_ASSERT(len
== sizeof(struct OpenResolvRequest
));
243 flags
= request
.resolv
.flags
;
245 if ((fd
= open(_PATH_RESCONF
, flags
)) == -1) {
246 Debug("error opening resolv.conf!");
247 fd
= open(DEVNULL
, O_RDONLY
);
248 response
.status
= -errno
;
250 sendFD(fd
, &response
);
255 Debug("Opener: received OpenSysLog");
256 MY_ASSERT(len
== sizeof(struct OpenLogRequest
));
258 logFile
= &kppp_syslog
[0];
260 if ((fd
= open(*logFile
, O_RDONLY
)) >= 0)
265 Debug("No success opening a syslog file !");
266 fd
= open(DEVNULL
, O_RDONLY
);
267 response
.status
= -errno
;
269 sendFD(fd
, &response
);
274 Debug("Opener: received SetSecret");
275 MY_ASSERT(len
== sizeof(struct SetSecretRequest
));
276 response
.status
= !createAuthFile(request
.secret
.method
,
277 request
.secret
.username
,
278 request
.secret
.password
);
279 sendResponse(&response
);
283 Debug("Opener: received RemoveSecret");
284 MY_ASSERT(len
== sizeof(struct RemoveSecretRequest
));
285 response
.status
= !removeAuthFile(request
.remove
.method
);
286 sendResponse(&response
);
290 Debug("Opener: received SetHostname");
291 MY_ASSERT(len
== sizeof(struct SetHostnameRequest
));
293 if(sethostname(request
.host
.name
, strlen(request
.host
.name
)))
294 response
.status
= -errno
;
295 sendResponse(&response
);
299 Debug("Opener: received ExecPPPDaemon");
300 MY_ASSERT(len
== sizeof(struct ExecDaemonRequest
));
301 response
.status
= execpppd(request
.daemon
.arguments
);
302 sendResponse(&response
);
306 Debug("Opener: received KillPPPDaemon");
307 MY_ASSERT(len
== sizeof(struct KillDaemonRequest
));
308 response
.status
= killpppd();
309 sendResponse(&response
);
313 Debug("Opener: received PPPDExitStatus");
314 MY_ASSERT(len
== sizeof(struct PPPDExitStatusRequest
));
315 response
.status
= pppdExitStatus
;
316 sendResponse(&response
);
320 Debug("Opener: received STOP command");
325 Debug("Opener: unknown command type. Exiting ...");
334 // Send an open fd over a UNIX socket pair
336 int Opener::sendFD(int fd
, struct ResponseHeader
*response
) {
338 struct { struct cmsghdr cmsg
; int fd
; } control
;
348 iov
.iov_base
= IOV_BASE_CAST response
;
349 iov
.iov_len
= sizeof(struct ResponseHeader
);
351 // Send a (duplicate of) the file descriptor
352 control
.cmsg
.cmsg_len
= sizeof(struct cmsghdr
) + sizeof(int);
353 control
.cmsg
.cmsg_level
= SOL_SOCKET
;
354 control
.cmsg
.cmsg_type
= MY_SCM_RIGHTS
;
356 msg
.msg_control
= (char *) &control
;
357 msg
.msg_controllen
= control
.cmsg
.cmsg_len
;
360 *((int *)CMSG_DATA(&control
.cmsg
)) = fd
;
362 *((int *) &control
.cmsg
.cmsg_data
) = fd
;
365 if (sendmsg(socket
, &msg
, 0) < 0) {
366 perror("unable to send file descriptors");
373 int Opener::sendResponse(struct ResponseHeader
*response
) {
382 msg
.msg_control
= 0L;
383 msg
.msg_controllen
= 0;
386 iov
.iov_base
= IOV_BASE_CAST response
;
387 iov
.iov_len
= sizeof(struct ResponseHeader
);
389 if (sendmsg(socket
, &msg
, 0) < 0) {
390 perror("unable to send response");
397 const char* Opener::deviceByIndex(int idx
) {
399 const char *device
= 0L;
401 for(int i
= 0; devices
[i
]; i
++)
408 bool Opener::createAuthFile(Auth method
, char *username
, char *password
) {
409 const char *authfile
, *oldName
, *newName
;
411 char regexp
[2*MaxStrLen
+30];
414 if(!(authfile
= authFile(method
)))
417 if(!(newName
= authFile(method
, New
)))
420 // look for username, "username" or 'username'
421 // if you modify this RE you have to adapt regexp's size above
422 snprintf(regexp
, sizeof(regexp
), "^[ \t]*%s[ \t]\\|^[ \t]*[\"\']%s[\"\']",
424 MY_ASSERT(regcomp(&preg
, regexp
, 0) == 0);
426 // copy to new file pap- or chap-secrets
427 int old_umask
= umask(0077);
428 FILE *fout
= fopen(newName
, "w");
431 FILE *fin
= fopen(authfile
, "r");
433 while(fgets(line
, sizeof(line
), fin
)) {
434 if(regexec(&preg
, line
, 0, 0L, 0) == 0)
441 // append user/pass pair
442 fprintf(fout
, "\"%s\"\t*\t\"%s\"\n", username
, password
);
449 // free memory allocated by regcomp
452 if(!(oldName
= authFile(method
, Old
)))
455 // delete old file if any
458 rename(authfile
, oldName
);
459 rename(newName
, authfile
);
465 bool Opener::removeAuthFile(Auth method
) {
466 const char *authfile
, *oldName
;
468 if(!(authfile
= authFile(method
)))
470 if(!(oldName
= authFile(method
, Old
)))
473 if(access(oldName
, F_OK
) == 0) {
475 return (rename(oldName
, authfile
) == 0);
481 const char* Opener::authFile(Auth method
, int version
) {
482 switch(method
|version
) {
484 return PAP_AUTH_FILE
;
487 return PAP_AUTH_FILE
".new";
490 return PAP_AUTH_FILE
".old";
493 return CHAP_AUTH_FILE
;
496 return CHAP_AUTH_FILE
".new";
499 return CHAP_AUTH_FILE
".old";
507 bool Opener::execpppd(const char *arguments
) {
508 char buf
[MAX_CMDLEN
];
517 switch(pppdPid
= fork())
520 fprintf(stderr
,"In parent: fork() failed\n");
525 // let's parse the arguments the user supplied into UNIX suitable form
526 // that is a list of pointers each pointing to exactly one word
527 strlcpy(buf
, arguments
, sizeof(buf
));
528 parseargs(buf
, args
);
529 // become a session leader and let /dev/ttySx
530 // be the controlling terminal.
533 if(ioctl(ttyfd
, TIOCSCTTY
, 0)<0)
534 fprintf(stderr
, "ioctl() failed.\n");
535 #elif defined (TIOCSPGRP)
536 if(ioctl(ttyfd
, TIOCSPGRP
, &pgrpid
)<0)
537 fprintf(stderr
, "ioctl() failed.\n");
539 if(tcsetpgrp(ttyfd
, pgrpid
)<0)
540 fprintf(stderr
, "tcsetpgrp() failed.\n");
545 switch (checkForInterface()) {
547 fprintf(stderr
, "Cannot determine if kernel supports ppp.\n");
550 fprintf(stderr
, "Kernel does not support ppp, oops.\n");
553 fprintf(stderr
, "Kernel supports ppp alright.\n");
557 execve(pppdPath(), args
, 0L);
562 Debug2("In parent: pppd pid %d\n",pppdPid
);
571 bool Opener::killpppd()const {
573 Debug2("In killpppd(): Sending SIGTERM to %d\n", pppdPid
);
574 if(kill(pppdPid
, SIGTERM
) < 0) {
575 Debug2("Error terminating %d. Sending SIGKILL\n", pppdPid
);
576 if(kill(pppdPid
, SIGKILL
) < 0) {
577 Debug2("Error killing %d\n", pppdPid
);
586 void Opener::parseargs(char* buf
, char** args
) {
590 while(nargs
< MaxArgs
-1 && *buf
!= '\0') {
594 // Strip whitespace. Use nulls, so that the previous argument is
595 // terminated automatically.
597 while ((*buf
== ' ' ) || (*buf
== '\t' ) || (*buf
== '\n' ) )
600 // detect begin of quoted argument
601 if (*buf
== '"' || *buf
== '\'') {
613 while ((*buf
!= '\0') && (*buf
!= '\n') &&
614 (*buf
!= '\t') && (*buf
!= ' '))
617 while ((*buf
!= '\0') && (*buf
!= quotes
))
627 const char* pppdPath() {
628 // wasting a few bytes
629 static char buffer
[sizeof(PPPDSEARCHPATH
)+sizeof(PPPDNAME
)];
630 static char *pppdPath
= 0L;
634 const char *c
= PPPDSEARCHPATH
;
639 while(*c
!= '\0' && *c
!= ':')
644 if(access(buffer
, F_OK
) == 0)
645 return (pppdPath
= buffer
);
652 int checkForInterface()
654 // I don't know if Linux needs more initialization to get the ioctl to
655 // work, pppd seems to hint it does. But BSD doesn't, and the following
656 // code should compile.
657 #if (defined(HAVE_NET_IF_PPP_H) || defined(HAVE_LINUX_IF_PPP_H)) && !defined(__svr4__)
660 // extern char *no_ppp_msg;
662 if ((s
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
663 return 1; /* can't tell */
665 strlcpy(ifr
.ifr_name
, "ppp0", sizeof (ifr
.ifr_name
));
666 ok
= ioctl(s
, SIOCGIFFLAGS
, (caddr_t
) &ifr
) >= 0;
670 // This is ifdef'd FreeBSD, because FreeBSD is the only BSD that supports
671 // KLDs, the old LKM interface couldn't handle loading devices
672 // dynamically, and thus can't load ppp support on the fly
674 // If we failed to load ppp support and don't have it already.
675 if (kldload("if_ppp") == -1) {
685 // We attempt to use the SunOS/SysVr4 method and stat /dev/ppp
688 memset(&buf
, 0, sizeof(buf
));
689 return stat("/dev/ppp", &buf
);
694 void sighandler_child(int) {
698 signal(SIGCHLD
, sighandler_child
);
700 pid
= waitpid(pppdPid
, &status
, WNOHANG
);
702 fprintf(stderr
, "received SIGCHLD from unknown origin.\n");
704 Debug("It was pppd that died");
706 if((WIFEXITED(status
))) {
707 pppdExitStatus
= (WEXITSTATUS(status
));
708 Debug2("pppd exited with return value %d\n", pppdExitStatus
);
711 Debug("pppd exited abnormally.");
713 Debug2("Sending %i a SIGUSR1\n", getppid());
714 kill(getppid(), SIGUSR1
);
717 fprintf(stderr
, "received unexpected SIGCHLD.\n");