Contact cannot be a null ptr, and it is accessed before anyway. Disscussed with kedge.
[kdenetwork.git] / kppp / opener.cpp
blobdfb1154a156ecca9014773d1f01758e097bd1e61
2 /*
3 * kPPP: A pppd Front End for the KDE project
5 * $Id$
7 * Copyright (C) 1997,98 Bernd Johannes Wuebben,
8 * Mario Weilguni
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.
36 #include <kdefakes.h>
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);
42 #if !defined(__osf__)
43 extern "C" int _Psendmsg(int, void*, int);
44 extern "C" int _Precvmsg(int, void*, int);
45 #endif
46 #endif
48 #include "kpppconfig.h"
50 #include <sys/types.h>
51 #include <sys/uio.h>
52 #include <sys/stat.h>
53 #include <sys/socket.h>
54 #include <sys/ioctl.h>
55 #include <sys/un.h>
56 #include <sys/wait.h>
57 #include <sys/param.h>
60 #include <netinet/in.h>
62 #ifdef __FreeBSD__
63 # include <sys/linker.h> // for kldload
64 #endif
66 #ifndef HAVE_NET_IF_PPP_H
67 # if defined(__DragonFly__)
68 # include <net/ppp_layer/ppp_defs.h>
69 # include <net/if.h>
70 # include <net/ppp/if_ppp.h>
71 # elif defined HAVE_LINUX_IF_PPP_H
72 # include <linux/if_ppp.h>
73 # endif
74 #else
75 # include <net/ppp_defs.h>
76 # include <net/if.h>
77 # include <net/if_ppp.h>
78 #endif
80 #include <errno.h>
81 #include <fcntl.h>
82 #include <regex.h>
83 #include <signal.h>
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <unistd.h>
88 #include <termios.h>
90 #include "opener.h"
91 #include "devices.h"
93 #ifdef HAVE_RESOLV_H
94 # include <arpa/nameser.h>
95 # include <resolv.h>
96 #endif
98 #ifndef _PATH_RESCONF
99 #define _PATH_RESCONF "/etc/resolv.conf"
100 #endif
102 #ifdef _XPG4_2
103 extern "C" {
104 ssize_t recvmsg(int, struct msghdr *, int);
105 ssize_t sendmsg(int, const struct msghdr *, int);
107 #endif
109 #define MY_ASSERT(x) if (!(x)) { \
110 fprintf(stderr, "ASSERT: \"%s\" in %s (%d)\n",#x,__FILE__,__LINE__); \
111 exit(1); }
113 #define MY_DEBUG
114 #ifndef MY_DEBUG
115 #define Debug(s) ((void)0);
116 #define Debug2(s, i) ((void)0);
117 #else
118 #define Debug(s) fprintf(stderr, (s "\n"));
119 #define Debug2(s, i) fprintf(stderr, (s), (i));
120 #endif
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",
129 "/var/log/syslog",
130 "/var/log/messages",
131 0 };
133 Opener::Opener(int s) : socket(s), ttyfd(-1) {
134 lockfile[0] = '\0';
135 signal(SIGUSR1, SIG_IGN);
136 signal(SIGTERM, SIG_IGN);
137 signal(SIGINT, SIG_IGN);
138 signal(SIGCHLD, sighandler_child);
139 mainLoop();
142 void Opener::mainLoop() {
144 int len;
145 int fd = -1;
146 int flags, mode;
147 const char *device, * const *logFile;
148 union AllRequests request;
149 struct ResponseHeader response;
150 struct msghdr msg;
151 struct iovec iov;
153 iov.iov_base = IOV_BASE_CAST &request;
154 iov.iov_len = sizeof(request);
156 msg.msg_name = 0L;
157 msg.msg_namelen = 0;
158 msg.msg_iov = &iov;
159 msg.msg_iovlen = 1;
160 msg.msg_control = 0L;
161 msg.msg_controllen = 0;
163 // loop forever
164 while(1) {
165 len = recvmsg(socket, &msg, 0);
166 if(len < 0) {
167 switch(errno) {
168 case EINTR:
169 Debug("Opener: interrupted system call, continuing");
170 break;
171 default:
172 perror("Opener: error reading from socket");
173 _exit(1);
175 } else {
176 switch(request.header.type) {
178 case OpenDevice:
179 Debug("Opener: received OpenDevice");
180 MY_ASSERT(len == sizeof(struct OpenModemRequest));
181 close(ttyfd);
182 device = deviceByIndex(request.modem.deviceNum);
183 response.status = 0;
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);
189 close(fd);
190 } else
191 sendFD(ttyfd, &response);
192 break;
194 case OpenLock:
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))
200 mode = 0644;
201 else
202 mode = 0;
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 );
208 response.status = 0;
209 // TODO:
210 // struct stat st;
211 // if(stat(lockfile.data(), &st) == -1) {
212 // if(errno == EBADF)
213 // return -1;
214 // } else {
215 // // make sure that this is a regular file
216 // if(!S_ISREG(st.st_mode))
217 // return -1;
218 // }
219 if ((fd = open(lockfile, flags, mode)) == -1) {
220 Debug("error opening lockfile!");
221 lockfile[0] = '\0';
222 fd = open(DEVNULL, O_RDONLY);
223 response.status = -errno;
224 } else
225 fchown(fd, 0, 0);
226 sendFD(fd, &response);
227 close(fd);
228 break;
230 case RemoveLock:
231 Debug("Opener: received RemoveLock");
232 MY_ASSERT(len == sizeof(struct RemoveLockRequest));
233 close(ttyfd);
234 ttyfd = -1;
235 response.status = unlink(lockfile);
236 lockfile[0] = '\0';
237 sendResponse(&response);
238 break;
240 case OpenResolv:
241 Debug("Opener: received OpenResolv");
242 MY_ASSERT(len == sizeof(struct OpenResolvRequest));
243 flags = request.resolv.flags;
244 response.status = 0;
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);
251 close(fd);
252 break;
254 case OpenSysLog:
255 Debug("Opener: received OpenSysLog");
256 MY_ASSERT(len == sizeof(struct OpenLogRequest));
257 response.status = 0;
258 logFile = &kppp_syslog[0];
259 while (*logFile) {
260 if ((fd = open(*logFile, O_RDONLY)) >= 0)
261 break;
262 logFile++;
264 if (!*logFile) {
265 Debug("No success opening a syslog file !");
266 fd = open(DEVNULL, O_RDONLY);
267 response.status = -errno;
269 sendFD(fd, &response);
270 close(fd);
271 break;
273 case SetSecret:
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);
280 break;
282 case RemoveSecret:
283 Debug("Opener: received RemoveSecret");
284 MY_ASSERT(len == sizeof(struct RemoveSecretRequest));
285 response.status = !removeAuthFile(request.remove.method);
286 sendResponse(&response);
287 break;
289 case SetHostname:
290 Debug("Opener: received SetHostname");
291 MY_ASSERT(len == sizeof(struct SetHostnameRequest));
292 response.status = 0;
293 if(sethostname(request.host.name, strlen(request.host.name)))
294 response.status = -errno;
295 sendResponse(&response);
296 break;
298 case ExecPPPDaemon:
299 Debug("Opener: received ExecPPPDaemon");
300 MY_ASSERT(len == sizeof(struct ExecDaemonRequest));
301 response.status = execpppd(request.daemon.arguments);
302 sendResponse(&response);
303 break;
305 case KillPPPDaemon:
306 Debug("Opener: received KillPPPDaemon");
307 MY_ASSERT(len == sizeof(struct KillDaemonRequest));
308 response.status = killpppd();
309 sendResponse(&response);
310 break;
312 case PPPDExitStatus:
313 Debug("Opener: received PPPDExitStatus");
314 MY_ASSERT(len == sizeof(struct PPPDExitStatusRequest));
315 response.status = pppdExitStatus;
316 sendResponse(&response);
317 break;
319 case Stop:
320 Debug("Opener: received STOP command");
321 _exit(0);
322 break;
324 default:
325 Debug("Opener: unknown command type. Exiting ...");
326 _exit(1);
328 } // else
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;
339 struct msghdr msg;
340 struct iovec iov;
342 msg.msg_name = 0L;
343 msg.msg_namelen = 0;
344 msg.msg_iov = &iov;
345 msg.msg_iovlen = 1;
347 // Send data
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;
359 #ifdef CMSG_DATA
360 *((int *)CMSG_DATA(&control.cmsg)) = fd;
361 #else
362 *((int *) &control.cmsg.cmsg_data) = fd;
363 #endif
365 if (sendmsg(socket, &msg, 0) < 0) {
366 perror("unable to send file descriptors");
367 return -1;
370 return 0;
373 int Opener::sendResponse(struct ResponseHeader *response) {
375 struct msghdr msg;
376 struct iovec iov;
378 msg.msg_name = 0L;
379 msg.msg_namelen = 0;
380 msg.msg_iov = &iov;
381 msg.msg_iovlen = 1;
382 msg.msg_control = 0L;
383 msg.msg_controllen = 0;
385 // Send data
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");
391 return -1;
394 return 0;
397 const char* Opener::deviceByIndex(int idx) {
399 const char *device = 0L;
401 for(int i = 0; devices[i]; i++)
402 if(i == idx)
403 device = devices[i];
404 MY_ASSERT(device);
405 return device;
408 bool Opener::createAuthFile(Auth method, char *username, char *password) {
409 const char *authfile, *oldName, *newName;
410 char line[100];
411 char regexp[2*MaxStrLen+30];
412 regex_t preg;
414 if(!(authfile = authFile(method)))
415 return false;
417 if(!(newName = authFile(method, New)))
418 return false;
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[\"\']",
423 username,username);
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");
429 if(fout) {
430 // copy old file
431 FILE *fin = fopen(authfile, "r");
432 if(fin) {
433 while(fgets(line, sizeof(line), fin)) {
434 if(regexec(&preg, line, 0, 0L, 0) == 0)
435 continue;
436 fputs(line, fout);
438 fclose(fin);
441 // append user/pass pair
442 fprintf(fout, "\"%s\"\t*\t\"%s\"\n", username, password);
443 fclose(fout);
446 // restore umask
447 umask(old_umask);
449 // free memory allocated by regcomp
450 regfree(&preg);
452 if(!(oldName = authFile(method, Old)))
453 return false;
455 // delete old file if any
456 unlink(oldName);
458 rename(authfile, oldName);
459 rename(newName, authfile);
461 return true;
465 bool Opener::removeAuthFile(Auth method) {
466 const char *authfile, *oldName;
468 if(!(authfile = authFile(method)))
469 return false;
470 if(!(oldName = authFile(method, Old)))
471 return false;
473 if(access(oldName, F_OK) == 0) {
474 unlink(authfile);
475 return (rename(oldName, authfile) == 0);
476 } else
477 return false;
481 const char* Opener::authFile(Auth method, int version) {
482 switch(method|version) {
483 case PAP|Original:
484 return PAP_AUTH_FILE;
485 break;
486 case PAP|New:
487 return PAP_AUTH_FILE".new";
488 break;
489 case PAP|Old:
490 return PAP_AUTH_FILE".old";
491 break;
492 case CHAP|Original:
493 return CHAP_AUTH_FILE;
494 break;
495 case CHAP|New:
496 return CHAP_AUTH_FILE".new";
497 break;
498 case CHAP|Old:
499 return CHAP_AUTH_FILE".old";
500 break;
501 default:
502 return 0L;
507 bool Opener::execpppd(const char *arguments) {
508 char buf[MAX_CMDLEN];
509 char *args[MaxArgs];
510 pid_t pgrpid;
512 if(ttyfd<0)
513 return false;
515 pppdExitStatus = -1;
517 switch(pppdPid = fork())
519 case -1:
520 fprintf(stderr,"In parent: fork() failed\n");
521 return false;
522 break;
524 case 0:
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.
531 pgrpid = setsid();
532 #ifdef TIOCSCTTY
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");
538 #endif
539 if(tcsetpgrp(ttyfd, pgrpid)<0)
540 fprintf(stderr, "tcsetpgrp() failed.\n");
542 dup2(ttyfd, 0);
543 dup2(ttyfd, 1);
545 switch (checkForInterface()) {
546 case 1:
547 fprintf(stderr, "Cannot determine if kernel supports ppp.\n");
548 break;
549 case -1:
550 fprintf(stderr, "Kernel does not support ppp, oops.\n");
551 break;
552 case 0:
553 fprintf(stderr, "Kernel supports ppp alright.\n");
554 break;
557 execve(pppdPath(), args, 0L);
558 _exit(0);
559 break;
561 default:
562 Debug2("In parent: pppd pid %d\n",pppdPid);
563 close(ttyfd);
564 ttyfd = -1;
565 return true;
566 break;
571 bool Opener::killpppd()const {
572 if(pppdPid > 0) {
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);
578 return false;
582 return true;
586 void Opener::parseargs(char* buf, char** args) {
587 int nargs = 0;
588 int quotes;
590 while(nargs < MaxArgs-1 && *buf != '\0') {
592 quotes = 0;
594 // Strip whitespace. Use nulls, so that the previous argument is
595 // terminated automatically.
597 while ((*buf == ' ' ) || (*buf == '\t' ) || (*buf == '\n' ) )
598 *buf++ = '\0';
600 // detect begin of quoted argument
601 if (*buf == '"' || *buf == '\'') {
602 quotes = *buf;
603 *buf++ = '\0';
606 // save the argument
607 if(*buf != '\0') {
608 *args++ = buf;
609 nargs++;
612 if (!quotes)
613 while ((*buf != '\0') && (*buf != '\n') &&
614 (*buf != '\t') && (*buf != ' '))
615 buf++;
616 else {
617 while ((*buf != '\0') && (*buf != quotes))
618 buf++;
619 *buf++ = '\0';
623 *args = 0L;
627 const char* pppdPath() {
628 // wasting a few bytes
629 static char buffer[sizeof(PPPDSEARCHPATH)+sizeof(PPPDNAME)];
630 static char *pppdPath = 0L;
631 char *p;
633 if(pppdPath == 0L) {
634 const char *c = PPPDSEARCHPATH;
635 while(*c != '\0') {
636 while(*c == ':')
637 c++;
638 p = buffer;
639 while(*c != '\0' && *c != ':')
640 *p++ = *c++;
641 *p = '\0';
642 strcat(p, "/");
643 strcat(p, PPPDNAME);
644 if(access(buffer, F_OK) == 0)
645 return (pppdPath = buffer);
649 return pppdPath;
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__)
658 int s, ok;
659 struct ifreq ifr;
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;
667 close(s);
669 if (ok == -1) {
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
673 #ifdef __FreeBSD__
674 // If we failed to load ppp support and don't have it already.
675 if (kldload("if_ppp") == -1) {
676 return -1;
678 return 0;
679 #else
680 return -1;
681 #endif
683 return 0;
684 #else
685 // We attempt to use the SunOS/SysVr4 method and stat /dev/ppp
686 struct stat buf;
688 memset(&buf, 0, sizeof(buf));
689 return stat("/dev/ppp", &buf);
690 #endif
694 void sighandler_child(int) {
695 pid_t pid;
696 int status;
698 signal(SIGCHLD, sighandler_child);
699 if(pppdPid>0) {
700 pid = waitpid(pppdPid, &status, WNOHANG);
701 if(pid != pppdPid) {
702 fprintf(stderr, "received SIGCHLD from unknown origin.\n");
703 } else {
704 Debug("It was pppd that died");
705 pppdPid = -1;
706 if((WIFEXITED(status))) {
707 pppdExitStatus = (WEXITSTATUS(status));
708 Debug2("pppd exited with return value %d\n", pppdExitStatus);
709 } else {
710 pppdExitStatus = 99;
711 Debug("pppd exited abnormally.");
713 Debug2("Sending %i a SIGUSR1\n", getppid());
714 kill(getppid(), SIGUSR1);
716 } else
717 fprintf(stderr, "received unexpected SIGCHLD.\n");