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-2012 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#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? */ 46int IsSetID = 0; 47 48static uid_t saved_uid = -2; 49static 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***********************************************************************/ 63int 64parsePacket(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_JUMBO_LEN - PPPOE_OVERHEAD) { /* 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***********************************************************************/ 118unsigned char * 119findTag(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_JUMBO_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***********************************************************************/ 173void 174switchToRealID (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***********************************************************************/ 198void 199switchToEffectiveID (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***********************************************************************/ 222void 223dropPrivs(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***********************************************************************/ 249void 250printErr(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***********************************************************************/ 264char * 265strDup(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***********************************************************************/ 283UINT16_t 284computeTCPChecksum(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***********************************************************************/ 338void 339clampMSS(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***********************************************************************/ 490void 491sendPADT(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***********************************************************************/ 581void 582sendPADTf(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***********************************************************************/ 608void 609pktLogErrs(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***********************************************************************/ 643void 644parseLogErrs(UINT16_t type, UINT16_t len, unsigned char *data, 645 void *extra) 646{ 647 pktLogErrs("PADT", type, len, data, extra); 648} 649