summaryrefslogtreecommitdiff
path: root/jni/src/common.c (plain)
blob: b8c53fdf74c458117be92ef337d1ddb66f129e43
1/***********************************************************************
2*
3* common.c
4*
5* Implementation of user-space PPPoE redirector for Linux.
6*
7* Common functions used by PPPoE client and server
8*
9* Copyright (C) 2000 by Roaring Penguin Software Inc.
10*
11* This program may be distributed according to the terms of the GNU
12* General Public License, version 2 or (at your option) any later version.
13*
14* LIC: GPL
15*
16***********************************************************************/
17
18static char const RCSID[] =
19"$Id$";
20/* For vsnprintf prototype */
21#define _ISOC99_SOURCE 1
22
23/* For seteuid prototype */
24#define _BSD_SOURCE 1
25
26#include "pppoe.h"
27
28
29#ifdef HAVE_SYSLOG_H
30#include <syslog.h>
31#include <android/log.h>
32#define syslog(prio, fmt...) \
33 __android_log_print(prio, "PPPOE", fmt)
34#endif
35
36#include <string.h>
37#include <errno.h>
38#include <stdlib.h>
39#include <stdarg.h>
40
41#ifdef HAVE_UNISTD_H
42#include <unistd.h>
43#endif
44
45#include <sys/types.h>
46#include <pwd.h>
47
48/* Are we running SUID or SGID? */
49int IsSetID = 0;
50
51static uid_t saved_uid = -2;
52static uid_t saved_gid = -2;
53
54/**********************************************************************
55*%FUNCTION: parsePacket
56*%ARGUMENTS:
57* packet -- the PPPoE discovery packet to parse
58* func -- function called for each tag in the packet
59* extra -- an opaque data pointer supplied to parsing function
60*%RETURNS:
61* 0 if everything went well; -1 if there was an error
62*%DESCRIPTION:
63* Parses a PPPoE discovery packet, calling "func" for each tag in the packet.
64* "func" is passed the additional argument "extra".
65***********************************************************************/
66int
67parsePacket(PPPoEPacket *packet, ParseFunc *func, void *extra)
68{
69 UINT16_t len = ntohs(packet->length);
70 unsigned char *curTag;
71 UINT16_t tagType, tagLen;
72
73 if (packet->ver != 1) {
74 syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver);
75 return -1;
76 }
77 if (packet->type != 1) {
78 syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type);
79 return -1;
80 }
81
82 /* Do some sanity checks on packet */
83 if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */
84 syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len);
85 return -1;
86 }
87
88 /* Step through the tags */
89 curTag = packet->payload;
90 while(curTag - packet->payload < len) {
91 /* Alignment is not guaranteed, so do this by hand... */
92 tagType = (((UINT16_t) curTag[0]) << 8) +
93 (UINT16_t) curTag[1];
94 tagLen = (((UINT16_t) curTag[2]) << 8) +
95 (UINT16_t) curTag[3];
96 if (tagType == TAG_END_OF_LIST) {
97 return 0;
98 }
99 if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) {
100 syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen);
101 return -1;
102 }
103 func(tagType, tagLen, curTag+TAG_HDR_SIZE, extra);
104 curTag = curTag + TAG_HDR_SIZE + tagLen;
105 }
106 return 0;
107}
108
109/**********************************************************************
110*%FUNCTION: findTag
111*%ARGUMENTS:
112* packet -- the PPPoE discovery packet to parse
113* type -- the type of the tag to look for
114* tag -- will be filled in with tag contents
115*%RETURNS:
116* A pointer to the tag if one of the specified type is found; NULL
117* otherwise.
118*%DESCRIPTION:
119* Looks for a specific tag type.
120***********************************************************************/
121unsigned char *
122findTag(PPPoEPacket *packet, UINT16_t type, PPPoETag *tag)
123{
124 UINT16_t len = ntohs(packet->length);
125 unsigned char *curTag;
126 UINT16_t tagType, tagLen;
127
128 if (packet->ver != 1) {
129 syslog(LOG_ERR, "Invalid PPPoE version (%d)", (int) packet->ver);
130 return NULL;
131 }
132 if (packet->type != 1) {
133 syslog(LOG_ERR, "Invalid PPPoE type (%d)", (int) packet->type);
134 return NULL;
135 }
136
137 /* Do some sanity checks on packet */
138 if (len > ETH_DATA_LEN - 6) { /* 6-byte overhead for PPPoE header */
139 syslog(LOG_ERR, "Invalid PPPoE packet length (%u)", len);
140 return NULL;
141 }
142
143 /* Step through the tags */
144 curTag = packet->payload;
145 while(curTag - packet->payload < len) {
146 /* Alignment is not guaranteed, so do this by hand... */
147 tagType = (((UINT16_t) curTag[0]) << 8) +
148 (UINT16_t) curTag[1];
149 tagLen = (((UINT16_t) curTag[2]) << 8) +
150 (UINT16_t) curTag[3];
151 if (tagType == TAG_END_OF_LIST) {
152 return NULL;
153 }
154 if ((curTag - packet->payload) + tagLen + TAG_HDR_SIZE > len) {
155 syslog(LOG_ERR, "Invalid PPPoE tag length (%u)", tagLen);
156 return NULL;
157 }
158 if (tagType == type) {
159 memcpy(tag, curTag, tagLen + TAG_HDR_SIZE);
160 return curTag;
161 }
162 curTag = curTag + TAG_HDR_SIZE + tagLen;
163 }
164 return NULL;
165}
166
167/**********************************************************************
168*%FUNCTION: switchToRealID
169*%ARGUMENTS:
170* None
171*%RETURNS:
172* Nothing
173*%DESCRIPTION:
174* Sets effective user-ID and group-ID to real ones. Aborts on failure
175***********************************************************************/
176void
177switchToRealID (void) {
178 if (IsSetID) {
179 if ((int)saved_uid < 0) saved_uid = geteuid();
180 if ((int)saved_gid < 0) saved_gid = getegid();
181 if (setegid(getgid()) < 0) {
182 printErr("setgid failed");
183 exit(EXIT_FAILURE);
184 }
185 if (seteuid(getuid()) < 0) {
186 printErr("seteuid failed");
187 exit(EXIT_FAILURE);
188 }
189 }
190}
191
192/**********************************************************************
193*%FUNCTION: switchToEffectiveID
194*%ARGUMENTS:
195* None
196*%RETURNS:
197* Nothing
198*%DESCRIPTION:
199* Sets effective user-ID and group-ID back to saved gid/uid
200***********************************************************************/
201void
202switchToEffectiveID (void) {
203 if (IsSetID) {
204 if (setegid(saved_gid) < 0) {
205 printErr("setgid failed");
206 exit(EXIT_FAILURE);
207 }
208 if (seteuid(saved_uid) < 0) {
209 printErr("seteuid failed");
210 exit(EXIT_FAILURE);
211 }
212 }
213}
214
215/**********************************************************************
216*%FUNCTION: dropPrivs
217*%ARGUMENTS:
218* None
219*%RETURNS:
220* Nothing
221*%DESCRIPTION:
222* If effective ID is root, try to become "nobody". If that fails and
223* we're SUID, switch to real user-ID
224***********************************************************************/
225void
226dropPrivs(void)
227{
228 struct passwd *pw = NULL;
229 int ok = 0;
230 if (geteuid() == 0) {
231 pw = getpwnam("nobody");
232 if (pw) {
233 if (setgid(pw->pw_gid) < 0) ok++;
234 if (setuid(pw->pw_uid) < 0) ok++;
235 }
236 }
237 if (ok < 2 && IsSetID) {
238 setegid(getgid());
239 seteuid(getuid());
240 }
241}
242
243/**********************************************************************
244*%FUNCTION: printErr
245*%ARGUMENTS:
246* str -- error message
247*%RETURNS:
248* Nothing
249*%DESCRIPTION:
250* Prints a message to stderr and syslog.
251***********************************************************************/
252void
253printErr(char const *str)
254{
255 fprintf(stderr, "pppoe: %s\n", str);
256 syslog(LOG_ERR, "%s", str);
257}
258
259
260/**********************************************************************
261*%FUNCTION: strDup
262*%ARGUMENTS:
263* str -- string to copy
264*%RETURNS:
265* A malloc'd copy of str. Exits if malloc fails.
266***********************************************************************/
267char *
268strDup(char const *str)
269{
270 char *copy = malloc(strlen(str)+1);
271 if (!copy) {
272 rp_fatal("strdup failed");
273 }
274 strcpy(copy, str);
275 return copy;
276}
277
278/**********************************************************************
279*%FUNCTION: computeTCPChecksum
280*%ARGUMENTS:
281* ipHdr -- pointer to IP header
282* tcpHdr -- pointer to TCP header
283*%RETURNS:
284* The computed TCP checksum
285***********************************************************************/
286UINT16_t
287computeTCPChecksum(unsigned char *ipHdr, unsigned char *tcpHdr)
288{
289 UINT32_t sum = 0;
290 UINT16_t count = ipHdr[2] * 256 + ipHdr[3];
291 UINT16_t tmp;
292
293 unsigned char *addr = tcpHdr;
294 unsigned char pseudoHeader[12];
295
296 /* Count number of bytes in TCP header and data */
297 count -= (ipHdr[0] & 0x0F) * 4;
298
299 memcpy(pseudoHeader, ipHdr+12, 8);
300 pseudoHeader[8] = 0;
301 pseudoHeader[9] = ipHdr[9];
302 pseudoHeader[10] = (count >> 8) & 0xFF;
303 pseudoHeader[11] = (count & 0xFF);
304
305 /* Checksum the pseudo-header */
306 sum += * (UINT16_t *) pseudoHeader;
307 sum += * ((UINT16_t *) (pseudoHeader+2));
308 sum += * ((UINT16_t *) (pseudoHeader+4));
309 sum += * ((UINT16_t *) (pseudoHeader+6));
310 sum += * ((UINT16_t *) (pseudoHeader+8));
311 sum += * ((UINT16_t *) (pseudoHeader+10));
312
313 /* Checksum the TCP header and data */
314 while (count > 1) {
315 memcpy(&tmp, addr, sizeof(tmp));
316 sum += (UINT32_t) tmp;
317 addr += sizeof(tmp);
318 count -= sizeof(tmp);
319 }
320 if (count > 0) {
321 sum += (unsigned char) *addr;
322 }
323
324 while(sum >> 16) {
325 sum = (sum & 0xffff) + (sum >> 16);
326 }
327 return (UINT16_t) ((~sum) & 0xFFFF);
328}
329
330/**********************************************************************
331*%FUNCTION: clampMSS
332*%ARGUMENTS:
333* packet -- PPPoE session packet
334* dir -- either "incoming" or "outgoing"
335* clampMss -- clamp value
336*%RETURNS:
337* Nothing
338*%DESCRIPTION:
339* Clamps MSS option if TCP SYN flag is set.
340***********************************************************************/
341void
342clampMSS(PPPoEPacket *packet, char const *dir, int clampMss)
343{
344 unsigned char *tcpHdr;
345 unsigned char *ipHdr;
346 unsigned char *opt;
347 unsigned char *endHdr;
348 unsigned char *mssopt = NULL;
349 UINT16_t csum;
350
351 int len, minlen;
352
353 /* check PPP protocol type */
354 if (packet->payload[0] & 0x01) {
355 /* 8 bit protocol type */
356
357 /* Is it IPv4? */
358 if (packet->payload[0] != 0x21) {
359 /* Nope, ignore it */
360 return;
361 }
362
363 ipHdr = packet->payload + 1;
364 minlen = 41;
365 } else {
366 /* 16 bit protocol type */
367
368 /* Is it IPv4? */
369 if (packet->payload[0] != 0x00 ||
370 packet->payload[1] != 0x21) {
371 /* Nope, ignore it */
372 return;
373 }
374
375 ipHdr = packet->payload + 2;
376 minlen = 42;
377 }
378
379 /* Is it too short? */
380 len = (int) ntohs(packet->length);
381 if (len < minlen) {
382 /* 20 byte IP header; 20 byte TCP header; at least 1 or 2 byte PPP protocol */
383 return;
384 }
385
386 /* Verify once more that it's IPv4 */
387 if ((ipHdr[0] & 0xF0) != 0x40) {
388 return;
389 }
390
391 /* Is it a fragment that's not at the beginning of the packet? */
392 if ((ipHdr[6] & 0x1F) || ipHdr[7]) {
393 /* Yup, don't touch! */
394 return;
395 }
396 /* Is it TCP? */
397 if (ipHdr[9] != 0x06) {
398 return;
399 }
400
401 /* Get start of TCP header */
402 tcpHdr = ipHdr + (ipHdr[0] & 0x0F) * 4;
403
404 /* Is SYN set? */
405 if (!(tcpHdr[13] & 0x02)) {
406 return;
407 }
408
409 /* Compute and verify TCP checksum -- do not touch a packet with a bad
410 checksum */
411 csum = computeTCPChecksum(ipHdr, tcpHdr);
412 if (csum) {
413 syslog(LOG_ERR, "Bad TCP checksum %x", (unsigned int) csum);
414
415 /* Upper layers will drop it */
416 return;
417 }
418
419 /* Look for existing MSS option */
420 endHdr = tcpHdr + ((tcpHdr[12] & 0xF0) >> 2);
421 opt = tcpHdr + 20;
422 while (opt < endHdr) {
423 if (!*opt) break; /* End of options */
424 switch(*opt) {
425 case 1:
426 opt++;
427 break;
428
429 case 2:
430 if (opt[1] != 4) {
431 /* Something fishy about MSS option length. */
432 syslog(LOG_ERR,
433 "Bogus length for MSS option (%u) from %u.%u.%u.%u",
434 (unsigned int) opt[1],
435 (unsigned int) ipHdr[12],
436 (unsigned int) ipHdr[13],
437 (unsigned int) ipHdr[14],
438 (unsigned int) ipHdr[15]);
439 return;
440 }
441 mssopt = opt;
442 break;
443 default:
444 if (opt[1] < 2) {
445 /* Someone's trying to attack us? */
446 syslog(LOG_ERR,
447 "Bogus TCP option length (%u) from %u.%u.%u.%u",
448 (unsigned int) opt[1],
449 (unsigned int) ipHdr[12],
450 (unsigned int) ipHdr[13],
451 (unsigned int) ipHdr[14],
452 (unsigned int) ipHdr[15]);
453 return;
454 }
455 opt += (opt[1]);
456 break;
457 }
458 /* Found existing MSS option? */
459 if (mssopt) break;
460 }
461
462 /* If MSS exists and it's low enough, do nothing */
463 if (mssopt) {
464 unsigned mss = mssopt[2] * 256 + mssopt[3];
465 if (mss <= (unsigned int)clampMss) {
466 return;
467 }
468
469 mssopt[2] = (((unsigned) clampMss) >> 8) & 0xFF;
470 mssopt[3] = ((unsigned) clampMss) & 0xFF;
471 } else {
472 /* No MSS option. Don't add one; we'll have to use 536. */
473 return;
474 }
475
476 /* Recompute TCP checksum */
477 tcpHdr[16] = 0;
478 tcpHdr[17] = 0;
479 csum = computeTCPChecksum(ipHdr, tcpHdr);
480 (* (UINT16_t *) (tcpHdr+16)) = csum;
481}
482
483/***********************************************************************
484*%FUNCTION: sendPADT
485*%ARGUMENTS:
486* conn -- PPPoE connection
487* msg -- if non-NULL, extra error message to include in PADT packet.
488*%RETURNS:
489* Nothing
490*%DESCRIPTION:
491* Sends a PADT packet
492***********************************************************************/
493void
494sendPADT(PPPoEConnection *conn, char const *msg)
495{
496 PPPoEPacket packet;
497 unsigned char *cursor = packet.payload;
498
499 UINT16_t plen = 0;
500
501 /* Do nothing if no session established yet */
502 if (!conn->session) return;
503
504 /* Do nothing if no discovery socket */
505 if (conn->discoverySocket < 0) return;
506
507 memcpy(packet.ethHdr.h_dest, conn->peerEth, ETH_ALEN);
508 memcpy(packet.ethHdr.h_source, conn->myEth, ETH_ALEN);
509
510 packet.ethHdr.h_proto = htons(Eth_PPPOE_Discovery);
511 packet.ver = 1;
512 packet.type = 1;
513 packet.code = CODE_PADT;
514 packet.session = conn->session;
515
516 /* Reset Session to zero so there is no possibility of
517 recursive calls to this function by any signal handler */
518 conn->session = 0;
519
520 /* If we're using Host-Uniq, copy it over */
521 if (conn->useHostUniq) {
522 PPPoETag hostUniq;
523 pid_t pid = getpid();
524 hostUniq.type = htons(TAG_HOST_UNIQ);
525 hostUniq.length = htons(sizeof(pid));
526 memcpy(hostUniq.payload, &pid, sizeof(pid));
527 memcpy(cursor, &hostUniq, sizeof(pid) + TAG_HDR_SIZE);
528 cursor += sizeof(pid) + TAG_HDR_SIZE;
529 plen += sizeof(pid) + TAG_HDR_SIZE;
530 }
531
532 /* Copy error message */
533 if (msg) {
534 PPPoETag err;
535 size_t elen = strlen(msg);
536 err.type = htons(TAG_GENERIC_ERROR);
537 err.length = htons(elen);
538 strcpy((char *) err.payload, msg);
539 memcpy(cursor, &err, elen + TAG_HDR_SIZE);
540 cursor += elen + TAG_HDR_SIZE;
541 plen += elen + TAG_HDR_SIZE;
542 }
543
544 /* Copy cookie and relay-ID if needed */
545 if (conn->cookie.type) {
546 CHECK_ROOM(cursor, packet.payload,
547 ntohs(conn->cookie.length) + TAG_HDR_SIZE);
548 memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE);
549 cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
550 plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
551 }
552
553 if (conn->relayId.type) {
554 CHECK_ROOM(cursor, packet.payload,
555 ntohs(conn->relayId.length) + TAG_HDR_SIZE);
556 memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE);
557 cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
558 plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
559 }
560
561 packet.length = htons(plen);
562 sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
563#ifdef DEBUGGING_ENABLED
564 if (conn->debugFile) {
565 dumpPacket(conn->debugFile, &packet, "SENT");
566 fprintf(conn->debugFile, "\n");
567 fflush(conn->debugFile);
568 }
569#endif
570 syslog(LOG_INFO,"Sent PADT");
571}
572
573/***********************************************************************
574*%FUNCTION: sendPADTf
575*%ARGUMENTS:
576* conn -- PPPoE connection
577* msg -- printf-style format string
578* args -- arguments for msg
579*%RETURNS:
580* Nothing
581*%DESCRIPTION:
582* Sends a PADT packet with a formatted message
583***********************************************************************/
584void
585sendPADTf(PPPoEConnection *conn, char const *fmt, ...)
586{
587 char msg[512];
588 va_list ap;
589
590 va_start(ap, fmt);
591 vsnprintf(msg, sizeof(msg), fmt, ap);
592 va_end(ap);
593 msg[511] = 0;
594
595 sendPADT(conn, msg);
596}
597
598/**********************************************************************
599*%FUNCTION: pktLogErrs
600*%ARGUMENTS:
601* pkt -- packet type (a string)
602* type -- tag type
603* len -- tag length
604* data -- tag data
605* extra -- extra user data
606*%RETURNS:
607* Nothing
608*%DESCRIPTION:
609* Logs error tags
610***********************************************************************/
611void
612pktLogErrs(char const *pkt,
613 UINT16_t type, UINT16_t len, unsigned char *data,
614 void *extra)
615{
616 char const *str;
617 char const *fmt = "%s: %s: %.*s";
618 switch(type) {
619 case TAG_SERVICE_NAME_ERROR:
620 str = "Service-Name-Error";
621 break;
622 case TAG_AC_SYSTEM_ERROR:
623 str = "System-Error";
624 break;
625 default:
626 str = "Generic-Error";
627 }
628
629 syslog(LOG_ERR, fmt, pkt, str, (int) len, data);
630 fprintf(stderr, fmt, pkt, str, (int) len, data);
631 fprintf(stderr, "\n");
632}
633
634/**********************************************************************
635*%FUNCTION: parseLogErrs
636*%ARGUMENTS:
637* type -- tag type
638* len -- tag length
639* data -- tag data
640* extra -- extra user data
641*%RETURNS:
642* Nothing
643*%DESCRIPTION:
644* Picks error tags out of a packet and logs them.
645***********************************************************************/
646void
647parseLogErrs(UINT16_t type, UINT16_t len, unsigned char *data,
648 void *extra)
649{
650 pktLogErrs("PADT", type, len, data, extra);
651}
652