1 /* 2 * upap.c - User/Password Authentication Protocol. 3 * 4 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * 3. The name "Carnegie Mellon University" must not be used to 19 * endorse or promote products derived from this software without 20 * prior written permission. For permission or any legal 21 * details, please contact 22 * Office of Technology Transfer 23 * Carnegie Mellon University 24 * 5000 Forbes Avenue 25 * Pittsburgh, PA 15213-3890 26 * (412) 268-4387, fax: (412) 268-7395 27 * tech-transfer@andrew.cmu.edu 28 * 29 * 4. Redistributions of any form whatsoever must retain the following 30 * acknowledgment: 31 * "This product includes software developed by Computing Services 32 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 33 * 34 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 35 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 36 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 37 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 38 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 39 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 40 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 41 */ 42 43 #include "netif/ppp/ppp_opts.h" 44 #if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ 45 46 /* 47 * @todo: 48 */ 49 50 #if 0 /* UNUSED */ 51 #include <stdio.h> 52 #include <string.h> 53 #endif /* UNUSED */ 54 55 #include "netif/ppp/ppp_impl.h" 56 57 #include "netif/ppp/upap.h" 58 59 #if PPP_OPTIONS 60 /* 61 * Command-line options. 62 */ 63 static option_t pap_option_list[] = { 64 { "hide-password", o_bool, &hide_password, 65 "Don't output passwords to log", OPT_PRIO | 1 }, 66 { "show-password", o_bool, &hide_password, 67 "Show password string in debug log messages", OPT_PRIOSUB | 0 }, 68 69 { "pap-restart", o_int, &upap[0].us_timeouttime, 70 "Set retransmit timeout for PAP", OPT_PRIO }, 71 { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, 72 "Set max number of transmissions for auth-reqs", OPT_PRIO }, 73 { "pap-timeout", o_int, &upap[0].us_reqtimeout, 74 "Set time limit for peer PAP authentication", OPT_PRIO }, 75 76 { NULL } 77 }; 78 #endif /* PPP_OPTIONS */ 79 80 /* 81 * Protocol entry points. 82 */ 83 static void upap_init(ppp_pcb *pcb); 84 static void upap_lowerup(ppp_pcb *pcb); 85 static void upap_lowerdown(ppp_pcb *pcb); 86 static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l); 87 static void upap_protrej(ppp_pcb *pcb); 88 #if PRINTPKT_SUPPORT 89 static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg); 90 #endif /* PRINTPKT_SUPPORT */ 91 92 const struct protent pap_protent = { 93 PPP_PAP, 94 upap_init, 95 upap_input, 96 upap_protrej, 97 upap_lowerup, 98 upap_lowerdown, 99 NULL, 100 NULL, 101 #if PRINTPKT_SUPPORT 102 upap_printpkt, 103 #endif /* PRINTPKT_SUPPORT */ 104 #if PPP_DATAINPUT 105 NULL, 106 #endif /* PPP_DATAINPUT */ 107 #if PRINTPKT_SUPPORT 108 "PAP", 109 NULL, 110 #endif /* PRINTPKT_SUPPORT */ 111 #if PPP_OPTIONS 112 pap_option_list, 113 NULL, 114 #endif /* PPP_OPTIONS */ 115 #if DEMAND_SUPPORT 116 NULL, 117 NULL 118 #endif /* DEMAND_SUPPORT */ 119 }; 120 121 static void upap_timeout(void *arg); 122 #if PPP_SERVER 123 static void upap_reqtimeout(void *arg); 124 static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len); 125 #endif /* PPP_SERVER */ 126 static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len); 127 static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len); 128 static void upap_sauthreq(ppp_pcb *pcb); 129 #if PPP_SERVER 130 static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen); 131 #endif /* PPP_SERVER */ 132 133 134 /* 135 * upap_init - Initialize a UPAP unit. 136 */ 137 static void upap_init(ppp_pcb *pcb) { 138 pcb->upap.us_user = NULL; 139 pcb->upap.us_userlen = 0; 140 pcb->upap.us_passwd = NULL; 141 pcb->upap.us_passwdlen = 0; 142 pcb->upap.us_clientstate = UPAPCS_INITIAL; 143 #if PPP_SERVER 144 pcb->upap.us_serverstate = UPAPSS_INITIAL; 145 #endif /* PPP_SERVER */ 146 pcb->upap.us_id = 0; 147 } 148 149 150 /* 151 * upap_authwithpeer - Authenticate us with our peer (start client). 152 * 153 * Set new state and send authenticate's. 154 */ 155 void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password) { 156 157 if(!user || !password) 158 return; 159 160 /* Save the username and password we're given */ 161 pcb->upap.us_user = user; 162 pcb->upap.us_userlen = (u8_t)LWIP_MIN(strlen(user), 0xff); 163 pcb->upap.us_passwd = password; 164 pcb->upap.us_passwdlen = (u8_t)LWIP_MIN(strlen(password), 0xff); 165 pcb->upap.us_transmits = 0; 166 167 /* Lower layer up yet? */ 168 if (pcb->upap.us_clientstate == UPAPCS_INITIAL || 169 pcb->upap.us_clientstate == UPAPCS_PENDING) { 170 pcb->upap.us_clientstate = UPAPCS_PENDING; 171 return; 172 } 173 174 upap_sauthreq(pcb); /* Start protocol */ 175 } 176 177 #if PPP_SERVER 178 /* 179 * upap_authpeer - Authenticate our peer (start server). 180 * 181 * Set new state. 182 */ 183 void upap_authpeer(ppp_pcb *pcb) { 184 185 /* Lower layer up yet? */ 186 if (pcb->upap.us_serverstate == UPAPSS_INITIAL || 187 pcb->upap.us_serverstate == UPAPSS_PENDING) { 188 pcb->upap.us_serverstate = UPAPSS_PENDING; 189 return; 190 } 191 192 pcb->upap.us_serverstate = UPAPSS_LISTEN; 193 if (pcb->settings.pap_req_timeout > 0) 194 TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout); 195 } 196 #endif /* PPP_SERVER */ 197 198 /* 199 * upap_timeout - Retransmission timer for sending auth-reqs expired. 200 */ 201 static void upap_timeout(void *arg) { 202 ppp_pcb *pcb = (ppp_pcb*)arg; 203 204 if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) 205 return; 206 207 if (pcb->upap.us_transmits >= pcb->settings.pap_max_transmits) { 208 /* give up in disgust */ 209 ppp_error(("No response to PAP authenticate-requests")); 210 pcb->upap.us_clientstate = UPAPCS_BADAUTH; 211 auth_withpeer_fail(pcb, PPP_PAP); 212 return; 213 } 214 215 upap_sauthreq(pcb); /* Send Authenticate-Request */ 216 } 217 218 219 #if PPP_SERVER 220 /* 221 * upap_reqtimeout - Give up waiting for the peer to send an auth-req. 222 */ 223 static void upap_reqtimeout(void *arg) { 224 ppp_pcb *pcb = (ppp_pcb*)arg; 225 226 if (pcb->upap.us_serverstate != UPAPSS_LISTEN) 227 return; /* huh?? */ 228 229 auth_peer_fail(pcb, PPP_PAP); 230 pcb->upap.us_serverstate = UPAPSS_BADAUTH; 231 } 232 #endif /* PPP_SERVER */ 233 234 235 /* 236 * upap_lowerup - The lower layer is up. 237 * 238 * Start authenticating if pending. 239 */ 240 static void upap_lowerup(ppp_pcb *pcb) { 241 242 if (pcb->upap.us_clientstate == UPAPCS_INITIAL) 243 pcb->upap.us_clientstate = UPAPCS_CLOSED; 244 else if (pcb->upap.us_clientstate == UPAPCS_PENDING) { 245 upap_sauthreq(pcb); /* send an auth-request */ 246 } 247 248 #if PPP_SERVER 249 if (pcb->upap.us_serverstate == UPAPSS_INITIAL) 250 pcb->upap.us_serverstate = UPAPSS_CLOSED; 251 else if (pcb->upap.us_serverstate == UPAPSS_PENDING) { 252 pcb->upap.us_serverstate = UPAPSS_LISTEN; 253 if (pcb->settings.pap_req_timeout > 0) 254 TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout); 255 } 256 #endif /* PPP_SERVER */ 257 } 258 259 260 /* 261 * upap_lowerdown - The lower layer is down. 262 * 263 * Cancel all timeouts. 264 */ 265 static void upap_lowerdown(ppp_pcb *pcb) { 266 267 if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */ 268 UNTIMEOUT(upap_timeout, pcb); /* Cancel timeout */ 269 #if PPP_SERVER 270 if (pcb->upap.us_serverstate == UPAPSS_LISTEN && pcb->settings.pap_req_timeout > 0) 271 UNTIMEOUT(upap_reqtimeout, pcb); 272 #endif /* PPP_SERVER */ 273 274 pcb->upap.us_clientstate = UPAPCS_INITIAL; 275 #if PPP_SERVER 276 pcb->upap.us_serverstate = UPAPSS_INITIAL; 277 #endif /* PPP_SERVER */ 278 } 279 280 281 /* 282 * upap_protrej - Peer doesn't speak this protocol. 283 * 284 * This shouldn't happen. In any case, pretend lower layer went down. 285 */ 286 static void upap_protrej(ppp_pcb *pcb) { 287 288 if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) { 289 ppp_error(("PAP authentication failed due to protocol-reject")); 290 auth_withpeer_fail(pcb, PPP_PAP); 291 } 292 #if PPP_SERVER 293 if (pcb->upap.us_serverstate == UPAPSS_LISTEN) { 294 ppp_error(("PAP authentication of peer failed (protocol-reject)")); 295 auth_peer_fail(pcb, PPP_PAP); 296 } 297 #endif /* PPP_SERVER */ 298 upap_lowerdown(pcb); 299 } 300 301 302 /* 303 * upap_input - Input UPAP packet. 304 */ 305 static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l) { 306 u_char *inp; 307 u_char code, id; 308 int len; 309 310 /* 311 * Parse header (code, id and length). 312 * If packet too short, drop it. 313 */ 314 inp = inpacket; 315 if (l < UPAP_HEADERLEN) { 316 UPAPDEBUG(("pap_input: rcvd short header.")); 317 return; 318 } 319 GETCHAR(code, inp); 320 GETCHAR(id, inp); 321 GETSHORT(len, inp); 322 if (len < UPAP_HEADERLEN) { 323 UPAPDEBUG(("pap_input: rcvd illegal length.")); 324 return; 325 } 326 if (len > l) { 327 UPAPDEBUG(("pap_input: rcvd short packet.")); 328 return; 329 } 330 len -= UPAP_HEADERLEN; 331 332 /* 333 * Action depends on code. 334 */ 335 switch (code) { 336 case UPAP_AUTHREQ: 337 #if PPP_SERVER 338 upap_rauthreq(pcb, inp, id, len); 339 #endif /* PPP_SERVER */ 340 break; 341 342 case UPAP_AUTHACK: 343 upap_rauthack(pcb, inp, id, len); 344 break; 345 346 case UPAP_AUTHNAK: 347 upap_rauthnak(pcb, inp, id, len); 348 break; 349 350 default: /* XXX Need code reject */ 351 break; 352 } 353 } 354 355 #if PPP_SERVER 356 /* 357 * upap_rauth - Receive Authenticate. 358 */ 359 static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len) { 360 u_char ruserlen, rpasswdlen; 361 char *ruser; 362 char *rpasswd; 363 char rhostname[256]; 364 int retcode; 365 const char *msg; 366 int msglen; 367 368 if (pcb->upap.us_serverstate < UPAPSS_LISTEN) 369 return; 370 371 /* 372 * If we receive a duplicate authenticate-request, we are 373 * supposed to return the same status as for the first request. 374 */ 375 if (pcb->upap.us_serverstate == UPAPSS_OPEN) { 376 upap_sresp(pcb, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ 377 return; 378 } 379 if (pcb->upap.us_serverstate == UPAPSS_BADAUTH) { 380 upap_sresp(pcb, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ 381 return; 382 } 383 384 /* 385 * Parse user/passwd. 386 */ 387 if (len < 1) { 388 UPAPDEBUG(("pap_rauth: rcvd short packet.")); 389 return; 390 } 391 GETCHAR(ruserlen, inp); 392 len -= sizeof (u_char) + ruserlen + sizeof (u_char); 393 if (len < 0) { 394 UPAPDEBUG(("pap_rauth: rcvd short packet.")); 395 return; 396 } 397 ruser = (char *) inp; 398 INCPTR(ruserlen, inp); 399 GETCHAR(rpasswdlen, inp); 400 if (len < rpasswdlen) { 401 UPAPDEBUG(("pap_rauth: rcvd short packet.")); 402 return; 403 } 404 405 rpasswd = (char *) inp; 406 407 /* 408 * Check the username and password given. 409 */ 410 retcode = UPAP_AUTHNAK; 411 if (auth_check_passwd(pcb, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen)) { 412 retcode = UPAP_AUTHACK; 413 } 414 BZERO(rpasswd, rpasswdlen); 415 416 #if 0 /* UNUSED */ 417 /* 418 * Check remote number authorization. A plugin may have filled in 419 * the remote number or added an allowed number, and rather than 420 * return an authenticate failure, is leaving it for us to verify. 421 */ 422 if (retcode == UPAP_AUTHACK) { 423 if (!auth_number()) { 424 /* We do not want to leak info about the pap result. */ 425 retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */ 426 warn("calling number %q is not authorized", remote_number); 427 } 428 } 429 430 msglen = strlen(msg); 431 if (msglen > 255) 432 msglen = 255; 433 #endif /* UNUSED */ 434 435 upap_sresp(pcb, retcode, id, msg, msglen); 436 437 /* Null terminate and clean remote name. */ 438 ppp_slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser); 439 440 if (retcode == UPAP_AUTHACK) { 441 pcb->upap.us_serverstate = UPAPSS_OPEN; 442 ppp_notice(("PAP peer authentication succeeded for %q", rhostname)); 443 auth_peer_success(pcb, PPP_PAP, 0, ruser, ruserlen); 444 } else { 445 pcb->upap.us_serverstate = UPAPSS_BADAUTH; 446 ppp_warn(("PAP peer authentication failed for %q", rhostname)); 447 auth_peer_fail(pcb, PPP_PAP); 448 } 449 450 if (pcb->settings.pap_req_timeout > 0) 451 UNTIMEOUT(upap_reqtimeout, pcb); 452 } 453 #endif /* PPP_SERVER */ 454 455 /* 456 * upap_rauthack - Receive Authenticate-Ack. 457 */ 458 static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len) { 459 u_char msglen; 460 char *msg; 461 LWIP_UNUSED_ARG(id); 462 463 if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */ 464 return; 465 466 /* 467 * Parse message. 468 */ 469 if (len < 1) { 470 UPAPDEBUG(("pap_rauthack: ignoring missing msg-length.")); 471 } else { 472 GETCHAR(msglen, inp); 473 if (msglen > 0) { 474 len -= sizeof (u_char); 475 if (len < msglen) { 476 UPAPDEBUG(("pap_rauthack: rcvd short packet.")); 477 return; 478 } 479 msg = (char *) inp; 480 PRINTMSG(msg, msglen); 481 } 482 } 483 484 UNTIMEOUT(upap_timeout, pcb); 485 pcb->upap.us_clientstate = UPAPCS_OPEN; 486 487 auth_withpeer_success(pcb, PPP_PAP, 0); 488 } 489 490 491 /* 492 * upap_rauthnak - Receive Authenticate-Nak. 493 */ 494 static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len) { 495 u_char msglen; 496 char *msg; 497 LWIP_UNUSED_ARG(id); 498 499 if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */ 500 return; 501 502 /* 503 * Parse message. 504 */ 505 if (len < 1) { 506 UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length.")); 507 } else { 508 GETCHAR(msglen, inp); 509 if (msglen > 0) { 510 len -= sizeof (u_char); 511 if (len < msglen) { 512 UPAPDEBUG(("pap_rauthnak: rcvd short packet.")); 513 return; 514 } 515 msg = (char *) inp; 516 PRINTMSG(msg, msglen); 517 } 518 } 519 520 UNTIMEOUT(upap_timeout, pcb); 521 pcb->upap.us_clientstate = UPAPCS_BADAUTH; 522 523 ppp_error(("PAP authentication failed")); 524 auth_withpeer_fail(pcb, PPP_PAP); 525 } 526 527 528 /* 529 * upap_sauthreq - Send an Authenticate-Request. 530 */ 531 static void upap_sauthreq(ppp_pcb *pcb) { 532 struct pbuf *p; 533 u_char *outp; 534 int outlen; 535 536 outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + 537 pcb->upap.us_userlen + pcb->upap.us_passwdlen; 538 p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PBUF_RAM); 539 if(NULL == p) 540 return; 541 if(p->tot_len != p->len) { 542 pbuf_free(p); 543 return; 544 } 545 546 outp = (u_char*)p->payload; 547 MAKEHEADER(outp, PPP_PAP); 548 549 PUTCHAR(UPAP_AUTHREQ, outp); 550 PUTCHAR(++pcb->upap.us_id, outp); 551 PUTSHORT(outlen, outp); 552 PUTCHAR(pcb->upap.us_userlen, outp); 553 MEMCPY(outp, pcb->upap.us_user, pcb->upap.us_userlen); 554 INCPTR(pcb->upap.us_userlen, outp); 555 PUTCHAR(pcb->upap.us_passwdlen, outp); 556 MEMCPY(outp, pcb->upap.us_passwd, pcb->upap.us_passwdlen); 557 558 ppp_write(pcb, p); 559 560 TIMEOUT(upap_timeout, pcb, pcb->settings.pap_timeout_time); 561 ++pcb->upap.us_transmits; 562 pcb->upap.us_clientstate = UPAPCS_AUTHREQ; 563 } 564 565 #if PPP_SERVER 566 /* 567 * upap_sresp - Send a response (ack or nak). 568 */ 569 static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen) { 570 struct pbuf *p; 571 u_char *outp; 572 int outlen; 573 574 outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; 575 p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PBUF_RAM); 576 if(NULL == p) 577 return; 578 if(p->tot_len != p->len) { 579 pbuf_free(p); 580 return; 581 } 582 583 outp = (u_char*)p->payload; 584 MAKEHEADER(outp, PPP_PAP); 585 586 PUTCHAR(code, outp); 587 PUTCHAR(id, outp); 588 PUTSHORT(outlen, outp); 589 PUTCHAR(msglen, outp); 590 MEMCPY(outp, msg, msglen); 591 592 ppp_write(pcb, p); 593 } 594 #endif /* PPP_SERVER */ 595 596 #if PRINTPKT_SUPPORT 597 /* 598 * upap_printpkt - print the contents of a PAP packet. 599 */ 600 static const char* const upap_codenames[] = { 601 "AuthReq", "AuthAck", "AuthNak" 602 }; 603 604 static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) { 605 int code, id, len; 606 int mlen, ulen, wlen; 607 const u_char *user, *pwd, *msg; 608 const u_char *pstart; 609 610 if (plen < UPAP_HEADERLEN) 611 return 0; 612 pstart = p; 613 GETCHAR(code, p); 614 GETCHAR(id, p); 615 GETSHORT(len, p); 616 if (len < UPAP_HEADERLEN || len > plen) 617 return 0; 618 619 if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(upap_codenames)) 620 printer(arg, " %s", upap_codenames[code-1]); 621 else 622 printer(arg, " code=0x%x", code); 623 printer(arg, " id=0x%x", id); 624 len -= UPAP_HEADERLEN; 625 switch (code) { 626 case UPAP_AUTHREQ: 627 if (len < 1) 628 break; 629 ulen = p[0]; 630 if (len < ulen + 2) 631 break; 632 wlen = p[ulen + 1]; 633 if (len < ulen + wlen + 2) 634 break; 635 user = (const u_char *) (p + 1); 636 pwd = (const u_char *) (p + ulen + 2); 637 p += ulen + wlen + 2; 638 len -= ulen + wlen + 2; 639 printer(arg, " user="); 640 ppp_print_string(user, ulen, printer, arg); 641 printer(arg, " password="); 642 /* FIXME: require ppp_pcb struct as printpkt() argument */ 643 #if 0 644 if (!pcb->settings.hide_password) 645 #endif 646 ppp_print_string(pwd, wlen, printer, arg); 647 #if 0 648 else 649 printer(arg, "<hidden>"); 650 #endif 651 break; 652 case UPAP_AUTHACK: 653 case UPAP_AUTHNAK: 654 if (len < 1) 655 break; 656 mlen = p[0]; 657 if (len < mlen + 1) 658 break; 659 msg = (const u_char *) (p + 1); 660 p += mlen + 1; 661 len -= mlen + 1; 662 printer(arg, " "); 663 ppp_print_string(msg, mlen, printer, arg); 664 break; 665 default: 666 break; 667 } 668 669 /* print the rest of the bytes in the packet */ 670 for (; len > 0; --len) { 671 GETCHAR(code, p); 672 printer(arg, " %.2x", code); 673 } 674 675 return p - pstart; 676 } 677 #endif /* PRINTPKT_SUPPORT */ 678 679 #endif /* PPP_SUPPORT && PAP_SUPPORT */ 680