/* * 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 "ppplcp.h" #include "ppppap.h" #include "cmdparse.h" #include "files.h" #include "trace.h" #include "main.h" static int dopap_user __ARGS((int argc, char *argv[], void *p)); static void pap_monitor __ARGS((int mustask, void *v1, void *v2)); static void pap_pwdlookup __ARGS((struct pap_s *pap_p)); 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_free __ARGS((struct fsm_s *fsm_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->edv; 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_pwdlookup( pap_p ); } } 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_monitor(unused, v1, v2) int unused; void *v1; void *v2; { struct iface *iface = v1; struct fsm_s *fsm_p = v2; struct pap_s *pap_p = fsm_p->pdv; char buf[21]; struct session *sp; int wait_code = 0; /* Allocate a session control block */ if((sp = newsession("PPP/PAP",PPPPASS,1)) == NULLSESSION){ tprintf("Too many sessions\n"); return; } while ( !main_exit && wait_code == 0 ) { /* get user name */ if (pap_p->username == NULLCHAR) { tprintf ("%s: PPP/PAP Username: ", iface->name); usflush(sp->output); if (recvline(sp->input,buf,20) > 0) { rip(buf); if (strlen(buf) > 0) { pap_p->username = strdup(buf); } } } else { tprintf ("%s: PPP/PAP Username: %s\n", iface->name, pap_p->username); usflush(sp->output); } /* get pass word */ if (pap_p->username != NULLCHAR && pap_p->password == 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) { rip(buf); if ( strlen(buf) > 0 ) { pap_p->password = strdup(buf); } } tprintf("\n"); usflush(sp->output); /* Turn echo back on */ sp->ttystate.echo = 1; } /* send pap request */ fsm_sendreq(fsm_p); wait_code = pwait ( pap_p ); /* show ack/nak reply */ if ( wait_code != EABORT && pap_p->message != NULLCHAR ) { tprintf ("%s: PPP/PAP %s\n", iface->name, pap_p->message ); } tprintf ( "\n" ); usflush(sp->output); } /* clean up */ if ( wait_code != EABORT ) { pause ( 10000L ); } freesession(sp); pap_p->pp = NULLPROC; } /* 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 */ if ( strlen(password) != 0 ) pap_p->password = strdup(password); free(buf); } /*******************************************/ /* 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) { trace_log(PPPiface,"PAP: username/password incorrect or not found: %s", username); return -1; } if ((privs & PPP_ACCESS_PRIV) == 0) { trace_log(PPPiface,"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) trace_log(PPPiface, " 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 connection" ); fsm_close( &(ppp_p->fsm[Lcp]) ); } /* 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; } ppp_p->flags &= ~flag; ppp_ready(ppp_p); } /****************************************************************************/ /* 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 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) trace_log(PPPiface," 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) trace_log(PPPiface," checking password: %s", password); #endif if (pap_verify(username,password) == 0) { free( fsm_p->ppp_p->peername ); fsm_p->ppp_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 */ /* it is replaced with the length of the message */ 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) { trace_log(PPPiface,"%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; PPPiface = fsm_p->ppp_p->iface; if ( ntohcnf(&hdr, &bp) == -1 ) fsm_log( fsm_p, "short authentication packet" ); if (PPPtrace > 1) trace_log(PPPiface, "%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) { alert ( pap_p->pp, -1 ); pap_opening(fsm_p, PPP_AP_REMOTE); } break; case CONFIG_NAK: if (pap_check(fsm_p, &hdr, bp) == 0) { stop_timer(&(fsm_p->timer)); /* Must have sent a bad username or password */ free ( pap_p->username ); pap_p->username = NULLCHAR; free ( pap_p->password ); pap_p->password = NULLCHAR; psignal ( pap_p, 1 ); } break; default: if (PPPtrace) trace_log(PPPiface, "%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; struct pap_s *pap_p = fsm_p->pdv; PPPtrace = fsm_p->ppp_p->trace; PPPiface = fsm_p->ppp_p->iface; fsm_log( fsm_p, "Timeout" ); if (fsm_p->retry > 0) { free ( pap_p->message ); pap_p->message = strdup("Request timeout"); psignal ( pap_p, 1 ); } else { free ( pap_p->message ); pap_p->message = strdup("Request retry exceeded"); psignal ( pap_p, 1 ); pwait ( NULL ); 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 */ /************************************************************************/ void pap_down(fsm_p) struct fsm_s *fsm_p; { struct pap_s *pap_p = fsm_p->pdv; if ( pap_p == NULL ) return; PPPtrace = fsm_p->ppp_p->trace; PPPiface = fsm_p->ppp_p->iface; fsm_log(fsm_p, "Down"); fsm_p->flags = FALSE; switch ( fsm_p->state ) { case fsmREQ_Sent: stop_timer(&(fsm_p->timer)); alert ( pap_p->pp, EABORT ); /* 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->message ); } /* Initialize configuration structure */ void pap_init(ppp_p) struct ppp_s *ppp_p; { struct fsm_s *fsm_p = &(ppp_p->fsm[Pap]); struct timer *t; PPPtrace = ppp_p->trace; PPPiface = ppp_p->iface; PPP_DEBUG_ROUTINES("pap_init()"); if (fsm_p->pdv != NULL) return; /* already initialized */ 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; fsm_p->state = fsmCLOSED; fsm_p->retry = fsm_p->try_req; fsm_p->retry_nak = fsm_p->try_nak; /* 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); } /* Initialize state machine for local */ int pap_local(ppp_p) struct ppp_s *ppp_p; { struct fsm_s *fsm_p = &(ppp_p->fsm[Pap]); PPPtrace = ppp_p->trace; PPP_DEBUG_ROUTINES("pap_local()"); fsm_p->state = fsmLISTEN; fsm_p->flags |= PPP_AP_LOCAL; ppp_p->flags |= PPP_AP_LOCAL; fsm_p->retry = fsm_p->try_req; 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; char *ifn; PPPtrace = ppp_p->trace; PPP_DEBUG_ROUTINES("pap_remote()"); fsm_p->state = fsmREQ_Sent; fsm_p->flags |= PPP_AP_REMOTE; ppp_p->flags |= PPP_AP_REMOTE; /* build a process/session to monitor user/password progress */ ifn = if_name( ppp_p->iface, " PAP" ); pap_p->pp = newproc( ifn, 512, pap_monitor, 0, ppp_p->iface, fsm_p, 0); free( ifn ); return 0; }