/***************************************************************************** * pap.c - Network Password Authentication Protocol program file. * * Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc. * portions Copyright (c) 1997 by Global Election Systems Inc. * * The authors hereby grant permission to use, copy, modify, distribute, * and license this software and its documentation for any purpose, provided * that existing copyright notices are retained in all copies and that this * notice and the following disclaimer are included verbatim in any * distributions. No written agreement, license, or royalty fee is required * for any of the authorized uses. * * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** * REVISION HISTORY * * 03-01-01 Marc Boucher * Ported to lwIP. * 97-12-12 Guy Lancaster , Global Election Systems Inc. * Original. *****************************************************************************/ /* * upap.c - User/Password Authentication Protocol. * * Copyright (c) 1989 Carnegie Mellon University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Carnegie Mellon University. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include "lwip/opt.h" #if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */ #if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */ #include "ppp_impl.h" #include "pppdebug.h" #include "auth.h" #include "pap.h" #include #if 0 /* UNUSED */ static bool hide_password = 1; /* * Command-line options. */ static option_t pap_option_list[] = { { "hide-password", o_bool, &hide_password, "Don't output passwords to log", 1 }, { "show-password", o_bool, &hide_password, "Show password string in debug log messages", 0 }, { "pap-restart", o_int, &upap[0].us_timeouttime, "Set retransmit timeout for PAP" }, { "pap-max-authreq", o_int, &upap[0].us_maxtransmits, "Set max number of transmissions for auth-reqs" }, { "pap-timeout", o_int, &upap[0].us_reqtimeout, "Set time limit for peer PAP authentication" }, { NULL } }; #endif /* * Protocol entry points. */ static void upap_init (int); static void upap_lowerup (int); static void upap_lowerdown (int); static void upap_input (int, u_char *, int); static void upap_protrej (int); #if PPP_ADDITIONAL_CALLBACKS static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *); #endif /* PPP_ADDITIONAL_CALLBACKS */ struct protent pap_protent = { PPP_PAP, upap_init, upap_input, upap_protrej, upap_lowerup, upap_lowerdown, NULL, NULL, #if PPP_ADDITIONAL_CALLBACKS upap_printpkt, NULL, #endif /* PPP_ADDITIONAL_CALLBACKS */ 1, "PAP", #if PPP_ADDITIONAL_CALLBACKS NULL, NULL, NULL #endif /* PPP_ADDITIONAL_CALLBACKS */ }; upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */ static void upap_timeout (void *); static void upap_reqtimeout(void *); static void upap_rauthreq (upap_state *, u_char *, u_char, int); static void upap_rauthack (upap_state *, u_char *, int, int); static void upap_rauthnak (upap_state *, u_char *, int, int); static void upap_sauthreq (upap_state *); static void upap_sresp (upap_state *, u_char, u_char, char *, int); /* * upap_init - Initialize a UPAP unit. */ static void upap_init(int unit) { upap_state *u = &upap[unit]; UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit)); u->us_unit = unit; u->us_user = NULL; u->us_userlen = 0; u->us_passwd = NULL; u->us_passwdlen = 0; u->us_clientstate = UPAPCS_INITIAL; u->us_serverstate = UPAPSS_INITIAL; u->us_id = 0; u->us_timeouttime = UPAP_DEFTIMEOUT; u->us_maxtransmits = 10; u->us_reqtimeout = UPAP_DEFREQTIME; } /* * upap_authwithpeer - Authenticate us with our peer (start client). * * Set new state and send authenticate's. */ void upap_authwithpeer(int unit, char *user, char *password) { upap_state *u = &upap[unit]; UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n", unit, user, password, u->us_clientstate)); /* Save the username and password we're given */ u->us_user = user; u->us_userlen = (int)strlen(user); u->us_passwd = password; u->us_passwdlen = (int)strlen(password); u->us_transmits = 0; /* Lower layer up yet? */ if (u->us_clientstate == UPAPCS_INITIAL || u->us_clientstate == UPAPCS_PENDING) { u->us_clientstate = UPAPCS_PENDING; return; } upap_sauthreq(u); /* Start protocol */ } /* * upap_authpeer - Authenticate our peer (start server). * * Set new state. */ void upap_authpeer(int unit) { upap_state *u = &upap[unit]; /* Lower layer up yet? */ if (u->us_serverstate == UPAPSS_INITIAL || u->us_serverstate == UPAPSS_PENDING) { u->us_serverstate = UPAPSS_PENDING; return; } u->us_serverstate = UPAPSS_LISTEN; if (u->us_reqtimeout > 0) { TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); } } /* * upap_timeout - Retransmission timer for sending auth-reqs expired. */ static void upap_timeout(void *arg) { upap_state *u = (upap_state *) arg; UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n", u->us_unit, u->us_timeouttime, u->us_clientstate)); if (u->us_clientstate != UPAPCS_AUTHREQ) { UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n")); return; } if (u->us_transmits >= u->us_maxtransmits) { /* give up in disgust */ UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n")); u->us_clientstate = UPAPCS_BADAUTH; auth_withpeer_fail(u->us_unit, PPP_PAP); return; } upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/ } /* * upap_reqtimeout - Give up waiting for the peer to send an auth-req. */ static void upap_reqtimeout(void *arg) { upap_state *u = (upap_state *) arg; if (u->us_serverstate != UPAPSS_LISTEN) { return; /* huh?? */ } auth_peer_fail(u->us_unit, PPP_PAP); u->us_serverstate = UPAPSS_BADAUTH; } /* * upap_lowerup - The lower layer is up. * * Start authenticating if pending. */ static void upap_lowerup(int unit) { upap_state *u = &upap[unit]; UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate)); if (u->us_clientstate == UPAPCS_INITIAL) { u->us_clientstate = UPAPCS_CLOSED; } else if (u->us_clientstate == UPAPCS_PENDING) { upap_sauthreq(u); /* send an auth-request */ /* now client state is UPAPCS__AUTHREQ */ } if (u->us_serverstate == UPAPSS_INITIAL) { u->us_serverstate = UPAPSS_CLOSED; } else if (u->us_serverstate == UPAPSS_PENDING) { u->us_serverstate = UPAPSS_LISTEN; if (u->us_reqtimeout > 0) { TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout); } } } /* * upap_lowerdown - The lower layer is down. * * Cancel all timeouts. */ static void upap_lowerdown(int unit) { upap_state *u = &upap[unit]; UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate)); if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ } if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) { UNTIMEOUT(upap_reqtimeout, u); } u->us_clientstate = UPAPCS_INITIAL; u->us_serverstate = UPAPSS_INITIAL; } /* * upap_protrej - Peer doesn't speak this protocol. * * This shouldn't happen. In any case, pretend lower layer went down. */ static void upap_protrej(int unit) { upap_state *u = &upap[unit]; if (u->us_clientstate == UPAPCS_AUTHREQ) { UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n")); auth_withpeer_fail(unit, PPP_PAP); } if (u->us_serverstate == UPAPSS_LISTEN) { UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n")); auth_peer_fail(unit, PPP_PAP); } upap_lowerdown(unit); } /* * upap_input - Input UPAP packet. */ static void upap_input(int unit, u_char *inpacket, int l) { upap_state *u = &upap[unit]; u_char *inp; u_char code, id; int len; /* * Parse header (code, id and length). * If packet too short, drop it. */ inp = inpacket; if (l < (int)UPAP_HEADERLEN) { UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n")); return; } GETCHAR(code, inp); GETCHAR(id, inp); GETSHORT(len, inp); if (len < (int)UPAP_HEADERLEN) { UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n")); return; } if (len > l) { UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n")); return; } len -= UPAP_HEADERLEN; /* * Action depends on code. */ switch (code) { case UPAP_AUTHREQ: upap_rauthreq(u, inp, id, len); break; case UPAP_AUTHACK: upap_rauthack(u, inp, id, len); break; case UPAP_AUTHNAK: upap_rauthnak(u, inp, id, len); break; default: /* XXX Need code reject */ UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len)); break; } } /* * upap_rauth - Receive Authenticate. */ static void upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len) { u_char ruserlen, rpasswdlen; char *ruser, *rpasswd; u_char retcode; char *msg; int msglen; UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id)); if (u->us_serverstate < UPAPSS_LISTEN) { return; } /* * If we receive a duplicate authenticate-request, we are * supposed to return the same status as for the first request. */ if (u->us_serverstate == UPAPSS_OPEN) { upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */ return; } if (u->us_serverstate == UPAPSS_BADAUTH) { upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */ return; } /* * Parse user/passwd. */ if (len < (int)sizeof (u_char)) { UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); return; } GETCHAR(ruserlen, inp); len -= sizeof (u_char) + ruserlen + sizeof (u_char); if (len < 0) { UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); return; } ruser = (char *) inp; INCPTR(ruserlen, inp); GETCHAR(rpasswdlen, inp); if (len < rpasswdlen) { UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n")); return; } rpasswd = (char *) inp; /* * Check the username and password given. */ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen); /* lwip: currently retcode is always UPAP_AUTHACK */ BZERO(rpasswd, rpasswdlen); upap_sresp(u, retcode, id, msg, msglen); if (retcode == UPAP_AUTHACK) { u->us_serverstate = UPAPSS_OPEN; auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen); } else { u->us_serverstate = UPAPSS_BADAUTH; auth_peer_fail(u->us_unit, PPP_PAP); } if (u->us_reqtimeout > 0) { UNTIMEOUT(upap_reqtimeout, u); } } /* * upap_rauthack - Receive Authenticate-Ack. */ static void upap_rauthack(upap_state *u, u_char *inp, int id, int len) { u_char msglen; char *msg; LWIP_UNUSED_ARG(id); UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate)); if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n")); return; } /* * Parse message. */ if (len < (int)sizeof (u_char)) { UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n")); } else { GETCHAR(msglen, inp); if (msglen > 0) { len -= sizeof (u_char); if (len < msglen) { UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n")); return; } msg = (char *) inp; PRINTMSG(msg, msglen); } } UNTIMEOUT(upap_timeout, u); /* Cancel timeout */ u->us_clientstate = UPAPCS_OPEN; auth_withpeer_success(u->us_unit, PPP_PAP); } /* * upap_rauthnak - Receive Authenticate-Nak. */ static void upap_rauthnak(upap_state *u, u_char *inp, int id, int len) { u_char msglen; char *msg; LWIP_UNUSED_ARG(id); UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate)); if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */ return; } /* * Parse message. */ if (len < sizeof (u_char)) { UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n")); } else { GETCHAR(msglen, inp); if(msglen > 0) { len -= sizeof (u_char); if (len < msglen) { UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n")); return; } msg = (char *) inp; PRINTMSG(msg, msglen); } } u->us_clientstate = UPAPCS_BADAUTH; UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n")); auth_withpeer_fail(u->us_unit, PPP_PAP); } /* * upap_sauthreq - Send an Authenticate-Request. */ static void upap_sauthreq(upap_state *u) { u_char *outp; int outlen; outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) + u->us_userlen + u->us_passwdlen; outp = outpacket_buf[u->us_unit]; MAKEHEADER(outp, PPP_PAP); PUTCHAR(UPAP_AUTHREQ, outp); PUTCHAR(++u->us_id, outp); PUTSHORT(outlen, outp); PUTCHAR(u->us_userlen, outp); BCOPY(u->us_user, outp, u->us_userlen); INCPTR(u->us_userlen, outp); PUTCHAR(u->us_passwdlen, outp); BCOPY(u->us_passwd, outp, u->us_passwdlen); pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id)); TIMEOUT(upap_timeout, u, u->us_timeouttime); ++u->us_transmits; u->us_clientstate = UPAPCS_AUTHREQ; } /* * upap_sresp - Send a response (ack or nak). */ static void upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen) { u_char *outp; int outlen; outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen; outp = outpacket_buf[u->us_unit]; MAKEHEADER(outp, PPP_PAP); PUTCHAR(code, outp); PUTCHAR(id, outp); PUTSHORT(outlen, outp); PUTCHAR(msglen, outp); BCOPY(msg, outp, msglen); pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN); UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate)); } #if PPP_ADDITIONAL_CALLBACKS static char *upap_codenames[] = { "AuthReq", "AuthAck", "AuthNak" }; /* * upap_printpkt - print the contents of a PAP packet. */ static int upap_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg ) { LWIP_UNUSED_ARG(p); LWIP_UNUSED_ARG(plen); LWIP_UNUSED_ARG(printer); LWIP_UNUSED_ARG(arg); return 0; } #endif /* PPP_ADDITIONAL_CALLBACKS */ #endif /* PAP_SUPPORT */ #endif /* PPP_SUPPORT */