"Fossies" - the Fresh Open Source Software Archive

Member "rp-pppoe-3.12/src/common.c" (11 Nov 2015, 16947 Bytes) of archive /linux/misc/rp-pppoe-3.12.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "common.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.11_vs_3.12.

    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 
   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 = (uid_t) -2;
   49 static uid_t saved_gid = (uid_t) -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_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 ***********************************************************************/
  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_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 ***********************************************************************/
  173 void
  174 switchToRealID (void) {
  175     if (IsSetID) {
  176     if (saved_uid == (uid_t) -2) saved_uid = geteuid();
  177     if (saved_gid == (uid_t) -2) 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->hostUniq) {
  519     PPPoETag hostUniq;
  520     int len = (int) strlen(conn->hostUniq);
  521     hostUniq.type = htons(TAG_HOST_UNIQ);
  522     hostUniq.length = htons(len);
  523     memcpy(hostUniq.payload, conn->hostUniq, len);
  524     CHECK_ROOM(cursor, packet.payload, len + TAG_HDR_SIZE);
  525     memcpy(cursor, &hostUniq, len + TAG_HDR_SIZE);
  526     cursor += len + TAG_HDR_SIZE;
  527     plen += len + TAG_HDR_SIZE;
  528     }
  529 
  530     /* Copy error message */
  531     if (msg) {
  532     PPPoETag err;
  533     size_t elen = strlen(msg);
  534     err.type = htons(TAG_GENERIC_ERROR);
  535     err.length = htons(elen);
  536     strcpy((char *) err.payload, msg);
  537     memcpy(cursor, &err, elen + TAG_HDR_SIZE);
  538     cursor += elen + TAG_HDR_SIZE;
  539     plen += elen + TAG_HDR_SIZE;
  540     }
  541 
  542     /* Copy cookie and relay-ID if needed */
  543     if (conn->cookie.type) {
  544     CHECK_ROOM(cursor, packet.payload,
  545            ntohs(conn->cookie.length) + TAG_HDR_SIZE);
  546     memcpy(cursor, &conn->cookie, ntohs(conn->cookie.length) + TAG_HDR_SIZE);
  547     cursor += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
  548     plen += ntohs(conn->cookie.length) + TAG_HDR_SIZE;
  549     }
  550 
  551     if (conn->relayId.type) {
  552     CHECK_ROOM(cursor, packet.payload,
  553            ntohs(conn->relayId.length) + TAG_HDR_SIZE);
  554     memcpy(cursor, &conn->relayId, ntohs(conn->relayId.length) + TAG_HDR_SIZE);
  555     cursor += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
  556     plen += ntohs(conn->relayId.length) + TAG_HDR_SIZE;
  557     }
  558 
  559     packet.length = htons(plen);
  560     sendPacket(conn, conn->discoverySocket, &packet, (int) (plen + HDR_SIZE));
  561 #ifdef DEBUGGING_ENABLED
  562     if (conn->debugFile) {
  563     dumpPacket(conn->debugFile, &packet, "SENT");
  564     fprintf(conn->debugFile, "\n");
  565     fflush(conn->debugFile);
  566     }
  567 #endif
  568     syslog(LOG_INFO,"Sent PADT");
  569 }
  570 
  571 /***********************************************************************
  572 *%FUNCTION: sendPADTf
  573 *%ARGUMENTS:
  574 * conn -- PPPoE connection
  575 * msg -- printf-style format string
  576 * args -- arguments for msg
  577 *%RETURNS:
  578 * Nothing
  579 *%DESCRIPTION:
  580 * Sends a PADT packet with a formatted message
  581 ***********************************************************************/
  582 void
  583 sendPADTf(PPPoEConnection *conn, char const *fmt, ...)
  584 {
  585     char msg[512];
  586     va_list ap;
  587 
  588     va_start(ap, fmt);
  589     vsnprintf(msg, sizeof(msg), fmt, ap);
  590     va_end(ap);
  591     msg[511] = 0;
  592 
  593     sendPADT(conn, msg);
  594 }
  595 
  596 /**********************************************************************
  597 *%FUNCTION: pktLogErrs
  598 *%ARGUMENTS:
  599 * pkt -- packet type (a string)
  600 * type -- tag type
  601 * len -- tag length
  602 * data -- tag data
  603 * extra -- extra user data
  604 *%RETURNS:
  605 * Nothing
  606 *%DESCRIPTION:
  607 * Logs error tags
  608 ***********************************************************************/
  609 void
  610 pktLogErrs(char const *pkt,
  611        UINT16_t type, UINT16_t len, unsigned char *data,
  612        void *extra)
  613 {
  614     char const *str;
  615     char const *fmt = "%s: %s: %.*s";
  616     switch(type) {
  617     case TAG_SERVICE_NAME_ERROR:
  618     str = "Service-Name-Error";
  619     break;
  620     case TAG_AC_SYSTEM_ERROR:
  621     str = "System-Error";
  622     break;
  623     default:
  624     str = "Generic-Error";
  625     }
  626 
  627     syslog(LOG_ERR, fmt, pkt, str, (int) len, data);
  628     fprintf(stderr, fmt, pkt, str, (int) len, data);
  629     fprintf(stderr, "\n");
  630 }
  631 
  632 /**********************************************************************
  633 *%FUNCTION: parseLogErrs
  634 *%ARGUMENTS:
  635 * type -- tag type
  636 * len -- tag length
  637 * data -- tag data
  638 * extra -- extra user data
  639 *%RETURNS:
  640 * Nothing
  641 *%DESCRIPTION:
  642 * Picks error tags out of a packet and logs them.
  643 ***********************************************************************/
  644 void
  645 parseLogErrs(UINT16_t type, UINT16_t len, unsigned char *data,
  646          void *extra)
  647 {
  648     pktLogErrs("PADT", type, len, data, extra);
  649 }