"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 }