/* * Copyright (c) 2001-2002 Packet Design, LLC. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, * use and redistribution of this software, in source or object code * forms, with or without modifications are expressly permitted by * Packet Design; provided, however, that: * * (i) Any and all reproductions of the source or object code * must include the copyright notice above and the following * disclaimer of warranties; and * (ii) No rights are granted, in any manner or form, to use * Packet Design trademarks, including the mark "PACKET DESIGN" * on advertising, endorsements, or otherwise except as such * appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 PACKET DESIGN IS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Author: Archie Cobbs */ #include "ppp/ppp_defs.h" #include "ppp/ppp_log.h" #include "ppp/ppp_fsm_option.h" #include "ppp/ppp_fsm.h" #include "ppp/ppp_auth.h" #include "ppp/ppp_link.h" #include "ppp/ppp_auth_pap.h" #define PAP_MTYPE "ppp_authtype.pap" #define PAP_RETRY 3 #define PAP_REQUEST 1 #define PAP_ACK 2 #define PAP_NAK 3 #define PAP_MSG_ACK "Authorization successful" #define PAP_MSG_NAK "Authorization failed" #define PAP_MSG_BUFSIZE 64 /* PAP info structure */ struct ppp_auth_pap { struct ppp_link *link; struct ppp_log *log; struct ppp_auth_config aconf; struct ppp_auth_cred cred; struct ppp_auth_resp resp; int dir; u_char id; struct pevent_ctx *ev_ctx; struct pevent *timer; pthread_mutex_t *mutex; }; /* Internal functions */ static void ppp_auth_pap_send_request(struct ppp_auth_pap *pap); static void ppp_auth_pap_send_response(struct ppp_auth_pap *pap, u_char id, int ack); static ppp_link_auth_finish_t ppp_auth_pap_acquire_finish; static ppp_link_auth_finish_t ppp_auth_pap_check_finish; /* Internal variables */ static const char *pap_codes[] = { "zero", "request", "ack", "nak" }; /* Macro for logging */ #define LOG(sev, fmt, args...) PPP_LOG(pap->log, sev, fmt , ## args) /* * Start PAP */ void * ppp_auth_pap_start(struct pevent_ctx *ev_ctx, struct ppp_link *link, pthread_mutex_t *mutex, int dir, u_int16_t *protop, struct ppp_log *log) { struct ppp_auth_pap *pap; /* Create info structure */ if ((pap = MALLOC(PAP_MTYPE, sizeof(*pap))) == NULL) return (NULL); memset(pap, 0, sizeof(*pap)); pap->ev_ctx = ev_ctx; pap->mutex = mutex; pap->link = link; pap->log = log; pap->dir = dir; pap->cred.type = PPP_AUTH_PAP; /* Get link auth config */ pap->aconf = *ppp_link_auth_get_config(link); /* Return protocol */ *protop = PPP_PROTO_PAP; /* If receiving auth, wait for peer's request */ if (dir == PPP_SELF) return (pap); /* If sending auth, acquire credentials */ if (ppp_link_authorize(pap->link, pap->dir, &pap->cred, ppp_auth_pap_acquire_finish) == -1) { FREE(PAP_MTYPE, pap); return (NULL); } /* Done */ return (pap); } /* * Cancel PAP */ void ppp_auth_pap_cancel(void *arg) { struct ppp_auth_pap *pap = arg; pevent_unregister(&pap->timer); ppp_log_close(&pap->log); FREE(PAP_MTYPE, pap); } /* * Handle PAP input */ void ppp_auth_pap_input(void *arg, int dir, void *data, size_t len) { struct ppp_auth_pap *pap = arg; struct ppp_fsm_pkt *const pkt = data; if (len < sizeof(*pkt) + 2) return; memcpy(pkt, data, sizeof(*pkt)); pkt->length = ntohs(pkt->length); if (pkt->length > len) return; if (pkt->length < len) len = pkt->length; len -= sizeof(*pkt); switch (pkt->code) { case PAP_REQUEST: { struct ppp_auth_cred_pap *const cred = &pap->cred.u.pap; u_char nlen; u_char plen; /* Check direction */ if (dir != PPP_SELF) break; /* Logging */ LOG(LOG_DEBUG, "rec'd %s #%u", pap_codes[pkt->code], pkt->id); /* Extract username and password */ if (len < 1) return; nlen = pkt->data[0]; if (len < 1 + nlen + 1) return; plen = pkt->data[1 + nlen]; if (len < 1 + nlen + 1 + plen) return; strncpy(cred->name, pkt->data + 1, MIN(nlen, sizeof(cred->name) - 1)); cred->name[sizeof(cred->name) - 1] = '\0'; strncpy(cred->password, pkt->data + 1 + nlen + 1, MIN(plen, sizeof(cred->password) - 1)); cred->password[sizeof(cred->password) - 1] = '\0'; /* Ignore if already checking a previous request */ if (ppp_link_auth_in_progress(pap->link, pap->dir)) { LOG(LOG_DEBUG, "ignoring packet, action pending"); break; } /* Check credentials */ if (ppp_link_authorize(pap->link, pap->dir, &pap->cred, ppp_auth_pap_check_finish) == -1) { ppp_auth_pap_send_response(pap, pap->id, 0); ppp_link_auth_complete(pap->link, pap->dir, NULL, NULL); break; } /* Save peer's id for my response */ pap->id = pkt->id; /* Now wait for credentials check to finish */ break; } case PAP_ACK: case PAP_NAK: /* Check direction */ if (dir != PPP_PEER) break; /* Logging */ LOG(LOG_DEBUG, "rec'd %s #%u", pap_codes[pkt->code], pkt->id); /* Stop timer */ pevent_unregister(&pap->timer); /* Finish up */ ppp_link_auth_complete(pap->link, pap->dir, pkt->code == PAP_ACK ? &pap->cred : NULL, NULL); break; default: return; } } /* * Continue after a successful credentials acquisition. */ static void ppp_auth_pap_acquire_finish(void *arg, const struct ppp_auth_cred *creds, const struct ppp_auth_resp *resp) { struct ppp_auth_pap *const pap = arg; struct ppp_auth_cred_pap *const cred = &pap->cred.u.pap; /* Copy credentials */ pap->cred = *creds; /* Sanitize credentials */ cred->name[sizeof(cred->name) - 1] = '\0'; cred->password[sizeof(cred->password) - 1] = '\0'; /* Send first PAP request */ ppp_auth_pap_send_request(pap); } /* * Continue after a successful credentials check. */ static void ppp_auth_pap_check_finish(void *arg, const struct ppp_auth_cred *creds, const struct ppp_auth_resp *resp) { struct ppp_auth_pap *const pap = arg; struct ppp_auth_cred_pap *const cred = &pap->cred.u.pap; int valid = (*resp->errmsg == '\0'); /* Copy response */ pap->resp = *resp; /* Report validity */ if (valid) { LOG(LOG_INFO, "rec'd %s credentials for \"%s\"", "valid", cred->name); } else { LOG(LOG_NOTICE, "rec'd %s credentials for \"%s\": %s", "invalid", cred->name, resp->errmsg); } /* Send response */ ppp_auth_pap_send_response(pap, pap->id, valid); /* Finish up */ ppp_link_auth_complete(pap->link, pap->dir, valid ? &pap->cred : NULL, NULL); } /* * Send a PAP request */ static void ppp_auth_pap_send_request(struct ppp_auth_pap *pap) { struct ppp_auth_cred_pap *const cred = &pap->cred.u.pap; union { u_char buf[sizeof(struct ppp_fsm_pkt) + sizeof(cred->name) + sizeof(cred->password)]; struct ppp_fsm_pkt pkt; } u; struct ppp_fsm_pkt *const pkt = &u.pkt; /* Cancel previous timeout event (if any) and start another */ pevent_unregister(&pap->timer); if (pevent_register(pap->ev_ctx, &pap->timer, 0, pap->mutex, (pevent_handler_t *)ppp_auth_pap_send_request, pap, PEVENT_TIME, PAP_RETRY * 1000) == -1) LOG(LOG_ERR, "%s: %m", "pevent_register"); /* Construct packet */ pkt->id = ++pap->id; pkt->code = PAP_REQUEST; pkt->length = htons(sizeof(*pkt) + 1 + strlen(cred->name) + 1 + strlen(cred->password)); pkt->data[0] = strlen(cred->name); memcpy(pkt->data + 1, cred->name, strlen(cred->name)); pkt->data[1 + strlen(cred->name)] = strlen(cred->password); memcpy(pkt->data + 1 + strlen(cred->name) + 1, cred->password, strlen(cred->password)); /* Logging */ LOG(LOG_DEBUG, "xmit %s #%u", pap_codes[pkt->code], pkt->id); /* Send packet */ ppp_link_write(pap->link, PPP_PROTO_PAP, pkt, ntohs(pkt->length)); } /* * Send a PAP response */ static void ppp_auth_pap_send_response(struct ppp_auth_pap *pap, u_char id, int ack) { union { u_char buf[sizeof(struct ppp_fsm_pkt) + 1 + PAP_MSG_BUFSIZE]; struct ppp_fsm_pkt pkt; } u; struct ppp_fsm_pkt *const pkt = &u.pkt; /* Construct packet */ pkt->id = id; pkt->code = ack ? PAP_ACK : PAP_NAK; snprintf(pkt->data + 1, PAP_MSG_BUFSIZE, "%s", ack ? PAP_MSG_ACK : PAP_MSG_NAK); pkt->data[0] = strlen(pkt->data + 1); pkt->length = htons(sizeof(*pkt) + 1 + strlen(pkt->data + 1)); /* Logging */ LOG(LOG_DEBUG, "xmit %s #%u", pap_codes[pkt->code], pkt->id); /* Send packet */ ppp_link_write(pap->link, PPP_PROTO_PAP, pkt, ntohs(pkt->length)); }