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