/* * PPPPAP.C -- Password Authentication Protocol for PPP * * This implementation of PPP is declared to be in the public domain. * * Jan 91 Bill_Simpson@um.cc.umich.edu * Computer Systems Consulting Services * * Acknowledgements and correction history may be found in PPP.C */ #include #include "global.h" #include "mbuf.h" #include "proc.h" #include "iface.h" #include "session.h" #include "socket.h" #include "ppp.h" #include "pppfsm.h" #include "ppppap.h" #include "cmdparse.h" #include "files.h" static int dopap_user __ARGS((int argc, char *argv[], void *p)); static void pap_input __ARGS((int mustask, void *v1, void *v2)); static void pap_pwdlookup __ARGS((struct pap_s *pap_p)); static void pap_getpassword __ARGS((struct fsm_s *fsm_p, int mustask)); static struct mbuf *pap_makereq __ARGS((struct fsm_s *fsm_p)); static int pap_verify __ARGS((char *username, char *password)); static void pap_shutdown __ARGS((struct fsm_s *fsm_p)); static void pap_opening __ARGS((struct fsm_s *fsm_p, int flag)); static int pap_request __ARGS((struct fsm_s *fsm_p, struct config_hdr *hdr, struct mbuf *data)); static int pap_check __ARGS((struct fsm_s *fsm_p, struct config_hdr *hdr, struct mbuf *data)); static void pap_timeout __ARGS((void *vp)); static void pap_reset __ARGS((struct fsm_s *fsm_p)); static void pap_free __ARGS((struct fsm_s *fsm_p)); static void pap_init __ARGS((struct ppp_s *ppp_p)); static struct fsm_constant_s pap_constants = { "Pap", PPP_PAP_PROTOCOL, 0x000E, /* codes 1-3 recognized */ Pap, PAP_REQ_TRY, PAP_FAIL_MAX, 0, PAP_TIMEOUT * 1000L, pap_free, fsm_no_action, /* pap_reset, */ fsm_no_action, /* pap_starting, */ fsm_no_action, /* pap_opening, */ fsm_no_action, /* pap_closing, */ fsm_no_action, /* pap_stopping, */ pap_makereq, fsm_no_check, /* pap_request, */ fsm_no_check, /* pap_ack, */ fsm_no_check, /* pap_nak, */ fsm_no_check, /* pap_reject */ }; /****************************************************************************/ /* "ppp pap" subcommands */ static struct cmds Papcmds[] = { "timeout", doppp_timeout, 0, 0, NULLCHAR, "try", doppp_try, 0, 0, NULLCHAR, "user", dopap_user, 0, 0, NULLCHAR, NULLCHAR, }; int doppp_pap(argc,argv,p) int argc; char *argv[]; void *p; { register struct iface *ifp = p; register struct ppp_s *ppp_p = ifp->extension; pap_init(ppp_p); return subcmd(Papcmds, argc, argv, &(ppp_p->fsm[Pap])); } /* Set user/password */ int dopap_user(argc,argv,p) int argc; char *argv[]; void *p; { register struct fsm_s *fsm_p = p; register struct pap_s *pap_p = fsm_p->pdv; if (argc < 2) { tprintf("%s\n", (pap_p->username == NULLCHAR) ? "None" : pap_p->username); return 0; } free(pap_p->username); pap_p->username = NULLCHAR; free(pap_p->password); pap_p->password = NULLCHAR; if (stricmp(argv[1],"none") != 0) { pap_p->username = strdup(argv[1]); if (argc > 2) { pap_p->password = strdup(argv[2]); } else { pap_getpassword( fsm_p, FALSE); } } return 0; } /****************************************************************************/ /* Bring up a session on the console for for the username/password. * Return a NULLCHAR in either username or password if aborted. */ static void pap_input(mustask, v1, v2) int mustask; void *v1; void *v2; { struct iface *iface = v1; struct pap_s *pap_p = v2; char buf[21]; struct session *sp; /* Allocate a session control block */ if((sp = newsession("PPP/PAP Auth",PPPPASS,0)) == NULLSESSION){ tprintf("Too many sessions\n"); return; } if (mustask) tprintf("\n%s: PPP/PAP Password Authentication Failed;" " enter ID and password again\n", iface->name); else tprintf("\n%s: PPP/PAP Password Authentication Required\n", iface->name); tprintf("%s: PPP/PAP user: ",iface->name); usflush(sp->output); /* Only ask for the user if it is unknown */ if (pap_p->username == NULLCHAR) { if (recvline(sp->input,buf,20) < 0) { pap_p->username = NULLCHAR; } else { rip(buf); pap_p->username = strdup(buf); } } else { tprintf("%s\n",pap_p->username); } if (pap_p->username != NULLCHAR) { /* turn off echo */ sp->ttystate.echo = 0; tprintf("%s: PPP/PAP Password: ",iface->name); usflush(sp->output); if (recvline(sp->input,buf,20) < 0) pap_p->password = NULLCHAR; else { rip(buf); pap_p->password = strdup(buf); } tprintf("\n"); /* Turn echo back on */ sp->ttystate.echo = 1; } freesession(sp); psignal(pap_p,0); } /* Check the FTP userfile for this user; get password if available */ static void pap_pwdlookup(pap_p) struct pap_s *pap_p; { char *buf; char *password; int permission; if ( pap_p->username == NULLCHAR ) return; if ( (buf = userlookup( pap_p->username, &password, NULLCHARP, &permission, NULL )) == NULLCHAR ) return; /* Check permissions for this user */ if ( (permission & PPP_PWD_LOOKUP) == 0 ) { /* Not in ftpuser file for password lookup */ free(buf); return; } /* Save the password from this userfile record */ pap_p->password = strdup(password); free(buf); } /* Get user ID and password * Return a NULLCHAR in either username or password if undefined */ static void pap_getpassword(fsm_p,mustask) struct fsm_s *fsm_p; int mustask; { struct pap_s *pap_p = fsm_p->pdv; if (mustask) { free(pap_p->username); pap_p->username = NULLCHAR; free(pap_p->password); pap_p->password = NULLCHAR; } else if ( pap_p->username != NULLCHAR && pap_p->password == NULLCHAR) { pap_pwdlookup(pap_p); } if ((pap_p->username == NULLCHAR) || (pap_p->password == NULLCHAR)) { char *ifn = if_name( fsm_p->ppp_p->iface, " PAP" ); newproc( ifn, 256, pap_input, mustask, fsm_p->ppp_p->iface, pap_p, 0); free( ifn ); pwait( fsm_p->pdv ); } } /*******************************************/ /* Verify user and password sent by remote host */ static int pap_verify(username,password) char *username; char *password; { int privs; char *path; int anony = 0; /* Use same login as FTP server */ path = mallocw(128); privs = userlogin(username,password,&path,128,&anony); free(path); /* Check privs for this user */ if (privs == -1) { log(-1,"PAP: username/password incorrect or not found: %s", username); return -1; } if ((privs & PPP_ACCESS_PRIV) == 0) { log(-1,"PAP: no permission for PPP access: %s", username); return -1; } return 0; } /****************************************************************************/ /* Build a request to send to remote host */ static struct mbuf * pap_makereq(fsm_p) struct fsm_s *fsm_p; { struct pap_s *pap_p = fsm_p->pdv; struct mbuf *req_bp = NULLBUF; register char *cp; int len; PPP_DEBUG_ROUTINES("pap_makereq()"); if ( pap_p->username == NULLCHAR || pap_p->password == NULLCHAR ) { fsm_log( fsm_p, "NULL username or password" ); return NULLBUF; } #ifdef PPP_DEBUG_OPTIONS if (PPPtrace & PPP_DEBUG_OPTIONS) log(-1, " making user id %s", pap_p->username); #endif /* Get buffer for authenticate request packet */ len = 2 + strlen(pap_p->username) + strlen(pap_p->password); if ((req_bp = alloc_mbuf(len)) == NULLBUF) return NULLBUF; /* Load user id and password for authenticate packet */ cp = req_bp->data; *cp++ = (char)strlen(pap_p->username); if ( strlen(pap_p->username) > 0 ) cp = stpcpy(cp, pap_p->username); *cp++ = (char)strlen(pap_p->password); if ( strlen(pap_p->password) > 0 ) cp = stpcpy(cp, pap_p->password); req_bp->cnt += len; return(req_bp); } /****************************************************************************/ /* abandon PAP attempt; shutdown LCP layer */ static void pap_shutdown(fsm_p) struct fsm_s *fsm_p; { struct ppp_s *ppp_p = fsm_p->ppp_p; PPP_DEBUG_ROUTINES("pap_shutdown()"); if (PPPtrace > 1) fsm_log( fsm_p, "Failed; close PPP connection" ); ppp_p->phase = pppTERMINATE; psignal(ppp_p, 0); } /* Configuration negotiation complete */ static void pap_opening(fsm_p, flag) struct fsm_s *fsm_p; int flag; { register struct ppp_s *ppp_p = fsm_p->ppp_p; fsm_log(fsm_p, "Open"); stop_timer(&(fsm_p->timer)); if ( !((fsm_p->flags &= ~flag) & (PPP_AP_LOCAL | PPP_AP_REMOTE)) ) { fsm_p->state = fsmOPENED; } if ( !((ppp_p->flags &= ~flag) & (PPP_AP_LOCAL | PPP_AP_REMOTE)) ) { ppp_p->phase = pppREADY; psignal(ppp_p, 0); } } /****************************************************************************/ /* Check request from remote host */ static int pap_request(fsm_p, hdr, data) struct fsm_s *fsm_p; struct config_hdr *hdr; struct mbuf *data; { struct pap_s *pap_p = fsm_p->pdv; struct mbuf *reply_bp; int result; char *message; int mess_length; char *username = NULLCHAR; int userlen; char *password = NULLCHAR; int passwordlen; PPP_DEBUG_ROUTINES("pap_request()"); /* Extract userID/password sent by remote host */ if ( (userlen = pullchar(&data)) != -1 ) { register int i; register char *cp; cp = username = mallocw(userlen+1); for ( i = userlen; i-- > 0; ) { *cp++ = PULLCHAR(&data); } *cp = '\0'; } #ifdef PPP_DEBUG_OPTIONS if (PPPtrace & PPP_DEBUG_OPTIONS) log(-1," checking user: %s", username); #endif if ( (passwordlen = pullchar(&data)) != -1 ) { register int i; register char *cp; cp = password = mallocw(passwordlen+1); for ( i = passwordlen; i-- > 0; ) { *cp++ = PULLCHAR(&data); } *cp = '\0'; } #ifdef PPP_DEBUG_OPTIONS if (PPPtrace & PPP_DEBUG_OPTIONS) log(-1," checking password: %s", password); #endif if (pap_verify(username,password) == 0) { free( pap_p->peername ); pap_p->peername = strdup(username); result = CONFIG_ACK; message = " Welcome"; } else { result = CONFIG_NAK; message = " Invalid username or password"; } /* the space at the beginning of the message is crucial */ mess_length = strlen(message); reply_bp = qdata(message,mess_length); reply_bp->data[0] = (char)(mess_length - 1); fsm_send(fsm_p, result, hdr->id, reply_bp); if (result == CONFIG_NAK) { if ( fsm_p->retry_nak > 0 ) { fsm_p->retry_nak--; } else { pap_shutdown(fsm_p); } } free_p(data); free(username); free(password); return (result != CONFIG_ACK); } /* Check acknowledgement from remote host */ static int pap_check(fsm_p, hdr, data) struct fsm_s *fsm_p; struct config_hdr *hdr; struct mbuf *data; { struct pap_s *pap_p = fsm_p->pdv; char *message; int mess_length; int full_length; int len; PPP_DEBUG_ROUTINES("pap_check()"); /* ID field must match last request we sent */ if (hdr->id != fsm_p->lastid) { PPP_DEBUG_CHECKS("PAP: wrong ID"); tprintf ("id mismatch hdrid=%d, lastid=%d\n", hdr->id, fsm_p->lastid); free_p(data); return -1; } /* Log ASCII message from remote host, if any */ if ( (mess_length = pullchar(&data)) != -1 ) { message = mallocw( mess_length+1 ); full_length = len_p(data); len = dqdata(data, message, mess_length); message[len] = '\0'; free( pap_p->message ); pap_p->message = message; if (PPPtrace) { log(-1,"%s: PPP/PAP %s %s: %s", fsm_p->ppp_p->iface->name, (len < mess_length) ? "Short" : (mess_length < full_length) ? "Long" : "Valid", (hdr->code == CONFIG_ACK) ? "Ack" : "Nak", message); } return (len < mess_length || mess_length < full_length); } free_p(data); PPP_DEBUG_CHECKS( "PAP: missing message count" ); return -1; } /************************************************************************/ /* E V E N T P R O C E S S I N G */ /************************************************************************/ /* Process incoming packet */ void pap_proc(fsm_p,bp) struct fsm_s *fsm_p; struct mbuf *bp; { struct pap_s *pap_p = fsm_p->pdv; struct config_hdr hdr; PPPtrace = fsm_p->ppp_p->trace; ntohcnf(&hdr, &bp); if (PPPtrace > 1) log(-1, "%s: PPP/%s Recv," " option: %s, id: %d, len: %d", fsm_p->ppp_p->iface->name, fsm_p->pdc->name, fsmCodes[hdr.code], hdr.id, hdr.len); hdr.len -= CONFIG_HDR_LEN; /* Length includes envelope */ trim_mbuf(&bp, hdr.len); /* Trim off padding */ switch(hdr.code) { case CONFIG_REQ: if ( pap_request(fsm_p, &hdr, bp) == 0) { pap_opening(fsm_p, PPP_AP_LOCAL); } break; case CONFIG_ACK: if (pap_check(fsm_p, &hdr, bp) == 0) { pap_opening(fsm_p, PPP_AP_REMOTE); } break; case CONFIG_NAK: if (pap_check(fsm_p, &hdr, bp) == 0) { /* Must have sent a bad user or password */ /* Get the password again */ pap_getpassword(fsm_p, TRUE); if (pap_p->username == NULLCHAR || pap_p->password == NULLCHAR) { pap_shutdown(fsm_p); } else { fsm_sendreq(fsm_p); } } break; default: if (PPPtrace) log(-1, "%s: PPP/Pap: Unknown packet type: %d;" " dropping packet", fsm_p->ppp_p->iface->name, hdr.code); free_p(bp); break; } } /* Timeout while waiting for reply from remote host */ static void pap_timeout(vp) void *vp; { struct fsm_s *fsm_p = (struct fsm_s *)vp; PPPtrace = fsm_p->ppp_p->trace; fsm_log( fsm_p, "Timeout" ); if (fsm_p->retry > 0) { fsm_sendreq(fsm_p); } else { fsm_log(fsm_p, "Request retry exceeded"); pap_shutdown(fsm_p); } } /************************************************************************/ /* I N I T I A L I Z A T I O N */ /************************************************************************/ /* Reset state machine */ static void pap_reset(fsm_p) struct fsm_s *fsm_p; { PPP_DEBUG_ROUTINES("pap_reset()"); fsm_p->state = fsmCLOSED; fsm_p->retry = fsm_p->try_req; fsm_p->retry_nak = fsm_p->try_nak; } /* Initialize state machine for local */ int pap_local(ppp_p) struct ppp_s *ppp_p; { struct fsm_s *fsm_p = &(ppp_p->fsm[Pap]); if (ppp_p->fsm[Pap].pdv == NULL) pap_init(ppp_p); PPPtrace = fsm_p->ppp_p->trace; PPP_DEBUG_ROUTINES("pap_local()"); pap_reset(fsm_p); fsm_p->state = fsmLISTEN; fsm_p->flags |= PPP_AP_LOCAL; return 0; } /* Initialize state machine for remote */ int pap_remote(ppp_p) struct ppp_s *ppp_p; { struct fsm_s *fsm_p = &(ppp_p->fsm[Pap]); struct pap_s *pap_p = fsm_p->pdv; if (ppp_p->fsm[Pap].pdv == NULL) pap_init(ppp_p); PPPtrace = fsm_p->ppp_p->trace; PPP_DEBUG_ROUTINES("pap_remote()"); /* We need to send REQ to remote host */ /* Get the user and password we will send */ if ((pap_p->username == NULLCHAR) || (pap_p->password == NULLCHAR)) { pap_getpassword(fsm_p, FALSE); if ((pap_p->username == NULLCHAR) || (pap_p->password == NULLCHAR)) { return -1; } } pap_reset(fsm_p); fsm_p->state = fsmREQ_Sent; fsm_p->flags |= PPP_AP_REMOTE; return(fsm_sendreq(fsm_p)); } void pap_down(fsm_p) struct fsm_s *fsm_p; { if ( fsm_p->pdv == NULL ) return; PPPtrace = fsm_p->ppp_p->trace; fsm_log(fsm_p, "Down"); fsm_p->flags = FALSE; switch ( fsm_p->state ) { case fsmREQ_Sent: stop_timer(&(fsm_p->timer)); /* fallthru */ case fsmOPENED: case fsmLISTEN: case fsmTERM_Sent: fsm_p->state = fsmCLOSED; break; case fsmCLOSED: /* Already closed; nothing to do */ break; } } static void pap_free(fsm_p) struct fsm_s *fsm_p; { struct pap_s *pap_p = fsm_p->pdv; free( pap_p->username ); free( pap_p->password ); free( pap_p->peername ); free( pap_p->message ); } /* Initialize configuration structure */ static void pap_init(ppp_p) struct ppp_s *ppp_p; { struct fsm_s *fsm_p; struct timer *t; PPPtrace = ppp_p->trace; PPP_DEBUG_ROUTINES("pap_init()"); if (ppp_p->fsm[Pap].pdv != NULL) return; /* already initialized */ fsm_p = &(ppp_p->fsm[Pap]); fsm_p->ppp_p = ppp_p; fsm_p->pdc = &pap_constants; fsm_p->pdv = callocw(1,sizeof(struct pap_s)); fsm_p->try_req = fsm_p->pdc->try_req; fsm_p->try_nak = fsm_p->pdc->try_nak; fsm_p->try_terminate = fsm_p->pdc->try_terminate; pap_reset(fsm_p); /* Initialize timer */ t = &(fsm_p->timer); t->func = (void (*)())pap_timeout; t->arg = (void *)fsm_p; set_timer(t, fsm_p->pdc->timeout); fsm_timer(fsm_p); stop_timer(t); }